diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 939b9d4f44..0000000000 --- a/.gitattributes +++ /dev/null @@ -1,3 +0,0 @@ -# Skip generated files from showing up in diffs. -**/*.g.dart linguist-generated=true -**/*.freezed.dart linguist-generated=true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml deleted file mode 100644 index 641468247a..0000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ /dev/null @@ -1,98 +0,0 @@ -name: Bug Report -description: Create a report to help us improve -labels: [bug] -body: - - type: markdown - attributes: - value: | - Thanks for taking the time to fill out this bug report! - - type: dropdown - id: packages - attributes: - label: Which packages are you using? - description: You may select more than one. - multiple: true - options: - - stream_chat - - stream_chat_flutter - - stream_chat_flutter_core - - stream_chat_persistance - - stream_chat_localizations - validations: - required: true - - type: dropdown - id: platforms - attributes: - label: On what platforms did you experience the issue? - description: You may select more than one. - multiple: true - options: - - iOS - - Android - - Web - - Windows - - MacOS - - Linux - validations: - required: true - - type: textarea - id: version - attributes: - label: What version are you using? - description: Please specify the package names and versions - placeholder: package - version - validations: - required: true - - type: textarea - id: what-happened - attributes: - label: What happened? - description: Also, what did you expect to happen? - placeholder: Description of the bug and what was expected. - validations: - required: true - - type: textarea - id: repro-steps - attributes: - label: Steps to reproduce - description: How do you trigger this bug? Please walk us through it step by step. - value: | - 1. Go to '...' - 2. Click on '...' - 3. Scroll down to '...' - ... - render: bash - validations: - required: true - - type: textarea - id: reproduce - attributes: - label: Supporting info to reproduce - description: Please add any relevant code, screenshots and info needed to reproduce this issue. - - type: textarea - id: logs - attributes: - label: Relevant log output - description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. - render: shell - - type: textarea - id: flutter-analyze - attributes: - label: Flutter analyze output - description: Paste the output of `flutter analyze` here. - placeholder: If there are any analysis errors, try resolving them before filing this issue. - render: shell - - type: textarea - id: flutter-doctor - attributes: - label: Flutter doctor output - description: Paste the output of `flutter doctor -v` here. - render: shell - - type: checkboxes - id: terms - attributes: - label: Code of Conduct - description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/GetStream/stream-chat-flutter/blob/develop/CODE_OF_CONDUCT.md) - options: - - label: "I agree to follow this project's Code of Conduct" - required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml deleted file mode 100644 index 997171d2b0..0000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ /dev/null @@ -1,66 +0,0 @@ -name: Feature Request -description: Suggest an idea for this project -labels: [enhancement] -body: - - type: markdown - attributes: - value: | - Thanks for taking the time to help us improve! - - type: dropdown - id: packages - attributes: - label: Please select which package this feature is related to - description: You may select more than one. - multiple: true - options: - - stream_chat - - stream_chat_flutter - - stream_chat_flutter_core - - stream_chat_persistance - - stream_chat_localizations - validations: - required: true - - type: dropdown - id: platforms - attributes: - label: Which platforms would this feature impact? - description: You may select more than one. - multiple: true - options: - - iOS - - Android - - Web - - Windows - - MacOS - - Linux - - type: textarea - id: problem - attributes: - label: Is your feature request related to a problem? - description: A clear description of what the problem is. - placeholder: "Example: I'm always frustrated when [...]" - - type: textarea - id: solution - attributes: - label: "Describe the solution that you'd like" - description: A clear description of what you want to happen. - placeholder: "Example: When clicking this I want that." - - type: textarea - id: alternatives - attributes: - label: "Describe alternatives that you have considered" - description: "A clear description of any alternative solutions or features you've considered." - placeholder: "Example: Instead of this it should do that." - - type: textarea - id: additional - attributes: - label: "Additional context" - description: "Add any other context or screenshots about the feature request here." - - type: checkboxes - id: terms - attributes: - label: Code of Conduct - description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/GetStream/stream-chat-flutter/blob/develop/CODE_OF_CONDUCT.md) - options: - - label: "I agree to follow this project's Code of Conduct" - required: true diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index fac8f083b9..0000000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,9 +0,0 @@ -# Submit a pull request - -## CLA - -- [ ] I have signed the [Stream CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) (required). -- [ ] The code changes follow best practices -- [ ] Code changes are tested (add some information if not applicable) - -## Description of the pull request diff --git a/.github/actions/pana/action.yml b/.github/actions/pana/action.yml deleted file mode 100644 index 997b2492ed..0000000000 --- a/.github/actions/pana/action.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Pana Workflow - -inputs: - min_score: - required: false - type: number - default: 120 - pana_version: - required: false - type: string - runs_on: - required: false - type: string - default: "ubuntu-latest" - working_directory: - required: false - type: string - default: "." - -runs: - using: "composite" - steps: - - name: Temporary Override Local Dependencies - uses: mikefarah/yq@master - with: - cmd: | - yq eval '.dependencies.stream_chat_flutter = {"path": "../stream_chat_flutter"}' -i packages/stream_chat_localizations/pubspec.yaml - yq eval '.dependencies.stream_chat = {"path": "../stream_chat"}' -i packages/stream_chat_flutter_core/pubspec.yaml - yq eval '.dependencies.stream_chat_flutter_core = {"path": "../stream_chat_flutter_core"}' -i packages/stream_chat_flutter/pubspec.yaml - yq eval '.dependencies.stream_chat = {"path": "../stream_chat"}' -i packages/stream_chat_persistence/pubspec.yaml - - - name: Install Flutter - uses: subosito/flutter-action@v2 - with: - cache: true - channel: stable - flutter-version: "3.x" - cache-key: flutter-:os:-:channel:-:version:-:arch:-:hash:-${{ hashFiles('**/pubspec.lock') }} - - - name: Install Pana - working-directory: ${{ inputs.working_directory }} - shell: bash - run: flutter pub global activate pana ${{inputs.pana_version}} - - - name: Verify Pana Score - working-directory: ${{ inputs.working_directory }} - shell: bash - run: | - PANA=$(pana . --no-warning); PANA_SCORE=$(echo $PANA | sed -n "s/.*Points: \([0-9]*\)\/\([0-9]*\)./\1\/\2/p") - echo "Score: $PANA_SCORE" - IFS='/'; read -a SCORE_ARR <<< "$PANA_SCORE"; SCORE=SCORE_ARR[0]; - if (( $SCORE < ${{inputs.min_score}} )); then echo "The minimum score of ${{inputs.min_score}} was not met!"; exit 1; fi \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 508508a7e0..0000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,26 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" - - package-ecosystem: "pub" - directory: "/packages/stream_chat" - schedule: - interval: "weekly" - - package-ecosystem: "pub" - directory: "/packages/stream_chat_flutter_core" - schedule: - interval: "weekly" - - package-ecosystem: "pub" - directory: "/packages/stream_chat_flutter" - schedule: - interval: "weekly" - - package-ecosystem: "pub" - directory: "/packages/stream_chat_persistence" - schedule: - interval: "weekly" - - package-ecosystem: "pub" - directory: "/packages/stream_chat_localizations" - schedule: - interval: "weekly" diff --git a/.github/workflows/housekeeping.yml b/.github/workflows/housekeeping.yml deleted file mode 100644 index 3822641575..0000000000 --- a/.github/workflows/housekeeping.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Close inactive issues -on: - schedule: - - cron: "0 */6 * * *" - -jobs: - housekeeping: - runs-on: ubuntu-latest - permissions: - issues: write - pull-requests: write - steps: - - uses: actions/stale@v9 - with: - days-before-issue-stale: 20 - days-before-issue-close: 7 - stale-issue-label: "stale" - stale-issue-message: "This issue is stale because it has been open for 20 days with no activity." - close-issue-message: "This issue was closed because it has been inactive for 7 days since being marked as stale." - days-before-pr-stale: 20 - days-before-pr-close: -1 - repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/legacy_version_analyze.yml b/.github/workflows/legacy_version_analyze.yml deleted file mode 100644 index dd1ee13258..0000000000 --- a/.github/workflows/legacy_version_analyze.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: legacy_version_analyze - -env: - # Note: The versions below should be manually updated after a new stable - # version comes out. - flutter_version: "3.27.4" - -on: - push: - branches: - - master - paths: - - 'packages/**' - - '.github/workflows/legacy_version_analyze.yml' - pull_request: - branches: - - master - paths: - - 'packages/**' - - '.github/workflows/legacy_version_analyze.yml' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - # Does a sanity check that packages at least pass analysis on the N-1 - # versions of Flutter stable if the package claims to support that version. - # This is to minimize accidentally making changes that break old versions - # (which we don't commit to supporting, but don't want to actively break) - # without updating the constraints. - analyze_legacy_versions: - timeout-minutes: 15 - if: github.event.pull_request.draft == false - runs-on: ubuntu-latest - steps: - - name: "Git Checkout" - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: "Install Flutter" - uses: subosito/flutter-action@v2 - with: - flutter-version: ${{ env.flutter_version }} - channel: stable - cache: true - cache-key: flutter-:os:-:channel:-:version:-:arch:-:hash:-${{ hashFiles('**/pubspec.lock') }} - - - name: "Install Tools" - run: flutter pub global activate melos - - - name: "Bootstrap Workspace" - run: melos bootstrap --verbose - - # Only analyze lib/; non-client code doesn't need to work on - # all supported legacy version. - - name: "Stream Chat Analyze" - run: cd packages/stream_chat/lib && dart analyze --fatal-infos . - - - name: "Stream Chat Flutter Core Analyze" - run: cd packages/stream_chat_flutter_core/lib && dart analyze --fatal-infos . - - - name: "Stream Chat Flutter Analyze" - run: cd packages/stream_chat_flutter/lib && dart analyze --fatal-infos . - - - name: "Stream Chat Persistence Analyze" - run: cd packages/stream_chat_persistence/lib && dart analyze --fatal-infos . - - - name: "Stream Chat Localizations Analyze" - run: cd packages/stream_chat_localizations/lib && dart analyze --fatal-infos . \ No newline at end of file diff --git a/.github/workflows/pana.yml b/.github/workflows/pana.yml deleted file mode 100644 index 230e88d09a..0000000000 --- a/.github/workflows/pana.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: pana - -on: - pull_request: - branches: - - master - push: - branches: - - master - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - stream_chat: - runs-on: ubuntu-latest - steps: - - name: šŸ“š Git Checkout - uses: actions/checkout@v4 - - name: šŸ“Š Verify Pana Score - uses: ./.github/actions/pana - with: - working_directory: packages/stream_chat - min_score: 120 - - stream_chat_persistence: - runs-on: ubuntu-latest - steps: - - name: šŸ“š Git Checkout - uses: actions/checkout@v4 - - name: šŸ“Š Verify Pana Score - uses: ./.github/actions/pana - with: - working_directory: packages/stream_chat_persistence - min_score: 120 - - stream_chat_flutter_core: - runs-on: ubuntu-latest - steps: - - name: šŸ“š Git Checkout - uses: actions/checkout@v4 - - name: šŸ“Š Verify Pana Score - uses: ./.github/actions/pana - with: - working_directory: packages/stream_chat_flutter_core - min_score: 120 - - stream_chat_flutter: - runs-on: ubuntu-latest - steps: - - name: šŸ“š Git Checkout - uses: actions/checkout@v4 - - name: šŸ“Š Verify Pana Score - uses: ./.github/actions/pana - with: - working_directory: packages/stream_chat_flutter - min_score: 120 - - stream_chat_localizations: - runs-on: ubuntu-latest - steps: - - name: šŸ“š Git Checkout - uses: actions/checkout@v4 - - name: šŸ“Š Verify Pana Score - uses: ./.github/actions/pana - with: - working_directory: packages/stream_chat_localizations - min_score: 120 \ No newline at end of file diff --git a/.github/workflows/pr_title.yml b/.github/workflows/pr_title.yml deleted file mode 100644 index 8b200d5d8c..0000000000 --- a/.github/workflows/pr_title.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: 'PR is Conventional and Semantic' -on: - pull_request_target: - types: - - opened - - edited - - synchronize - branches: - - master - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - conventional_pr_title: - runs-on: ubuntu-latest - steps: - - uses: amannn/action-semantic-pull-request@v5.5.3 - with: - scopes: | - llc - persistence - core - ui - doc - repo - localization - requireScope: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - semantic_changelog_update: - needs: conventional_pr_title # Trigger after the [conventional_pr_title] completes - runs-on: ubuntu-latest - steps: - - uses: GetStream/verify-semantic-changelog-update@main - with: - scopes: | - { - "llc": "packages/stream_chat", - "ui": "packages/stream_chat_flutter", - "core": "packages/stream_chat_flutter_core", - "localization": "packages/stream_chat_localizations", - "persistence": "packages/stream_chat_persistence" - } - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/scripts/remove-from-coverage.sh b/.github/workflows/scripts/remove-from-coverage.sh deleted file mode 100755 index 0ea18f975a..0000000000 --- a/.github/workflows/scripts/remove-from-coverage.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -# Fast fail the script on failures. -set -e - -flutter pub global run remove_from_coverage:remove_from_coverage -f coverage/lcov.info -r '\.g\.dart$' -r '\.freezed\.dart$' diff --git a/.github/workflows/scripts/validate-formatting.sh b/.github/workflows/scripts/validate-formatting.sh deleted file mode 100755 index 27e33215f8..0000000000 --- a/.github/workflows/scripts/validate-formatting.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -if [[ $(git ls-files --modified) ]]; then - echo "" - echo "" - echo "These files are not formatted correctly:" - for f in $(git ls-files --modified); do - echo "" - echo "" - echo "-----------------------------------------------------------------" - echo "$f" - echo "-----------------------------------------------------------------" - echo "" - git --no-pager diff --unified=0 --minimal $f - echo "" - echo "-----------------------------------------------------------------" - echo "" - echo "" - done - if [[ $GITHUB_WORKFLOW ]]; then - git checkout . > /dev/null 2>&1 - fi - echo "" - echo "āŒ Some files are incorrectly formatted, see above output." - echo "" - echo "To fix these locally, run: 'melos run format'." - exit 1 -else - echo "" - echo "āœ… All files are formatted correctly." -fi \ No newline at end of file diff --git a/.github/workflows/stream_flutter_workflow.yml b/.github/workflows/stream_flutter_workflow.yml deleted file mode 100644 index 5c97dcbe3a..0000000000 --- a/.github/workflows/stream_flutter_workflow.yml +++ /dev/null @@ -1,149 +0,0 @@ -name: stream_flutter_workflow - -env: - ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' - flutter_version: "3.x" - -on: - pull_request: - paths: - - 'packages/**' - - '.github/workflows/stream_flutter_workflow.yml' - types: - - opened - - reopened - - synchronize - - ready_for_review - push: - branches: - - master - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - analyze: - timeout-minutes: 15 - if: github.event.pull_request.draft == false - runs-on: ubuntu-latest - steps: - - name: "Git Checkout" - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: "Install Flutter" - uses: subosito/flutter-action@v2 - with: - flutter-version: ${{ env.flutter_version }} - channel: stable - cache: true - cache-key: flutter-:os:-:channel:-:version:-:arch:-:hash:-${{ hashFiles('**/pubspec.lock') }} - - name: "Install Tools" - run: | - flutter pub global activate melos - - name: "Bootstrap Workspace" - run: melos bootstrap --verbose - - name: "Dart Analyze" - run: | - melos run analyze - - name: "Pub Check" - if: github.base_ref == 'master' - run: | - melos run lint:pub - - format: - runs-on: ubuntu-latest - if: github.event.pull_request.draft == false - timeout-minutes: 15 - steps: - - name: "Git Checkout" - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: "Install Flutter" - uses: subosito/flutter-action@v2 - with: - flutter-version: ${{ env.flutter_version }} - channel: stable - cache: true - cache-key: flutter-:os:-:channel:-:version:-:arch:-:hash:-${{ hashFiles('**/pubspec.lock') }} - - name: "Install Tools" - run: | - flutter pub global activate melos - - name: "Bootstrap Workspace" - run: melos bootstrap - - name: "Melos Format" - run: melos run format - - name: "Validate Formatting" - run: | - ./.github/workflows/scripts/validate-formatting.sh - - test: - runs-on: ubuntu-latest - if: github.event.pull_request.draft == false - timeout-minutes: 30 - steps: - - name: "Git Checkout" - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: "Install Flutter" - uses: subosito/flutter-action@v2 - with: - flutter-version: ${{ env.flutter_version }} - channel: stable - cache: true - cache-key: flutter-:os:-:channel:-:version:-:arch:-:hash:-${{ hashFiles('**/pubspec.lock') }} - # This step is needed due to https://github.com/actions/runner-images/issues/11279 - - name: Install SQLite3 - run: sudo apt-get update && sudo apt-get install -y sqlite3 libsqlite3-dev - - name: "Install Tools" - run: | - flutter pub global activate melos - flutter pub global activate remove_from_coverage - - name: "Bootstrap Workspace" - run: melos bootstrap - - name: "Flutter Test" - run: melos run test:all - - name: "Collect Coverage" - run: melos run coverage:ignore-file --no-select - - name: "Upload Coverage" - uses: codecov/codecov-action@v5 - with: - token: ${{secrets.CODECOV_TOKEN}} - files: packages/*/coverage/lcov.info - - name: "Stream Chat Coverage Check" - uses: VeryGoodOpenSource/very_good_coverage@v3.0.0 - with: - path: packages/stream_chat/coverage/lcov.info - min_coverage: 70 - - name: "Stream Chat Localizations Coverage Check" - uses: VeryGoodOpenSource/very_good_coverage@v3.0.0 - with: - path: packages/stream_chat_localizations/coverage/lcov.info - min_coverage: 100 - - name: "Stream Chat Persistence Coverage Check" - uses: VeryGoodOpenSource/very_good_coverage@v3.0.0 - with: - path: packages/stream_chat_persistence/coverage/lcov.info - min_coverage: 95 - - name: "Stream Chat Flutter Core Coverage Check" - uses: VeryGoodOpenSource/very_good_coverage@v3.0.0 - with: - path: packages/stream_chat_flutter_core/coverage/lcov.info - min_coverage: 30 - - name: "Stream Chat Flutter Coverage Check" - uses: VeryGoodOpenSource/very_good_coverage@v3.0.0 - with: - path: packages/stream_chat_flutter/coverage/lcov.info - min_coverage: 44 - - draft-build: - runs-on: ubuntu-latest - if: github.event.pull_request.draft == true - timeout-minutes: 1 - - steps: - - name: Run a one-line script - run: echo Draft PR, you are good. diff --git a/.github/workflows/update_goldens.yml b/.github/workflows/update_goldens.yml deleted file mode 100644 index 3ee1ffd0d9..0000000000 --- a/.github/workflows/update_goldens.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: update_goldens - -on: workflow_dispatch - -jobs: - update_goldens: - runs-on: ubuntu-latest - steps: - - name: šŸ“š Checkout branch - uses: actions/checkout@v4 - - - name: 🐦 Install Flutter - uses: subosito/flutter-action@v2 - with: - flutter-version: "3.x" - channel: stable - cache: true - cache-key: flutter-:os:-:channel:-:version:-:arch:-:hash:-${{ hashFiles('**/pubspec.lock') }} - - - name: šŸ“¦ Install Tools - run: flutter pub global activate melos - - - name: šŸ”§ Bootstrap Workspace - run: melos bootstrap --verbose - - - name: šŸ–¼ļø Update Goldens - continue-on-error: true - run: melos run update:goldens - - - name: šŸ“¤ Commit Changes - id: commit_changes - uses: stefanzweifel/git-auto-commit-action@v5 - with: - commit_message: "chore: Update Goldens" - commit_user_name: github-actions[bot] - commit_user_email: 41898282+github-actions[bot]@users.noreply.github.com \ No newline at end of file diff --git a/.gitignore b/.gitignore deleted file mode 100644 index c24b8110b1..0000000000 --- a/.gitignore +++ /dev/null @@ -1,132 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ -.project -.classpath -.settings - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# VS Code related -.vscode/ - -# Flutter/Dart/Pub related -.dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies -.packages -.pub-cache/ -.pub/ -build/ -coverage/ -coverage_helper_test.dart -**/doc/api/ -pubspec.lock -pubspec_overrides.yaml -flutter_export_environment.sh -generated_plugin_registrant.* -GeneratedPluginRegistrant.* - -# Android related -**/android/**/gradle-wrapper.jar -**/android/.gradle -**/android/captures/ -**/android/gradlew -**/android/gradlew.bat -**/android/local.properties -**/android/**/GeneratedPluginRegistrant.java - -# iOS/XCode related -**/ios/**/*.mode1v3 -**/ios/**/*.mode2v3 -**/ios/**/*.moved-aside -**/ios/**/*.pbxuser -**/ios/**/*.perspectivev3 -**/ios/**/*sync/ -**/ios/**/.sconsign.dblite -**/ios/**/.tags* -**/ios/**/.vagrant/ -**/ios/**/DerivedData/ -**/ios/**/Icon? -**/ios/**/Pods/ -**/ios/**/.symlinks/ -**/ios/**/profile -**/ios/**/xcuserdata -**/ios/.generated/ -**/ios/Flutter/App.framework -**/ios/Flutter/Flutter.framework -**/ios/Flutter/Flutter.podspec -**/ios/Flutter/Generated.xcconfig -**/ios/Flutter/app.flx -**/ios/Flutter/app.zip -**/ios/Flutter/flutter_assets/ -**/ios/Flutter/flutter_export_environment.sh -**/ios/ServiceDefinitions.json -**/ios/Runner/GeneratedPluginRegistrant.* -Podfile.lock - -# Exceptions to iOS rules -!**/ios/**/default.mode1v3 -!**/ios/**/default.mode2v3 -!**/ios/**/default.pbxuser -!**/ios/**/default.perspectivev3 - -# macOS-related -**/macos/**/Pods/ -**/macos/**/xcuserdata -**/macos/**/Flutter/ephemeral - -# Windows-related -**/windows/flutter/ephemeral/ - -# Linux-related -**/linux/flutter/ephemeral/ - -# Generated files -*.dart.js -*.info.json -*.js -*.js_ -*.js.deps -*.js.map -*.wasm -**/lcov.info - -# Other ignored files -google-services.json -.melos_tool/ -.packages/flutter_widgets/example/ios/Flutter/.last_build_id -.packages/dart_client/example/ios/Flutter/.last_build_id -.packages/dart_client/example/ios/Runner.xcodeproj/project.pbxproj - -# Ignore FVM -**/.fvm - -# Gradle related -.gradle/ -gradlew -gradlew.bat -gradle-wrapper.jar - -# Non-CI golden files and failures -**/test/**/goldens/**/*.* -**/test/**/failures/**/*.* -!**/test/**/goldens/ci/*.* - -# Exceptions to the above rules -!/pubspec.lock -!**/example/web/sql-wasm.js -!**/example/web/sql-wasm.wasm -!/sample_app/web/sql-wasm.js -!/sample_app/web/sql-wasm.wasm \ No newline at end of file diff --git a/.last_build_id b/.last_build_id new file mode 100644 index 0000000000..03c8d12413 --- /dev/null +++ b/.last_build_id @@ -0,0 +1 @@ +2b2b28d91c806f1c3455b7b05f0d5400 \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 131926c711..0000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,47 +0,0 @@ -# Code of conduct - -We expect Stream's contributors to act professionally and respectfully, and -we expect our social spaces to be safe and dignified environments. - -Specifically: - -* Respect people, their identities, their culture, and their work. -* Be kind. Be courteous. Be welcoming. -* Listen. Consider and acknowledge people's points before responding. - -Should you experience anything that makes you feel unwelcome in Stream's -community, please contact [support](https://getstream.io/contact/support/) -or, if you prefer, directly contact someone on the team, for instance -[Salvatore](mailto:salvatore@getstream.io) or [Nash](mailto:nash@getstream.io). We will -not tolerate harassment from anyone in Stream's community, even outside -of Stream's public communication channels. - -## Conflict resolution - -When multiple contributors disagree on the direction for a particular -patch or the general direction of the project, the conflict should be -resolved by communication. The people who disagree should get -together, try to understand each other's points of view, and work to -find a design that addresses everyone's concerns. - -This is usually sufficient to resolve issues. If you cannot come to an -agreement, ask for the advice of a more senior member of the team. - -Be wary of agreement by attrition, where one person argues a point -repeatedly until other participants give up in the interests of moving -on. This is not conflict resolution, as it does not address everyone's -concerns. Be wary of agreement by compromise, where two good competing -solutions are merged into one mediocre solution. A conflict is -addressed when the participants agree that the final solution is -_better_ than all the conflicting proposals. Sometimes the solution is -more work than either of the proposals. - -## Questions - -It's always ok to ask questions. Once you find the answer, document it in -the first place you looked. That way, the next person will be brought -up to speed even quicker. - -!["I try not to make fun of people for admitting they don't know things, because for each thing 'everyone knows' by the time they're adults, every day there are, on average, 10,000 people in the US hearing about it for the first time. If I make fun of people, I train them not to tell me when they have those moments. And I miss out on the fun." "Diet coke and mentos thing? What's that?" "Oh, man! We're going to the grocery store." "Why?" "You're one of today's lucky 10,000."](https://imgs.xkcd.com/comics/ten_thousand.png) - -Source: _[xkcd, May 2012](https://xkcd.com/1053/)_ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 279e3bc4e2..0000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,344 +0,0 @@ -Welcome to Stream’s Flutter repository. Thank you for taking the time to contribute to our codebase. šŸŽ‰. - -This document outlines a set of guidelines for contributing to Stream and our packages. These are mostly guidelines, not necessarily a fixed set of rules. Please use your best judgment and feel free to propose changes to this document in a pull request. - ---- - -# If I have a question, do I need to read this guide? šŸ’¬ - -Probably not. Most questions can be answered by looking at our Frequently Asked Questions (FAQ) or Flutter Cookbook. - -If you are still having doubts around a specific API, please create an issue on your repository with the label "Question". Community members or team members would be happy to assist. - -In cases where developers suspect the issue may be a defect or bug, please use one of our pre-made templates to file an issue. Be sure to include as many details as possible to help our team reproduce the error. A good bug report should have clear and consistent instructions for reproducing, screenshots or videos of the bug if applicable, and information on your environment setup and Flutter version. - -You can include the output of `flutter doctor --verbose` when filing an issue. - -šŸ”—: [https://github.com/GetStream/stream-chat-flutter/issue](https://github.com/GetStream/stream-chat-flutter/issues) - ---- - -# What should I know before diving into code? šŸ¤” - -Stream's Flutter code is kept in a single mono-repository consisting of multiple packages. Source code for each package can be found under the top-level `/packages` directory. - -Screen_Shot_2021-03-31_at_4 13 52_PM - -### Project Structure 🧱 - -`.github` - GitHub files including issue templates, pull request templates, and GitHub Action scripts. - -`images` - Static images used in our README and elsewhere. - -`package` - Directory containing Stream's Flutter source code. Each sub-directory represents a Flutter (or Dart) project. - -`.gitignore` - Listing of files and file extensions ignored for this project. - -`CODE_OF_CONDUCT` - Our values, approach to writing code, and expectations for Stream developers and contributors. - -`LICENSE` - Legal. Feast your eyes on the fine print. - -`README` - Project overview. - -`melos.yaml` - Configuration file used to control [Melos](https://pub.dev/packages/melos), our mono-repo management tool of choice. - -### Current Stream Packages - -`stream_chat` - Stream Chat is a low-level wrapper around Stream's REST API and web sockets. It contains minimal external dependencies and does not rely on Flutter. It is possible to use this package on most platforms supported by Dart. - -`stream_chat_flutter_core` - This package provides business logic to fetch common things required to integrate Stream Chat into your application. The core package allows more customization, providing business logic but no UI components. - -`stream_chat_flutter` - This library includes both a low-level chat SDK and a set of reusable and customizable UI components. - -`stream_chat_persistence` - This package provides a persistence client for fetching and saving chat data locally. Stream Chat Persistence uses Moor as a disk cache. - -`stream_chat_localizations` - This package provides a set of localizations for the SDK. - -### Local Setup - -Congratulations. šŸŽ‰. You've successfully cloned our repository, and you are ready to make your first contribution. Before you can start making code changes, there are a few things to configure. - -**Melos Setup** - -Stream uses `Melos` to manage our mono-repository. For those unfamiliar, Melos is used to split up large code bases into separate independently versioned packages. To install Melos, developers can run the following command: - -```bash -pub global activate melos -``` - -Once activated, users can now "bootstrap" their local clone by running the following: - -```bash -melos bootstrap -``` - -Bootstrap will automatically fetch and link dependencies for all packages in the repository. It is the Melos equivalent of running `flutter pub get`. - -Bonus Tip: Did you know it is possible to define and run custom scripts using Melos? Our team uses custom scripts for all sorts of actions like testing, lints, and more. - -To run a script, use `melos run - - - - - - - - - - - example - - - - - - - - diff --git a/packages/stream_chat_flutter/example/web/manifest.json b/packages/stream_chat_flutter/example/web/manifest.json deleted file mode 100644 index 8c012917da..0000000000 --- a/packages/stream_chat_flutter/example/web/manifest.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "example", - "short_name": "example", - "start_url": ".", - "display": "standalone", - "background_color": "#0175C2", - "theme_color": "#0175C2", - "description": "A new Flutter project.", - "orientation": "portrait-primary", - "prefer_related_applications": false, - "icons": [ - { - "src": "icons/Icon-192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "icons/Icon-512.png", - "sizes": "512x512", - "type": "image/png" - } - ] -} diff --git a/packages/stream_chat_flutter/example/web/sql-wasm.js b/packages/stream_chat_flutter/example/web/sql-wasm.js deleted file mode 100644 index f04a231d6c..0000000000 --- a/packages/stream_chat_flutter/example/web/sql-wasm.js +++ /dev/null @@ -1,203 +0,0 @@ - -// We are modularizing this manually because the current modularize setting in Emscripten has some issues: -// https://github.com/kripken/emscripten/issues/5820 -// In addition, When you use emcc's modularization, it still expects to export a global object called `Module`, -// which is able to be used/called before the WASM is loaded. -// The modularization below exports a promise that loads and resolves to the actual sql.js module. -// That way, this module can't be used before the WASM is finished loading. - -// We are going to define a function that a user will call to start loading initializing our Sql.js library -// However, that function might be called multiple times, and on subsequent calls, we don't actually want it to instantiate a new instance of the Module -// Instead, we want to return the previously loaded module - -// TODO: Make this not declare a global if used in the browser -var initSqlJsPromise = undefined; - -var initSqlJs = function (moduleConfig) { - - if (initSqlJsPromise){ - return initSqlJsPromise; - } - // If we're here, we've never called this function before - initSqlJsPromise = new Promise(function (resolveModule, reject) { - - // We are modularizing this manually because the current modularize setting in Emscripten has some issues: - // https://github.com/kripken/emscripten/issues/5820 - - // The way to affect the loading of emcc compiled modules is to create a variable called `Module` and add - // properties to it, like `preRun`, `postRun`, etc - // We are using that to get notified when the WASM has finished loading. - // Only then will we return our promise - - // If they passed in a moduleConfig object, use that - // Otherwise, initialize Module to the empty object - var Module = typeof moduleConfig !== 'undefined' ? moduleConfig : {}; - - // EMCC only allows for a single onAbort function (not an array of functions) - // So if the user defined their own onAbort function, we remember it and call it - var originalOnAbortFunction = Module['onAbort']; - Module['onAbort'] = function (errorThatCausedAbort) { - reject(new Error(errorThatCausedAbort)); - if (originalOnAbortFunction){ - originalOnAbortFunction(errorThatCausedAbort); - } - }; - - Module['postRun'] = Module['postRun'] || []; - Module['postRun'].push(function () { - // When Emscripted calls postRun, this promise resolves with the built Module - resolveModule(Module); - }); - - // There is a section of code in the emcc-generated code below that looks like this: - // (Note that this is lowercase `module`) - // if (typeof module !== 'undefined') { - // module['exports'] = Module; - // } - // When that runs, it's going to overwrite our own modularization export efforts in shell-post.js! - // The only way to tell emcc not to emit it is to pass the MODULARIZE=1 or MODULARIZE_INSTANCE=1 flags, - // but that carries with it additional unnecessary baggage/bugs we don't want either. - // So, we have three options: - // 1) We undefine `module` - // 2) We remember what `module['exports']` was at the beginning of this function and we restore it later - // 3) We write a script to remove those lines of code as part of the Make process. - // - // Since those are the only lines of code that care about module, we will undefine it. It's the most straightforward - // of the options, and has the side effect of reducing emcc's efforts to modify the module if its output were to change in the future. - // That's a nice side effect since we're handling the modularization efforts ourselves - module = undefined; - - // The emcc-generated code and shell-post.js code goes below, - // meaning that all of it runs inside of this promise. If anything throws an exception, our promise will abort - -var e;e||(e=typeof Module !== 'undefined' ? Module : {});null; -e.onRuntimeInitialized=function(){function a(h,l){this.Ra=h;this.db=l;this.Qa=1;this.lb=[]}function b(h,l){this.db=l;l=aa(h)+1;this.eb=ba(l);if(null===this.eb)throw Error("Unable to allocate memory for the SQL string");k(h,m,this.eb,l);this.jb=this.eb;this.$a=this.pb=null}function c(h){this.filename="dbfile_"+(4294967295*Math.random()>>>0);if(null!=h){var l=this.filename,p=l?r("//"+l):"/";l=ca(!0,!0);p=da(p,(void 0!==l?l:438)&4095|32768,0);if(h){if("string"===typeof h){for(var q=Array(h.length),B= -0,ha=h.length;Bd;++d)g.parameters.push(f["viii"[d]]); -d=new WebAssembly.Function(g,a)}else{f=[1,0,1,96];g={i:127,j:126,f:125,d:124};f.push(3);for(d=0;3>d;++d)f.push(g["iii"[d]]);f.push(0);f[1]=f.length-2;d=new Uint8Array([0,97,115,109,1,0,0,0].concat(f,[2,7,1,1,101,1,102,0,0,7,5,1,1,102,0,0]));d=new WebAssembly.Module(d);d=(new WebAssembly.Instance(d,{e:{f:a}})).exports.f}b.set(c,d)}Ia.set(a,c);a=c}return a}function ra(a){ua(a)}var Ka;e.wasmBinary&&(Ka=e.wasmBinary);var noExitRuntime;e.noExitRuntime&&(noExitRuntime=e.noExitRuntime); -"object"!==typeof WebAssembly&&K("no native wasm support detected"); -function pa(a){var b="i32";"*"===b.charAt(b.length-1)&&(b="i32");switch(b){case "i1":z[a>>0]=0;break;case "i8":z[a>>0]=0;break;case "i16":La[a>>1]=0;break;case "i32":L[a>>2]=0;break;case "i64":M=[0,(N=0,1<=+Math.abs(N)?0>>0:~~+Math.ceil((N-+(~~N>>>0))/4294967296)>>>0:0)];L[a>>2]=M[0];L[a+4>>2]=M[1];break;case "float":Ma[a>>2]=0;break;case "double":Na[a>>3]=0;break;default:K("invalid type for setValue: "+b)}} -function x(a,b){b=b||"i8";"*"===b.charAt(b.length-1)&&(b="i32");switch(b){case "i1":return z[a>>0];case "i8":return z[a>>0];case "i16":return La[a>>1];case "i32":return L[a>>2];case "i64":return L[a>>2];case "float":return Ma[a>>2];case "double":return Na[a>>3];default:K("invalid type for getValue: "+b)}return null}var Oa,Ja,Pa=!1;function assert(a,b){a||K("Assertion failed: "+b)}function Qa(a){var b=e["_"+a];assert(b,"Cannot call unknown function "+a+", make sure it is exported");return b} -function Ra(a,b,c,d){var f={string:function(v){var C=0;if(null!==v&&void 0!==v&&0!==v){var H=(v.length<<2)+1;C=y(H);k(v,m,C,H)}return C},array:function(v){var C=y(v.length);z.set(v,C);return C}},g=Qa(a),n=[];a=0;if(d)for(var t=0;t=d);)++c;if(16f?d+=String.fromCharCode(f):(f-=65536,d+=String.fromCharCode(55296|f>>10,56320|f&1023))}}else d+=String.fromCharCode(f)}return d}function A(a,b){return a?Va(m,a,b):""} -function k(a,b,c,d){if(!(0=n){var t=a.charCodeAt(++g);n=65536+((n&1023)<<10)|t&1023}if(127>=n){if(c>=d)break;b[c++]=n}else{if(2047>=n){if(c+1>=d)break;b[c++]=192|n>>6}else{if(65535>=n){if(c+2>=d)break;b[c++]=224|n>>12}else{if(c+3>=d)break;b[c++]=240|n>>18;b[c++]=128|n>>12&63}b[c++]=128|n>>6&63}b[c++]=128|n&63}}b[c]=0;return c-f} -function aa(a){for(var b=0,c=0;c=d&&(d=65536+((d&1023)<<10)|a.charCodeAt(++c)&1023);127>=d?++b:b=2047>=d?b+2:65535>=d?b+3:b+4}return b}function Wa(a){var b=aa(a)+1,c=ba(b);c&&k(a,z,c,b);return c}var Xa,z,m,La,L,Ma,Na; -function Ya(a){Xa=a;e.HEAP8=z=new Int8Array(a);e.HEAP16=La=new Int16Array(a);e.HEAP32=L=new Int32Array(a);e.HEAPU8=m=new Uint8Array(a);e.HEAPU16=new Uint16Array(a);e.HEAPU32=new Uint32Array(a);e.HEAPF32=Ma=new Float32Array(a);e.HEAPF64=Na=new Float64Array(a)}var Za=e.INITIAL_MEMORY||16777216;e.wasmMemory?Oa=e.wasmMemory:Oa=new WebAssembly.Memory({initial:Za/65536,maximum:32768});Oa&&(Xa=Oa.buffer);Za=Xa.byteLength;Ya(Xa);var $a=[],ab=[],bb=[],cb=[]; -function db(){var a=e.preRun.shift();$a.unshift(a)}var eb=0,fb=null,gb=null;e.preloadedImages={};e.preloadedAudios={};function K(a){if(e.onAbort)e.onAbort(a);J(a);Pa=!0;throw new WebAssembly.RuntimeError("abort("+a+"). Build with -s ASSERTIONS=1 for more info.");}function hb(a){var b=ib;return String.prototype.startsWith?b.startsWith(a):0===b.indexOf(a)}function jb(){return hb("data:application/octet-stream;base64,")}var ib="sql-wasm.wasm"; -if(!jb()){var kb=ib;ib=e.locateFile?e.locateFile(kb,I):I+kb}function lb(){try{if(Ka)return new Uint8Array(Ka);if(Ca)return Ca(ib);throw"both async and sync fetching of the wasm failed";}catch(a){K(a)}}function mb(){return Ka||!ya&&!G||"function"!==typeof fetch||hb("file://")?Promise.resolve().then(lb):fetch(ib,{credentials:"same-origin"}).then(function(a){if(!a.ok)throw"failed to load wasm binary file at '"+ib+"'";return a.arrayBuffer()}).catch(function(){return lb()})}var N,M; -function nb(a){for(;0>2]=60*(new Date).getTimezoneOffset();var b=(new Date).getFullYear(),c=new Date(b,0,1);b=new Date(b,6,1);L[vb()>>2]=Number(c.getTimezoneOffset()!=b.getTimezoneOffset());var d=a(c),f=a(b);d=Wa(d);f=Wa(f);b.getTimezoneOffset()>2]=d,L[xb()+4>>2]=f):(L[xb()>>2]=f,L[xb()+4>>2]=d)}}var tb; -function yb(a,b){for(var c=0,d=a.length-1;0<=d;d--){var f=a[d];"."===f?a.splice(d,1):".."===f?(a.splice(d,1),c++):c&&(a.splice(d,1),c--)}if(b)for(;c;c--)a.unshift("..");return a}function r(a){var b="/"===a.charAt(0),c="/"===a.substr(-1);(a=yb(a.split("/").filter(function(d){return!!d}),!b).join("/"))||b||(a=".");a&&c&&(a+="/");return(b?"/":"")+a} -function zb(a){var b=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.exec(a).slice(1);a=b[0];b=b[1];if(!a&&!b)return".";b&&(b=b.substr(0,b.length-1));return a+b}function Ab(a){if("/"===a)return"/";a=r(a);a=a.replace(/\/$/,"");var b=a.lastIndexOf("/");return-1===b?a:a.substr(b+1)}function Bb(a){L[Cb()>>2]=a} -function Db(){if("object"===typeof crypto&&"function"===typeof crypto.getRandomValues){var a=new Uint8Array(1);return function(){crypto.getRandomValues(a);return a[0]}}if(za)try{var b=require("crypto");return function(){return b.randomBytes(1)[0]}}catch(c){}return function(){K("randomDevice")}} -function Eb(){for(var a="",b=!1,c=arguments.length-1;-1<=c&&!b;c--){b=0<=c?arguments[c]:"/";if("string"!==typeof b)throw new TypeError("Arguments to path.resolve must be strings");if(!b)return"";a=b+"/"+a;b="/"===b.charAt(0)}a=yb(a.split("/").filter(function(d){return!!d}),!b).join("/");return(b?"/":"")+a||"."}var Fb=[];function Gb(a,b){Fb[a]={input:[],output:[],cb:b};Hb(a,Ib)} -var Ib={open:function(a){var b=Fb[a.node.rdev];if(!b)throw new O(43);a.tty=b;a.seekable=!1},close:function(a){a.tty.cb.flush(a.tty)},flush:function(a){a.tty.cb.flush(a.tty)},read:function(a,b,c,d){if(!a.tty||!a.tty.cb.xb)throw new O(60);for(var f=0,g=0;g=b||(b=Math.max(b,c*(1048576>c?2:1.125)>>>0),0!=c&&(b=Math.max(b,256)),c=a.Ma,a.Ma=new Uint8Array(b),0b)a.Ma.length=b;else for(;a.Ma.length=a.node.Sa)return 0;a=Math.min(a.node.Sa-f,d);if(8b)throw new O(28);return b},sb:function(a,b,c){P.vb(a.node,b+c);a.node.Sa=Math.max(a.node.Sa,b+c)},hb:function(a,b,c,d,f,g){assert(0===b);if(32768!==(a.node.mode&61440))throw new O(43);a=a.node.Ma; -if(g&2||a.buffer!==Xa){if(0>>0)%T.length}function Wb(a){var b=Vb(a.parent.id,a.name);if(T[b]===a)T[b]=a.bb;else for(b=T[b];b;){if(b.bb===a){b.bb=a.bb;break}b=b.bb}} -function Ob(a,b){var c;if(c=(c=Xb(a,"x"))?c:a.Na.lookup?0:2)throw new O(c,a);for(c=T[Vb(a.id,b)];c;c=c.bb){var d=c.name;if(c.parent.id===a.id&&d===b)return c}return a.Na.lookup(a,b)}function Mb(a,b,c,d){a=new Yb(a,b,c,d);b=Vb(a.parent.id,a.name);a.bb=T[b];return T[b]=a}function Q(a){return 16384===(a&61440)}var Zb={r:0,rs:1052672,"r+":2,w:577,wx:705,xw:705,"w+":578,"wx+":706,"xw+":706,a:1089,ax:1217,xa:1217,"a+":1090,"ax+":1218,"xa+":1218}; -function $b(a){var b=["r","w","rw"][a&3];a&512&&(b+="w");return b}function Xb(a,b){if(Sb)return 0;if(-1===b.indexOf("r")||a.mode&292){if(-1!==b.indexOf("w")&&!(a.mode&146)||-1!==b.indexOf("x")&&!(a.mode&73))return 2}else return 2;return 0}function ac(a,b){try{return Ob(a,b),20}catch(c){}return Xb(a,"wx")}function bc(a,b,c){try{var d=Ob(a,b)}catch(f){return f.Pa}if(a=Xb(a,"wx"))return a;if(c){if(!Q(d.mode))return 54;if(d===d.parent||"/"===Ub(d))return 10}else if(Q(d.mode))return 31;return 0} -function cc(a){var b=4096;for(a=a||0;a<=b;a++)if(!S[a])return a;throw new O(33);}function dc(a,b){ec||(ec=function(){},ec.prototype={});var c=new ec,d;for(d in a)c[d]=a[d];a=c;b=cc(b);a.fd=b;return S[b]=a}var Lb={open:function(a){a.Oa=Qb[a.node.rdev].Oa;a.Oa.open&&a.Oa.open(a)},Za:function(){throw new O(70);}};function Hb(a,b){Qb[a]={Oa:b}} -function fc(a,b){var c="/"===b,d=!b;if(c&&Pb)throw new O(10);if(!c&&!d){var f=V(b,{wb:!1});b=f.path;f=f.node;if(f.ab)throw new O(10);if(!Q(f.mode))throw new O(54);}b={type:a,Ub:{},yb:b,Mb:[]};a=a.Wa(b);a.Wa=b;b.root=a;c?Pb=a:f&&(f.ab=b,f.Wa&&f.Wa.Mb.push(b))}function da(a,b,c){var d=V(a,{parent:!0}).node;a=Ab(a);if(!a||"."===a||".."===a)throw new O(28);var f=ac(d,a);if(f)throw new O(f);if(!d.Na.gb)throw new O(63);return d.Na.gb(d,a,b,c)}function W(a,b){da(a,(void 0!==b?b:511)&1023|16384,0)} -function hc(a,b,c){"undefined"===typeof c&&(c=b,b=438);da(a,b|8192,c)}function ic(a,b){if(!Eb(a))throw new O(44);var c=V(b,{parent:!0}).node;if(!c)throw new O(44);b=Ab(b);var d=ac(c,b);if(d)throw new O(d);if(!c.Na.symlink)throw new O(63);c.Na.symlink(c,b,a)} -function ta(a){var b=V(a,{parent:!0}).node,c=Ab(a),d=Ob(b,c),f=bc(b,c,!1);if(f)throw new O(f);if(!b.Na.unlink)throw new O(63);if(d.ab)throw new O(10);try{U.willDeletePath&&U.willDeletePath(a)}catch(g){J("FS.trackingDelegate['willDeletePath']('"+a+"') threw an exception: "+g.message)}b.Na.unlink(b,c);Wb(d);try{if(U.onDeletePath)U.onDeletePath(a)}catch(g){J("FS.trackingDelegate['onDeletePath']('"+a+"') threw an exception: "+g.message)}} -function Tb(a){a=V(a).node;if(!a)throw new O(44);if(!a.Na.readlink)throw new O(28);return Eb(Ub(a.parent),a.Na.readlink(a))}function jc(a,b){a=V(a,{Ya:!b}).node;if(!a)throw new O(44);if(!a.Na.Ua)throw new O(63);return a.Na.Ua(a)}function kc(a){return jc(a,!0)}function ea(a,b){var c;"string"===typeof a?c=V(a,{Ya:!0}).node:c=a;if(!c.Na.Ta)throw new O(63);c.Na.Ta(c,{mode:b&4095|c.mode&-4096,timestamp:Date.now()})} -function lc(a){var b;"string"===typeof a?b=V(a,{Ya:!0}).node:b=a;if(!b.Na.Ta)throw new O(63);b.Na.Ta(b,{timestamp:Date.now()})}function mc(a,b){if(0>b)throw new O(28);var c;"string"===typeof a?c=V(a,{Ya:!0}).node:c=a;if(!c.Na.Ta)throw new O(63);if(Q(c.mode))throw new O(31);if(32768!==(c.mode&61440))throw new O(28);if(a=Xb(c,"w"))throw new O(a);c.Na.Ta(c,{size:b,timestamp:Date.now()})} -function u(a,b,c,d){if(""===a)throw new O(44);if("string"===typeof b){var f=Zb[b];if("undefined"===typeof f)throw Error("Unknown file open mode: "+b);b=f}c=b&64?("undefined"===typeof c?438:c)&4095|32768:0;if("object"===typeof a)var g=a;else{a=r(a);try{g=V(a,{Ya:!(b&131072)}).node}catch(n){}}f=!1;if(b&64)if(g){if(b&128)throw new O(20);}else g=da(a,c,0),f=!0;if(!g)throw new O(44);8192===(g.mode&61440)&&(b&=-513);if(b&65536&&!Q(g.mode))throw new O(54);if(!f&&(c=g?40960===(g.mode&61440)?32:Q(g.mode)&& -("r"!==$b(b)||b&512)?31:Xb(g,$b(b)):44))throw new O(c);b&512&&mc(g,0);b&=-131713;d=dc({node:g,path:Ub(g),flags:b,seekable:!0,position:0,Oa:g.Oa,Rb:[],error:!1},d);d.Oa.open&&d.Oa.open(d);!e.logReadFiles||b&1||(Pc||(Pc={}),a in Pc||(Pc[a]=1,J("FS.trackingDelegate error on read file: "+a)));try{U.onOpenFile&&(g=0,1!==(b&2097155)&&(g|=1),0!==(b&2097155)&&(g|=2),U.onOpenFile(a,g))}catch(n){J("FS.trackingDelegate['onOpenFile']('"+a+"', flags) threw an exception: "+n.message)}return d} -function ka(a){if(null===a.fd)throw new O(8);a.ob&&(a.ob=null);try{a.Oa.close&&a.Oa.close(a)}catch(b){throw b;}finally{S[a.fd]=null}a.fd=null}function Qc(a,b,c){if(null===a.fd)throw new O(8);if(!a.seekable||!a.Oa.Za)throw new O(70);if(0!=c&&1!=c&&2!=c)throw new O(28);a.position=a.Oa.Za(a,b,c);a.Rb=[]} -function Sc(a,b,c,d,f){if(0>d||0>f)throw new O(28);if(null===a.fd)throw new O(8);if(1===(a.flags&2097155))throw new O(8);if(Q(a.node.mode))throw new O(31);if(!a.Oa.read)throw new O(28);var g="undefined"!==typeof f;if(!g)f=a.position;else if(!a.seekable)throw new O(70);b=a.Oa.read(a,b,c,d,f);g||(a.position+=b);return b} -function fa(a,b,c,d,f,g){if(0>d||0>f)throw new O(28);if(null===a.fd)throw new O(8);if(0===(a.flags&2097155))throw new O(8);if(Q(a.node.mode))throw new O(31);if(!a.Oa.write)throw new O(28);a.seekable&&a.flags&1024&&Qc(a,0,2);var n="undefined"!==typeof f;if(!n)f=a.position;else if(!a.seekable)throw new O(70);b=a.Oa.write(a,b,c,d,f,g);n||(a.position+=b);try{if(a.path&&U.onWriteToFile)U.onWriteToFile(a.path)}catch(t){J("FS.trackingDelegate['onWriteToFile']('"+a.path+"') threw an exception: "+t.message)}return b} -function sa(a){var b={encoding:"binary"};b=b||{};b.flags=b.flags||"r";b.encoding=b.encoding||"binary";if("utf8"!==b.encoding&&"binary"!==b.encoding)throw Error('Invalid encoding type "'+b.encoding+'"');var c,d=u(a,b.flags);a=jc(a).size;var f=new Uint8Array(a);Sc(d,f,0,a,0);"utf8"===b.encoding?c=Va(f,0):"binary"===b.encoding&&(c=f);ka(d);return c} -function Tc(){O||(O=function(a,b){this.node=b;this.Qb=function(c){this.Pa=c};this.Qb(a);this.message="FS error"},O.prototype=Error(),O.prototype.constructor=O,[44].forEach(function(a){Nb[a]=new O(a);Nb[a].stack=""}))}var Uc;function ca(a,b){var c=0;a&&(c|=365);b&&(c|=146);return c} -function Vc(a,b,c){a=r("/dev/"+a);var d=ca(!!b,!!c);Wc||(Wc=64);var f=Wc++<<8|0;Hb(f,{open:function(g){g.seekable=!1},close:function(){c&&c.buffer&&c.buffer.length&&c(10)},read:function(g,n,t,w){for(var v=0,C=0;C>2]=d.dev;L[c+4>>2]=0;L[c+8>>2]=d.ino;L[c+12>>2]=d.mode;L[c+16>>2]=d.nlink;L[c+20>>2]=d.uid;L[c+24>>2]=d.gid;L[c+28>>2]=d.rdev;L[c+32>>2]=0;M=[d.size>>>0,(N=d.size,1<=+Math.abs(N)?0>>0:~~+Math.ceil((N-+(~~N>>>0))/4294967296)>>>0:0)];L[c+40>>2]=M[0];L[c+44>>2]=M[1];L[c+48>>2]=4096;L[c+52>>2]=d.blocks;L[c+56>>2]=d.atime.getTime()/1E3|0;L[c+60>>2]= -0;L[c+64>>2]=d.mtime.getTime()/1E3|0;L[c+68>>2]=0;L[c+72>>2]=d.ctime.getTime()/1E3|0;L[c+76>>2]=0;M=[d.ino>>>0,(N=d.ino,1<=+Math.abs(N)?0>>0:~~+Math.ceil((N-+(~~N>>>0))/4294967296)>>>0:0)];L[c+80>>2]=M[0];L[c+84>>2]=M[1];return 0}var Zc=void 0;function $c(){Zc+=4;return L[Zc-4>>2]}function Z(a){a=S[a];if(!a)throw new O(8);return a}var ad={}; -function bd(){if(!cd){var a={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"===typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:xa||"./this.program"},b;for(b in ad)a[b]=ad[b];var c=[];for(b in a)c.push(b+"="+a[b]);cd=c}return cd}var cd,dd;za?dd=function(){var a=process.hrtime();return 1E3*a[0]+a[1]/1E6}:"undefined"!==typeof dateNow?dd=dateNow:dd=function(){return performance.now()}; -function ed(a){for(var b=dd();dd()-b>2]);L[b>>2]=a.getSeconds();L[b+4>>2]=a.getMinutes();L[b+8>>2]=a.getHours();L[b+12>>2]=a.getDate();L[b+16>>2]=a.getMonth();L[b+20>>2]=a.getFullYear()-1900;L[b+24>>2]=a.getDay();var c=new Date(a.getFullYear(),0,1);L[b+28>>2]=(a.getTime()-c.getTime())/864E5|0;L[b+36>>2]=-(60*a.getTimezoneOffset());var d=(new Date(a.getFullYear(),6,1)).getTimezoneOffset(); -c=c.getTimezoneOffset();a=(d!=c&&a.getTimezoneOffset()==Math.min(c,d))|0;L[b+32>>2]=a;a=L[xb()+(a?4:0)>>2];L[b+40>>2]=a;return b},j:function(a,b){try{a=A(a);if(b&-8)var c=-28;else{var d;(d=V(a,{Ya:!0}).node)?(a="",b&4&&(a+="r"),b&2&&(a+="w"),b&1&&(a+="x"),c=a&&Xb(d,a)?-2:0):c=-44}return c}catch(f){return"undefined"!==typeof X&&f instanceof O||K(f),-f.Pa}},v:function(a,b){try{return a=A(a),ea(a,b),0}catch(c){return"undefined"!==typeof X&&c instanceof O||K(c),-c.Pa}},D:function(a){try{return a=A(a), -lc(a),0}catch(b){return"undefined"!==typeof X&&b instanceof O||K(b),-b.Pa}},w:function(a,b){try{var c=S[a];if(!c)throw new O(8);ea(c.node,b);return 0}catch(d){return"undefined"!==typeof X&&d instanceof O||K(d),-d.Pa}},E:function(a){try{var b=S[a];if(!b)throw new O(8);lc(b.node);return 0}catch(c){return"undefined"!==typeof X&&c instanceof O||K(c),-c.Pa}},c:function(a,b,c){Zc=c;try{var d=Z(a);switch(b){case 0:var f=$c();return 0>f?-28:u(d.path,d.flags,0,f).fd;case 1:case 2:return 0;case 3:return d.flags; -case 4:return f=$c(),d.flags|=f,0;case 12:return f=$c(),La[f+0>>1]=2,0;case 13:case 14:return 0;case 16:case 8:return-28;case 9:return Bb(28),-1;default:return-28}}catch(g){return"undefined"!==typeof X&&g instanceof O||K(g),-g.Pa}},x:function(a,b){try{var c=Z(a);return Yc(jc,c.path,b)}catch(d){return"undefined"!==typeof X&&d instanceof O||K(d),-d.Pa}},i:function(a,b,c){try{var d=S[a];if(!d)throw new O(8);if(0===(d.flags&2097155))throw new O(28);mc(d.node,c);return 0}catch(f){return"undefined"!==typeof X&& -f instanceof O||K(f),-f.Pa}},J:function(a,b){try{if(0===b)return-28;if(b=c)var d=-28;else{var f=Tb(a),g=Math.min(c,aa(f)),n=z[b+g];k(f,m,b,c+1);z[b+g]=n;d=g}return d}catch(t){return"undefined"!==typeof X&&t instanceof O||K(t),-t.Pa}},C:function(a){try{a=A(a);var b=V(a,{parent:!0}).node,c=Ab(a),d=Ob(b,c),f=bc(b,c,!0);if(f)throw new O(f);if(!b.Na.rmdir)throw new O(63);if(d.ab)throw new O(10);try{U.willDeletePath&&U.willDeletePath(a)}catch(g){J("FS.trackingDelegate['willDeletePath']('"+a+"') threw an exception: "+ -g.message)}b.Na.rmdir(b,c);Wb(d);try{if(U.onDeletePath)U.onDeletePath(a)}catch(g){J("FS.trackingDelegate['onDeletePath']('"+a+"') threw an exception: "+g.message)}return 0}catch(g){return"undefined"!==typeof X&&g instanceof O||K(g),-g.Pa}},f:function(a,b){try{return a=A(a),Yc(jc,a,b)}catch(c){return"undefined"!==typeof X&&c instanceof O||K(c),-c.Pa}},H:function(a){try{return a=A(a),ta(a),0}catch(b){return"undefined"!==typeof X&&b instanceof O||K(b),-b.Pa}},n:function(a,b,c){m.copyWithin(a,b,b+c)}, -d:function(a){a>>>=0;var b=m.length;if(2147483648=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,a+100663296);d=Math.max(16777216,a,d);0>>16);Ya(Oa.buffer);var f=1;break a}catch(g){}f=void 0}if(f)return!0}return!1},p:function(a,b){var c=0;bd().forEach(function(d,f){var g=b+c;f=L[a+4*f>>2]=g;for(g=0;g>0]=d.charCodeAt(g);z[f>>0]=0;c+=d.length+1});return 0},q:function(a,b){var c= -bd();L[a>>2]=c.length;var d=0;c.forEach(function(f){d+=f.length+1});L[b>>2]=d;return 0},g:function(a){try{var b=Z(a);ka(b);return 0}catch(c){return"undefined"!==typeof X&&c instanceof O||K(c),c.Pa}},o:function(a,b){try{var c=Z(a);z[b>>0]=c.tty?2:Q(c.mode)?3:40960===(c.mode&61440)?7:4;return 0}catch(d){return"undefined"!==typeof X&&d instanceof O||K(d),d.Pa}},m:function(a,b,c,d,f){try{var g=Z(a);a=4294967296*c+(b>>>0);if(-9007199254740992>=a||9007199254740992<=a)return-61;Qc(g,a,d);M=[g.position>>> -0,(N=g.position,1<=+Math.abs(N)?0>>0:~~+Math.ceil((N-+(~~N>>>0))/4294967296)>>>0:0)];L[f>>2]=M[0];L[f+4>>2]=M[1];g.ob&&0===a&&0===d&&(g.ob=null);return 0}catch(n){return"undefined"!==typeof X&&n instanceof O||K(n),n.Pa}},K:function(a){try{var b=Z(a);return b.Oa&&b.Oa.fsync?-b.Oa.fsync(b):0}catch(c){return"undefined"!==typeof X&&c instanceof O||K(c),c.Pa}},I:function(a,b,c,d){try{a:{for(var f=Z(a),g=a=0;g>2],L[b+(8* -g+4)>>2],void 0);if(0>n){var t=-1;break a}a+=n}t=a}L[d>>2]=t;return 0}catch(w){return"undefined"!==typeof X&&w instanceof O||K(w),w.Pa}},h:function(a){var b=Date.now();L[a>>2]=b/1E3|0;L[a+4>>2]=b%1E3*1E3|0;return 0},a:Oa,k:function(a,b){if(0===a)return Bb(28),-1;var c=L[a>>2];a=L[a+4>>2];if(0>a||999999999c)return Bb(28),-1;0!==b&&(L[b>>2]=0,L[b+4>>2]=0);return ed(1E6*c+a/1E3)},B:function(a){switch(a){case 30:return 16384;case 85:return 131072;case 132:case 133:case 12:case 137:case 138:case 15:case 235:case 16:case 17:case 18:case 19:case 20:case 149:case 13:case 10:case 236:case 153:case 9:case 21:case 22:case 159:case 154:case 14:case 77:case 78:case 139:case 80:case 81:case 82:case 68:case 67:case 164:case 11:case 29:case 47:case 48:case 95:case 52:case 51:case 46:case 79:return 200809; -case 27:case 246:case 127:case 128:case 23:case 24:case 160:case 161:case 181:case 182:case 242:case 183:case 184:case 243:case 244:case 245:case 165:case 178:case 179:case 49:case 50:case 168:case 169:case 175:case 170:case 171:case 172:case 97:case 76:case 32:case 173:case 35:return-1;case 176:case 177:case 7:case 155:case 8:case 157:case 125:case 126:case 92:case 93:case 129:case 130:case 131:case 94:case 91:return 1;case 74:case 60:case 69:case 70:case 4:return 1024;case 31:case 42:case 72:return 32; -case 87:case 26:case 33:return 2147483647;case 34:case 1:return 47839;case 38:case 36:return 99;case 43:case 37:return 2048;case 0:return 2097152;case 3:return 65536;case 28:return 32768;case 44:return 32767;case 75:return 16384;case 39:return 1E3;case 89:return 700;case 71:return 256;case 40:return 255;case 2:return 100;case 180:return 64;case 25:return 20;case 5:return 16;case 6:return 6;case 73:return 4;case 84:return"object"===typeof navigator?navigator.hardwareConcurrency||1:1}Bb(28);return-1}, -L:function(a){var b=Date.now()/1E3|0;a&&(L[a>>2]=b);return b},s:function(a,b){if(b){var c=1E3*L[b+8>>2];c+=L[b+12>>2]/1E3}else c=Date.now();a=A(a);try{b=c;var d=V(a,{Ya:!0}).node;d.Na.Ta(d,{timestamp:Math.max(b,c)});return 0}catch(f){a=f;if(!(a instanceof O)){a+=" : ";a:{d=Error();if(!d.stack){try{throw Error();}catch(g){d=g}if(!d.stack){d="(no stack trace available)";break a}}d=d.stack.toString()}e.extraStackTrace&&(d+="\n"+e.extraStackTrace());d=ob(d);throw a+d;}Bb(a.Pa);return-1}}}; -(function(){function a(f){e.asm=f.exports;Ja=e.asm.M;eb--;e.monitorRunDependencies&&e.monitorRunDependencies(eb);0==eb&&(null!==fb&&(clearInterval(fb),fb=null),gb&&(f=gb,gb=null,f()))}function b(f){a(f.instance)}function c(f){return mb().then(function(g){return WebAssembly.instantiate(g,d)}).then(f,function(g){J("failed to asynchronously prepare wasm: "+g);K(g)})}var d={a:id};eb++;e.monitorRunDependencies&&e.monitorRunDependencies(eb);if(e.instantiateWasm)try{return e.instantiateWasm(d,a)}catch(f){return J("Module.instantiateWasm callback failed with error: "+ -f),!1}(function(){if(Ka||"function"!==typeof WebAssembly.instantiateStreaming||jb()||hb("file://")||"function"!==typeof fetch)return c(b);fetch(ib,{credentials:"same-origin"}).then(function(f){return WebAssembly.instantiateStreaming(f,d).then(b,function(g){J("wasm streaming compile failed: "+g);J("falling back to ArrayBuffer instantiation");return c(b)})})})();return{}})(); -var fd=e.___wasm_call_ctors=function(){return(fd=e.___wasm_call_ctors=e.asm.N).apply(null,arguments)},hd=e._memset=function(){return(hd=e._memset=e.asm.O).apply(null,arguments)};e._sqlite3_free=function(){return(e._sqlite3_free=e.asm.P).apply(null,arguments)};var Cb=e.___errno_location=function(){return(Cb=e.___errno_location=e.asm.Q).apply(null,arguments)};e._sqlite3_finalize=function(){return(e._sqlite3_finalize=e.asm.R).apply(null,arguments)}; -e._sqlite3_reset=function(){return(e._sqlite3_reset=e.asm.S).apply(null,arguments)};e._sqlite3_clear_bindings=function(){return(e._sqlite3_clear_bindings=e.asm.T).apply(null,arguments)};e._sqlite3_value_blob=function(){return(e._sqlite3_value_blob=e.asm.U).apply(null,arguments)};e._sqlite3_value_text=function(){return(e._sqlite3_value_text=e.asm.V).apply(null,arguments)};e._sqlite3_value_bytes=function(){return(e._sqlite3_value_bytes=e.asm.W).apply(null,arguments)}; -e._sqlite3_value_double=function(){return(e._sqlite3_value_double=e.asm.X).apply(null,arguments)};e._sqlite3_value_int=function(){return(e._sqlite3_value_int=e.asm.Y).apply(null,arguments)};e._sqlite3_value_type=function(){return(e._sqlite3_value_type=e.asm.Z).apply(null,arguments)};e._sqlite3_result_blob=function(){return(e._sqlite3_result_blob=e.asm._).apply(null,arguments)};e._sqlite3_result_double=function(){return(e._sqlite3_result_double=e.asm.$).apply(null,arguments)}; -e._sqlite3_result_error=function(){return(e._sqlite3_result_error=e.asm.aa).apply(null,arguments)};e._sqlite3_result_int=function(){return(e._sqlite3_result_int=e.asm.ba).apply(null,arguments)};e._sqlite3_result_int64=function(){return(e._sqlite3_result_int64=e.asm.ca).apply(null,arguments)};e._sqlite3_result_null=function(){return(e._sqlite3_result_null=e.asm.da).apply(null,arguments)};e._sqlite3_result_text=function(){return(e._sqlite3_result_text=e.asm.ea).apply(null,arguments)}; -e._sqlite3_step=function(){return(e._sqlite3_step=e.asm.fa).apply(null,arguments)};e._sqlite3_column_count=function(){return(e._sqlite3_column_count=e.asm.ga).apply(null,arguments)};e._sqlite3_data_count=function(){return(e._sqlite3_data_count=e.asm.ha).apply(null,arguments)};e._sqlite3_column_blob=function(){return(e._sqlite3_column_blob=e.asm.ia).apply(null,arguments)};e._sqlite3_column_bytes=function(){return(e._sqlite3_column_bytes=e.asm.ja).apply(null,arguments)}; -e._sqlite3_column_double=function(){return(e._sqlite3_column_double=e.asm.ka).apply(null,arguments)};e._sqlite3_column_text=function(){return(e._sqlite3_column_text=e.asm.la).apply(null,arguments)};e._sqlite3_column_type=function(){return(e._sqlite3_column_type=e.asm.ma).apply(null,arguments)};e._sqlite3_column_name=function(){return(e._sqlite3_column_name=e.asm.na).apply(null,arguments)};e._sqlite3_bind_blob=function(){return(e._sqlite3_bind_blob=e.asm.oa).apply(null,arguments)}; -e._sqlite3_bind_double=function(){return(e._sqlite3_bind_double=e.asm.pa).apply(null,arguments)};e._sqlite3_bind_int=function(){return(e._sqlite3_bind_int=e.asm.qa).apply(null,arguments)};e._sqlite3_bind_text=function(){return(e._sqlite3_bind_text=e.asm.ra).apply(null,arguments)};e._sqlite3_bind_parameter_index=function(){return(e._sqlite3_bind_parameter_index=e.asm.sa).apply(null,arguments)};e._sqlite3_sql=function(){return(e._sqlite3_sql=e.asm.ta).apply(null,arguments)}; -e._sqlite3_normalized_sql=function(){return(e._sqlite3_normalized_sql=e.asm.ua).apply(null,arguments)};e._sqlite3_errmsg=function(){return(e._sqlite3_errmsg=e.asm.va).apply(null,arguments)};e._sqlite3_exec=function(){return(e._sqlite3_exec=e.asm.wa).apply(null,arguments)};e._sqlite3_prepare_v2=function(){return(e._sqlite3_prepare_v2=e.asm.xa).apply(null,arguments)};e._sqlite3_changes=function(){return(e._sqlite3_changes=e.asm.ya).apply(null,arguments)}; -e._sqlite3_close_v2=function(){return(e._sqlite3_close_v2=e.asm.za).apply(null,arguments)};e._sqlite3_create_function_v2=function(){return(e._sqlite3_create_function_v2=e.asm.Aa).apply(null,arguments)};e._sqlite3_open=function(){return(e._sqlite3_open=e.asm.Ba).apply(null,arguments)};var ba=e._malloc=function(){return(ba=e._malloc=e.asm.Ca).apply(null,arguments)},na=e._free=function(){return(na=e._free=e.asm.Da).apply(null,arguments)}; -e._RegisterExtensionFunctions=function(){return(e._RegisterExtensionFunctions=e.asm.Ea).apply(null,arguments)}; -var xb=e.__get_tzname=function(){return(xb=e.__get_tzname=e.asm.Fa).apply(null,arguments)},vb=e.__get_daylight=function(){return(vb=e.__get_daylight=e.asm.Ga).apply(null,arguments)},ub=e.__get_timezone=function(){return(ub=e.__get_timezone=e.asm.Ha).apply(null,arguments)},oa=e.stackSave=function(){return(oa=e.stackSave=e.asm.Ia).apply(null,arguments)},qa=e.stackRestore=function(){return(qa=e.stackRestore=e.asm.Ja).apply(null,arguments)},y=e.stackAlloc=function(){return(y=e.stackAlloc=e.asm.Ka).apply(null, -arguments)},gd=e._memalign=function(){return(gd=e._memalign=e.asm.La).apply(null,arguments)};e.cwrap=function(a,b,c,d){c=c||[];var f=c.every(function(g){return"number"===g});return"string"!==b&&f&&!d?Qa(a):function(){return Ra(a,b,c,arguments)}};e.UTF8ToString=A;e.stackSave=oa;e.stackRestore=qa;e.stackAlloc=y;var jd;gb=function kd(){jd||ld();jd||(gb=kd)}; -function ld(){function a(){if(!jd&&(jd=!0,e.calledRun=!0,!Pa)){e.noFSInit||Uc||(Uc=!0,Tc(),e.stdin=e.stdin,e.stdout=e.stdout,e.stderr=e.stderr,e.stdin?Vc("stdin",e.stdin):ic("/dev/tty","/dev/stdin"),e.stdout?Vc("stdout",null,e.stdout):ic("/dev/tty","/dev/stdout"),e.stderr?Vc("stderr",null,e.stderr):ic("/dev/tty1","/dev/stderr"),u("/dev/stdin","r"),u("/dev/stdout","w"),u("/dev/stderr","w"));nb(ab);Sb=!1;nb(bb);if(e.onRuntimeInitialized)e.onRuntimeInitialized();if(e.postRun)for("function"==typeof e.postRun&& -(e.postRun=[e.postRun]);e.postRun.length;){var b=e.postRun.shift();cb.unshift(b)}nb(cb)}}if(!(0:_DEBUG>") -endfunction() - -set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") - -# Flutter library and tool build rules. -add_subdirectory(${FLUTTER_MANAGED_DIR}) - -# Application build -add_subdirectory("runner") - -# Generated plugin build rules, which manage building the plugins and adding -# them to the application. -include(flutter/generated_plugins.cmake) - - -# === Installation === -# Support files are copied into place next to the executable, so that it can -# run in place. This is done instead of making a separate bundle (as on Linux) -# so that building and running from within Visual Studio will work. -set(BUILD_BUNDLE_DIR "$") -# Make the "install" step default, as it's required to run. -set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) -endif() - -set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") -set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") - -install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) - -if(PLUGIN_BUNDLED_LIBRARIES) - install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" - DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endif() - -# Fully re-copy the assets directory on each build to avoid having stale files -# from a previous install. -set(FLUTTER_ASSET_DIR_NAME "flutter_assets") -install(CODE " - file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") - " COMPONENT Runtime) -install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" - DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) - -# Install the AOT library on non-Debug builds only. -install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - CONFIGURATIONS Profile;Release - COMPONENT Runtime) diff --git a/packages/stream_chat_flutter/example/windows/flutter/CMakeLists.txt b/packages/stream_chat_flutter/example/windows/flutter/CMakeLists.txt deleted file mode 100644 index b2e4bd8d65..0000000000 --- a/packages/stream_chat_flutter/example/windows/flutter/CMakeLists.txt +++ /dev/null @@ -1,103 +0,0 @@ -cmake_minimum_required(VERSION 3.14) - -set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") - -# Configuration provided via flutter tool. -include(${EPHEMERAL_DIR}/generated_config.cmake) - -# TODO: Move the rest of this into files in ephemeral. See -# https://github.com/flutter/flutter/issues/57146. -set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") - -# === Flutter Library === -set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") - -# Published to parent scope for install step. -set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) -set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) -set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) -set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) - -list(APPEND FLUTTER_LIBRARY_HEADERS - "flutter_export.h" - "flutter_windows.h" - "flutter_messenger.h" - "flutter_plugin_registrar.h" - "flutter_texture_registrar.h" -) -list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") -add_library(flutter INTERFACE) -target_include_directories(flutter INTERFACE - "${EPHEMERAL_DIR}" -) -target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") -add_dependencies(flutter flutter_assemble) - -# === Wrapper === -list(APPEND CPP_WRAPPER_SOURCES_CORE - "core_implementations.cc" - "standard_codec.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_PLUGIN - "plugin_registrar.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_APP - "flutter_engine.cc" - "flutter_view_controller.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") - -# Wrapper sources needed for a plugin. -add_library(flutter_wrapper_plugin STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} -) -apply_standard_settings(flutter_wrapper_plugin) -set_target_properties(flutter_wrapper_plugin PROPERTIES - POSITION_INDEPENDENT_CODE ON) -set_target_properties(flutter_wrapper_plugin PROPERTIES - CXX_VISIBILITY_PRESET hidden) -target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) -target_include_directories(flutter_wrapper_plugin PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_plugin flutter_assemble) - -# Wrapper sources needed for the runner. -add_library(flutter_wrapper_app STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_APP} -) -apply_standard_settings(flutter_wrapper_app) -target_link_libraries(flutter_wrapper_app PUBLIC flutter) -target_include_directories(flutter_wrapper_app PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_app flutter_assemble) - -# === Flutter tool backend === -# _phony_ is a non-existent file to force this command to run every time, -# since currently there's no way to get a full input/output list from the -# flutter tool. -set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") -set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) -add_custom_command( - OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} - ${PHONY_OUTPUT} - COMMAND ${CMAKE_COMMAND} -E env - ${FLUTTER_TOOL_ENVIRONMENT} - "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ - VERBATIM -) -add_custom_target(flutter_assemble DEPENDS - "${FLUTTER_LIBRARY}" - ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} -) diff --git a/packages/stream_chat_flutter/example/windows/flutter/generated_plugins.cmake b/packages/stream_chat_flutter/example/windows/flutter/generated_plugins.cmake deleted file mode 100644 index 9dcc325c0d..0000000000 --- a/packages/stream_chat_flutter/example/windows/flutter/generated_plugins.cmake +++ /dev/null @@ -1,34 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST - connectivity_plus - desktop_drop - file_selector_windows - gal - media_kit_video - record_windows - screen_brightness_windows - share_plus - sqlite3_flutter_libs - thumblr_windows - url_launcher_windows -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/packages/stream_chat_flutter/example/windows/runner/CMakeLists.txt b/packages/stream_chat_flutter/example/windows/runner/CMakeLists.txt deleted file mode 100644 index de2d8916b7..0000000000 --- a/packages/stream_chat_flutter/example/windows/runner/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -cmake_minimum_required(VERSION 3.14) -project(runner LANGUAGES CXX) - -add_executable(${BINARY_NAME} WIN32 - "flutter_window.cpp" - "main.cpp" - "utils.cpp" - "win32_window.cpp" - "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" - "Runner.rc" - "runner.exe.manifest" -) -apply_standard_settings(${BINARY_NAME}) -target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") -target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) -target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") -add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/packages/stream_chat_flutter/example/windows/runner/Runner.rc b/packages/stream_chat_flutter/example/windows/runner/Runner.rc deleted file mode 100644 index 5fdea291cf..0000000000 --- a/packages/stream_chat_flutter/example/windows/runner/Runner.rc +++ /dev/null @@ -1,121 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#pragma code_page(65001) -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "winres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDI_APP_ICON ICON "resources\\app_icon.ico" - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER -#else -#define VERSION_AS_NUMBER 1,0,0 -#endif - -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME -#else -#define VERSION_AS_STRING "1.0.0" -#endif - -VS_VERSION_INFO VERSIONINFO - FILEVERSION VERSION_AS_NUMBER - PRODUCTVERSION VERSION_AS_NUMBER - FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#ifdef _DEBUG - FILEFLAGS VS_FF_DEBUG -#else - FILEFLAGS 0x0L -#endif - FILEOS VOS__WINDOWS32 - FILETYPE VFT_APP - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904e4" - BEGIN - VALUE "CompanyName", "com.example" "\0" - VALUE "FileDescription", "example" "\0" - VALUE "FileVersion", VERSION_AS_STRING "\0" - VALUE "InternalName", "example" "\0" - VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0" - VALUE "OriginalFilename", "example.exe" "\0" - VALUE "ProductName", "example" "\0" - VALUE "ProductVersion", VERSION_AS_STRING "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED diff --git a/packages/stream_chat_flutter/example/windows/runner/flutter_window.cpp b/packages/stream_chat_flutter/example/windows/runner/flutter_window.cpp deleted file mode 100644 index b43b9095ea..0000000000 --- a/packages/stream_chat_flutter/example/windows/runner/flutter_window.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "flutter_window.h" - -#include - -#include "flutter/generated_plugin_registrant.h" - -FlutterWindow::FlutterWindow(const flutter::DartProject& project) - : project_(project) {} - -FlutterWindow::~FlutterWindow() {} - -bool FlutterWindow::OnCreate() { - if (!Win32Window::OnCreate()) { - return false; - } - - RECT frame = GetClientArea(); - - // The size here must match the window dimensions to avoid unnecessary surface - // creation / destruction in the startup path. - flutter_controller_ = std::make_unique( - frame.right - frame.left, frame.bottom - frame.top, project_); - // Ensure that basic setup of the controller was successful. - if (!flutter_controller_->engine() || !flutter_controller_->view()) { - return false; - } - RegisterPlugins(flutter_controller_->engine()); - SetChildContent(flutter_controller_->view()->GetNativeWindow()); - return true; -} - -void FlutterWindow::OnDestroy() { - if (flutter_controller_) { - flutter_controller_ = nullptr; - } - - Win32Window::OnDestroy(); -} - -LRESULT -FlutterWindow::MessageHandler(HWND hwnd, UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - // Give Flutter, including plugins, an opportunity to handle window messages. - if (flutter_controller_) { - std::optional result = - flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, - lparam); - if (result) { - return *result; - } - } - - switch (message) { - case WM_FONTCHANGE: - flutter_controller_->engine()->ReloadSystemFonts(); - break; - } - - return Win32Window::MessageHandler(hwnd, message, wparam, lparam); -} diff --git a/packages/stream_chat_flutter/example/windows/runner/flutter_window.h b/packages/stream_chat_flutter/example/windows/runner/flutter_window.h deleted file mode 100644 index 6da0652f05..0000000000 --- a/packages/stream_chat_flutter/example/windows/runner/flutter_window.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef RUNNER_FLUTTER_WINDOW_H_ -#define RUNNER_FLUTTER_WINDOW_H_ - -#include -#include - -#include - -#include "win32_window.h" - -// A window that does nothing but host a Flutter view. -class FlutterWindow : public Win32Window { - public: - // Creates a new FlutterWindow hosting a Flutter view running |project|. - explicit FlutterWindow(const flutter::DartProject& project); - virtual ~FlutterWindow(); - - protected: - // Win32Window: - bool OnCreate() override; - void OnDestroy() override; - LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, - LPARAM const lparam) noexcept override; - - private: - // The project to run. - flutter::DartProject project_; - - // The Flutter instance hosted by this window. - std::unique_ptr flutter_controller_; -}; - -#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/stream_chat_flutter/example/windows/runner/main.cpp b/packages/stream_chat_flutter/example/windows/runner/main.cpp deleted file mode 100644 index bcb57b0e2a..0000000000 --- a/packages/stream_chat_flutter/example/windows/runner/main.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include -#include - -#include "flutter_window.h" -#include "utils.h" - -int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, - _In_ wchar_t *command_line, _In_ int show_command) { - // Attach to console when present (e.g., 'flutter run') or create a - // new console when running with a debugger. - if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { - CreateAndAttachConsole(); - } - - // Initialize COM, so that it is available for use in the library and/or - // plugins. - ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); - - flutter::DartProject project(L"data"); - - std::vector command_line_arguments = - GetCommandLineArguments(); - - project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); - - FlutterWindow window(project); - Win32Window::Point origin(10, 10); - Win32Window::Size size(1280, 720); - if (!window.CreateAndShow(L"example", origin, size)) { - return EXIT_FAILURE; - } - window.SetQuitOnClose(true); - - ::MSG msg; - while (::GetMessage(&msg, nullptr, 0, 0)) { - ::TranslateMessage(&msg); - ::DispatchMessage(&msg); - } - - ::CoUninitialize(); - return EXIT_SUCCESS; -} diff --git a/packages/stream_chat_flutter/example/windows/runner/resource.h b/packages/stream_chat_flutter/example/windows/runner/resource.h deleted file mode 100644 index 66a65d1e4a..0000000000 --- a/packages/stream_chat_flutter/example/windows/runner/resource.h +++ /dev/null @@ -1,16 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by Runner.rc -// -#define IDI_APP_ICON 101 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/packages/stream_chat_flutter/example/windows/runner/resources/app_icon.ico b/packages/stream_chat_flutter/example/windows/runner/resources/app_icon.ico deleted file mode 100644 index c04e20caf6..0000000000 Binary files a/packages/stream_chat_flutter/example/windows/runner/resources/app_icon.ico and /dev/null differ diff --git a/packages/stream_chat_flutter/example/windows/runner/runner.exe.manifest b/packages/stream_chat_flutter/example/windows/runner/runner.exe.manifest deleted file mode 100644 index c977c4a425..0000000000 --- a/packages/stream_chat_flutter/example/windows/runner/runner.exe.manifest +++ /dev/null @@ -1,20 +0,0 @@ - - - - - PerMonitorV2 - - - - - - - - - - - - - - - diff --git a/packages/stream_chat_flutter/example/windows/runner/utils.cpp b/packages/stream_chat_flutter/example/windows/runner/utils.cpp deleted file mode 100644 index d19bdbbcc3..0000000000 --- a/packages/stream_chat_flutter/example/windows/runner/utils.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "utils.h" - -#include -#include -#include -#include - -#include - -void CreateAndAttachConsole() { - if (::AllocConsole()) { - FILE *unused; - if (freopen_s(&unused, "CONOUT$", "w", stdout)) { - _dup2(_fileno(stdout), 1); - } - if (freopen_s(&unused, "CONOUT$", "w", stderr)) { - _dup2(_fileno(stdout), 2); - } - std::ios::sync_with_stdio(); - FlutterDesktopResyncOutputStreams(); - } -} - -std::vector GetCommandLineArguments() { - // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. - int argc; - wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); - if (argv == nullptr) { - return std::vector(); - } - - std::vector command_line_arguments; - - // Skip the first argument as it's the binary name. - for (int i = 1; i < argc; i++) { - command_line_arguments.push_back(Utf8FromUtf16(argv[i])); - } - - ::LocalFree(argv); - - return command_line_arguments; -} - -std::string Utf8FromUtf16(const wchar_t* utf16_string) { - if (utf16_string == nullptr) { - return std::string(); - } - int target_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, nullptr, 0, nullptr, nullptr); - if (target_length == 0) { - return std::string(); - } - std::string utf8_string; - utf8_string.resize(target_length); - int converted_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, utf8_string.data(), - target_length, nullptr, nullptr); - if (converted_length == 0) { - return std::string(); - } - return utf8_string; -} diff --git a/packages/stream_chat_flutter/example/windows/runner/utils.h b/packages/stream_chat_flutter/example/windows/runner/utils.h deleted file mode 100644 index 3879d54755..0000000000 --- a/packages/stream_chat_flutter/example/windows/runner/utils.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef RUNNER_UTILS_H_ -#define RUNNER_UTILS_H_ - -#include -#include - -// Creates a console for the process, and redirects stdout and stderr to -// it for both the runner and the Flutter library. -void CreateAndAttachConsole(); - -// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string -// encoded in UTF-8. Returns an empty std::string on failure. -std::string Utf8FromUtf16(const wchar_t* utf16_string); - -// Gets the command line arguments passed in as a std::vector, -// encoded in UTF-8. Returns an empty std::vector on failure. -std::vector GetCommandLineArguments(); - -#endif // RUNNER_UTILS_H_ diff --git a/packages/stream_chat_flutter/example/windows/runner/win32_window.cpp b/packages/stream_chat_flutter/example/windows/runner/win32_window.cpp deleted file mode 100644 index c10f08dc7d..0000000000 --- a/packages/stream_chat_flutter/example/windows/runner/win32_window.cpp +++ /dev/null @@ -1,245 +0,0 @@ -#include "win32_window.h" - -#include - -#include "resource.h" - -namespace { - -constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; - -// The number of Win32Window objects that currently exist. -static int g_active_window_count = 0; - -using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); - -// Scale helper to convert logical scaler values to physical using passed in -// scale factor -int Scale(int source, double scale_factor) { - return static_cast(source * scale_factor); -} - -// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. -// This API is only needed for PerMonitor V1 awareness mode. -void EnableFullDpiSupportIfAvailable(HWND hwnd) { - HMODULE user32_module = LoadLibraryA("User32.dll"); - if (!user32_module) { - return; - } - auto enable_non_client_dpi_scaling = - reinterpret_cast( - GetProcAddress(user32_module, "EnableNonClientDpiScaling")); - if (enable_non_client_dpi_scaling != nullptr) { - enable_non_client_dpi_scaling(hwnd); - FreeLibrary(user32_module); - } -} - -} // namespace - -// Manages the Win32Window's window class registration. -class WindowClassRegistrar { - public: - ~WindowClassRegistrar() = default; - - // Returns the singleton registar instance. - static WindowClassRegistrar* GetInstance() { - if (!instance_) { - instance_ = new WindowClassRegistrar(); - } - return instance_; - } - - // Returns the name of the window class, registering the class if it hasn't - // previously been registered. - const wchar_t* GetWindowClass(); - - // Unregisters the window class. Should only be called if there are no - // instances of the window. - void UnregisterWindowClass(); - - private: - WindowClassRegistrar() = default; - - static WindowClassRegistrar* instance_; - - bool class_registered_ = false; -}; - -WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; - -const wchar_t* WindowClassRegistrar::GetWindowClass() { - if (!class_registered_) { - WNDCLASS window_class{}; - window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); - window_class.lpszClassName = kWindowClassName; - window_class.style = CS_HREDRAW | CS_VREDRAW; - window_class.cbClsExtra = 0; - window_class.cbWndExtra = 0; - window_class.hInstance = GetModuleHandle(nullptr); - window_class.hIcon = - LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); - window_class.hbrBackground = 0; - window_class.lpszMenuName = nullptr; - window_class.lpfnWndProc = Win32Window::WndProc; - RegisterClass(&window_class); - class_registered_ = true; - } - return kWindowClassName; -} - -void WindowClassRegistrar::UnregisterWindowClass() { - UnregisterClass(kWindowClassName, nullptr); - class_registered_ = false; -} - -Win32Window::Win32Window() { - ++g_active_window_count; -} - -Win32Window::~Win32Window() { - --g_active_window_count; - Destroy(); -} - -bool Win32Window::CreateAndShow(const std::wstring& title, - const Point& origin, - const Size& size) { - Destroy(); - - const wchar_t* window_class = - WindowClassRegistrar::GetInstance()->GetWindowClass(); - - const POINT target_point = {static_cast(origin.x), - static_cast(origin.y)}; - HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); - UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); - double scale_factor = dpi / 96.0; - - HWND window = CreateWindow( - window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, - Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), - Scale(size.width, scale_factor), Scale(size.height, scale_factor), - nullptr, nullptr, GetModuleHandle(nullptr), this); - - if (!window) { - return false; - } - - return OnCreate(); -} - -// static -LRESULT CALLBACK Win32Window::WndProc(HWND const window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - if (message == WM_NCCREATE) { - auto window_struct = reinterpret_cast(lparam); - SetWindowLongPtr(window, GWLP_USERDATA, - reinterpret_cast(window_struct->lpCreateParams)); - - auto that = static_cast(window_struct->lpCreateParams); - EnableFullDpiSupportIfAvailable(window); - that->window_handle_ = window; - } else if (Win32Window* that = GetThisFromHandle(window)) { - return that->MessageHandler(window, message, wparam, lparam); - } - - return DefWindowProc(window, message, wparam, lparam); -} - -LRESULT -Win32Window::MessageHandler(HWND hwnd, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - switch (message) { - case WM_DESTROY: - window_handle_ = nullptr; - Destroy(); - if (quit_on_close_) { - PostQuitMessage(0); - } - return 0; - - case WM_DPICHANGED: { - auto newRectSize = reinterpret_cast(lparam); - LONG newWidth = newRectSize->right - newRectSize->left; - LONG newHeight = newRectSize->bottom - newRectSize->top; - - SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, - newHeight, SWP_NOZORDER | SWP_NOACTIVATE); - - return 0; - } - case WM_SIZE: { - RECT rect = GetClientArea(); - if (child_content_ != nullptr) { - // Size and position the child window. - MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, - rect.bottom - rect.top, TRUE); - } - return 0; - } - - case WM_ACTIVATE: - if (child_content_ != nullptr) { - SetFocus(child_content_); - } - return 0; - } - - return DefWindowProc(window_handle_, message, wparam, lparam); -} - -void Win32Window::Destroy() { - OnDestroy(); - - if (window_handle_) { - DestroyWindow(window_handle_); - window_handle_ = nullptr; - } - if (g_active_window_count == 0) { - WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); - } -} - -Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { - return reinterpret_cast( - GetWindowLongPtr(window, GWLP_USERDATA)); -} - -void Win32Window::SetChildContent(HWND content) { - child_content_ = content; - SetParent(content, window_handle_); - RECT frame = GetClientArea(); - - MoveWindow(content, frame.left, frame.top, frame.right - frame.left, - frame.bottom - frame.top, true); - - SetFocus(child_content_); -} - -RECT Win32Window::GetClientArea() { - RECT frame; - GetClientRect(window_handle_, &frame); - return frame; -} - -HWND Win32Window::GetHandle() { - return window_handle_; -} - -void Win32Window::SetQuitOnClose(bool quit_on_close) { - quit_on_close_ = quit_on_close; -} - -bool Win32Window::OnCreate() { - // No-op; provided for subclasses. - return true; -} - -void Win32Window::OnDestroy() { - // No-op; provided for subclasses. -} diff --git a/packages/stream_chat_flutter/example/windows/runner/win32_window.h b/packages/stream_chat_flutter/example/windows/runner/win32_window.h deleted file mode 100644 index 17ba431125..0000000000 --- a/packages/stream_chat_flutter/example/windows/runner/win32_window.h +++ /dev/null @@ -1,98 +0,0 @@ -#ifndef RUNNER_WIN32_WINDOW_H_ -#define RUNNER_WIN32_WINDOW_H_ - -#include - -#include -#include -#include - -// A class abstraction for a high DPI-aware Win32 Window. Intended to be -// inherited from by classes that wish to specialize with custom -// rendering and input handling -class Win32Window { - public: - struct Point { - unsigned int x; - unsigned int y; - Point(unsigned int x, unsigned int y) : x(x), y(y) {} - }; - - struct Size { - unsigned int width; - unsigned int height; - Size(unsigned int width, unsigned int height) - : width(width), height(height) {} - }; - - Win32Window(); - virtual ~Win32Window(); - - // Creates and shows a win32 window with |title| and position and size using - // |origin| and |size|. New windows are created on the default monitor. Window - // sizes are specified to the OS in physical pixels, hence to ensure a - // consistent size to will treat the width height passed in to this function - // as logical pixels and scale to appropriate for the default monitor. Returns - // true if the window was created successfully. - bool CreateAndShow(const std::wstring& title, - const Point& origin, - const Size& size); - - // Release OS resources associated with window. - void Destroy(); - - // Inserts |content| into the window tree. - void SetChildContent(HWND content); - - // Returns the backing Window handle to enable clients to set icon and other - // window properties. Returns nullptr if the window has been destroyed. - HWND GetHandle(); - - // If true, closing this window will quit the application. - void SetQuitOnClose(bool quit_on_close); - - // Return a RECT representing the bounds of the current client area. - RECT GetClientArea(); - - protected: - // Processes and route salient window messages for mouse handling, - // size change and DPI. Delegates handling of these to member overloads that - // inheriting classes can handle. - virtual LRESULT MessageHandler(HWND window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept; - - // Called when CreateAndShow is called, allowing subclass window-related - // setup. Subclasses should return false if setup fails. - virtual bool OnCreate(); - - // Called when Destroy is called. - virtual void OnDestroy(); - - private: - friend class WindowClassRegistrar; - - // OS callback called by message pump. Handles the WM_NCCREATE message which - // is passed when the non-client area is being created and enables automatic - // non-client DPI scaling so that the non-client area automatically - // responsponds to changes in DPI. All other messages are handled by - // MessageHandler. - static LRESULT CALLBACK WndProc(HWND const window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept; - - // Retrieves a class instance pointer for |window| - static Win32Window* GetThisFromHandle(HWND const window) noexcept; - - bool quit_on_close_ = false; - - // window handle for top level window. - HWND window_handle_ = nullptr; - - // window handle for hosted content. - HWND child_content_ = nullptr; -}; - -#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/stream_chat_flutter/lib/conditional_parent_builder/README.md b/packages/stream_chat_flutter/lib/conditional_parent_builder/README.md deleted file mode 100644 index f4cd55e3df..0000000000 --- a/packages/stream_chat_flutter/lib/conditional_parent_builder/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# conditional_parent_builder - -A widget that allows developers to conditionally wrap a child widget with a parent widget. -This is useful in situations where a child widget should always be built, but should be wrapped -by another widget if certain conditions are met. - -In the following real-world example, we conditionally wrap the child widget -tree with a context menu if the message is not deleted: -```dart -ConditionalParentBuilder( - builder: (context, child) { - if (!widget.message.isDeleted) { - return ContextMenuArea( - builder: (context) => buildContextMenu(), - child: child, - ); - } else { - return child; - } - }, - child: Material(...), -), -``` -This example can be found in the `stream_chat_flutter` source code under -`src/message_widget/message_widget.dart`. \ No newline at end of file diff --git a/packages/stream_chat_flutter/lib/conditional_parent_builder/conditional_parent_builder.dart b/packages/stream_chat_flutter/lib/conditional_parent_builder/conditional_parent_builder.dart deleted file mode 100644 index 8314757de3..0000000000 --- a/packages/stream_chat_flutter/lib/conditional_parent_builder/conditional_parent_builder.dart +++ /dev/null @@ -1,55 +0,0 @@ -library conditional_parent_builder; - -import 'package:flutter/material.dart'; - -/// {@template parentBuilder} -/// A function that provides the [BuildContext] and the [child] widget. -/// {@endtemplate} -typedef ParentBuilder = Widget Function( - BuildContext context, - Widget child, -); - -/// {@template conditionalParentBuilder} -/// A widget that allows developers to conditionally wrap the [child] widget -/// with a parent widget. -/// -/// In the following real-world example, we conditionally wrap the child widget -/// tree with a context menu if the message is not deleted: -/// ```dart -/// ConditionalParentBuilder( -/// builder: (context, child) { -/// if (!widget.message.isDeleted) { -/// return ContextMenuArea( -/// builder: (context) => buildContextMenu(), -/// child: child, -/// ); -/// } else { -/// return child; -/// } -/// }, -/// child: Material(...), -/// ), -/// ``` -/// This example can be found in the `stream_chat_flutter` source code under -/// `src/message_widget/message_widget.dart`. -/// {@endtemplate} -class ConditionalParentBuilder extends StatelessWidget { - /// {@macro conditionalParentBuilder} - const ConditionalParentBuilder({ - super.key, - required this.builder, - required this.child, - }); - - /// {@macro parentBuilder} - final ParentBuilder builder; - - /// The child widget to build. - final Widget child; - - @override - Widget build(BuildContext context) { - return builder.call(context, child); - } -} diff --git a/packages/stream_chat_flutter/lib/platform_widget_builder/README.md b/packages/stream_chat_flutter/lib/platform_widget_builder/README.md deleted file mode 100644 index c936a5528f..0000000000 --- a/packages/stream_chat_flutter/lib/platform_widget_builder/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# platform_widget_builder - -This package is a more specialized version of the [flutter_platform_widgets](https://pub.dev/packages/flutter_platform_widgets) package. -It provides two specialized widget builders: -* `PlatformWidgetBuilder` -* `DesktopWidgetBuilder` - -### `PlatformWidgetBuilder` -This widget differs from the `PlatformWidgetBuilder` found in the `flutter_platform_widgets` package in that it -provides three builders: -* `mobile` -* `desktop` -* `web` - -This allows developers to build different widgets for their generalized platform targets - (Android + iOS = mobile, macOS + Windows + Linux = desktop). This is advantageous when requirements call for one set -of widgets that are identical across mobile, a different set of widgets that are identical across desktop, and another -that are required for web. - -### `DesktopWidgetBuilder` -This widget is more specialized than `PlatformWidgetBuilder` in that it allows for more targeted widget-building for -desktop platforms. It provides three builders: -* `macOS` -* `windows` -* `linux` - -This allows developers to build different widgets for the various desktop platforms. This is advantageous when building -native-looking desktop applications using the `macos_ui`, `fluent_ui`, and `yaru_widgets` packages. diff --git a/packages/stream_chat_flutter/lib/platform_widget_builder/platform_widget_builder.dart b/packages/stream_chat_flutter/lib/platform_widget_builder/platform_widget_builder.dart deleted file mode 100644 index 712d8798c5..0000000000 --- a/packages/stream_chat_flutter/lib/platform_widget_builder/platform_widget_builder.dart +++ /dev/null @@ -1,4 +0,0 @@ -library platform_widget_builder; - -export 'src/desktop_widget_builder.dart'; -export 'src/platform_widget_builder.dart'; diff --git a/packages/stream_chat_flutter/lib/platform_widget_builder/src/desktop_widget.dart b/packages/stream_chat_flutter/lib/platform_widget_builder/src/desktop_widget.dart deleted file mode 100644 index 349b9d4734..0000000000 --- a/packages/stream_chat_flutter/lib/platform_widget_builder/src/desktop_widget.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:stream_chat_flutter/platform_widget_builder/src/desktop_widget_base.dart'; - -/// A widget that will only be built for the specified desktop Platforms. -/// -/// See [DesktopWidgetBuilder] and [DesktopWidgetBase] for more. -/// -/// Also see: [PlatformWidget] and [PlatformWidgetBase]. -class DesktopWidget extends DesktopWidgetBase { - /// Builds a [DesktopWidget]. - const DesktopWidget({ - super.key, - this.macOS, - this.windows, - this.linux, - }); - - /// The widget to build for macOS. - final PlatformBuilder? macOS; - - /// The widget to build for Windows. - final PlatformBuilder? windows; - - /// The widget to build for Linux. - final PlatformBuilder? linux; - - @override - Widget createMacosWidget(BuildContext context) => - macOS?.call(context) ?? const SizedBox.shrink(); - - @override - Widget createWindowsWidget(BuildContext context) => - windows?.call(context) ?? const SizedBox.shrink(); - - @override - Widget createLinuxWidget(BuildContext context) => - linux?.call(context) ?? const SizedBox.shrink(); -} diff --git a/packages/stream_chat_flutter/lib/platform_widget_builder/src/desktop_widget_base.dart b/packages/stream_chat_flutter/lib/platform_widget_builder/src/desktop_widget_base.dart deleted file mode 100644 index f5a42a7bd7..0000000000 --- a/packages/stream_chat_flutter/lib/platform_widget_builder/src/desktop_widget_base.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart' show Theme; -import 'package:flutter/widgets.dart'; - -/// A generic widget builder function. -typedef PlatformBuilder = T Function( - BuildContext context, -); - -/// An abstract class used as a building block for creating -/// [DesktopPlatformWidget]s. -/// -/// This class is similar to [PlatformWidgetBase], which broadly covers -/// the platform categories; it combines the desktop platforms into a single -/// "desktop" target, for the purpose of returning the same widget one time -/// for all platforms in that category. [DesktopWidgetBase] differs in that -/// it more specifically targets each platform in the "desktop" category. -/// -/// This class utilizes generics to define the types of widgets it expects to -/// build: -/// * M = macOS -/// * W = Windows -/// * L = Linux -abstract class DesktopWidgetBase extends StatelessWidget { - /// Builds a [DesktopWidgetBase]. - const DesktopWidgetBase({super.key}); - - @override - Widget build(BuildContext context) { - final platform = Theme.of(context).platform; - if (platform == TargetPlatform.macOS) { - return createMacosWidget(context); - } else if (platform == TargetPlatform.windows) { - return createWindowsWidget(context); - } else if (platform == TargetPlatform.linux) { - return createLinuxWidget(context); - } - - return throw UnsupportedError( - 'This platform is not supported: $defaultTargetPlatform', - ); - } - - /// Builds a `M` macOS widget. - M createMacosWidget(BuildContext context); - - /// Builds a `W` Windows widget. - W createWindowsWidget(BuildContext context); - - /// Builds a `L` Linux widget. - L createLinuxWidget(BuildContext context); -} diff --git a/packages/stream_chat_flutter/lib/platform_widget_builder/src/desktop_widget_builder.dart b/packages/stream_chat_flutter/lib/platform_widget_builder/src/desktop_widget_builder.dart deleted file mode 100644 index 923678617d..0000000000 --- a/packages/stream_chat_flutter/lib/platform_widget_builder/src/desktop_widget_builder.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:stream_chat_flutter/platform_widget_builder/src/desktop_widget.dart'; - -/// A widget-building function that includes the child widget. -typedef DesktopTargetBuilder = Widget? Function( - BuildContext context, - Widget? child, -)?; - -/// A widget that utilizes [DesktopWidgetBuilder]s to build different widgets -/// for each specified desktop platform. -/// -/// Usage: -/// ``` -/// DesktopWidgetBuilder( -/// macOS: (context, child) => MacosWidget(), -/// windows: (context, child) => WindowsWidget(), -/// linux: (context, child) => LinuxWidget(), -/// ), -/// ``` -class DesktopWidgetBuilder extends StatelessWidget { - /// Builds a [DesktopWidgetBuilder]. - const DesktopWidgetBuilder({ - super.key, - this.child, - this.macOS, - this.windows, - this.linux, - }); - - /// The child widget. - final Widget? child; - - /// The widget to build for macOS. - final DesktopTargetBuilder? macOS; - - /// The widget to build for windows. - final DesktopTargetBuilder? windows; - - /// The widget to build for linux. - final DesktopTargetBuilder? linux; - - @override - Widget build(BuildContext context) { - return DesktopWidget( - macOS: (context) => macOS?.call(context, child), - windows: (context) => windows?.call(context, child), - linux: (context) => linux?.call(context, child), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/platform_widget_builder/src/platform_widget.dart b/packages/stream_chat_flutter/lib/platform_widget_builder/src/platform_widget.dart deleted file mode 100644 index 90789d865d..0000000000 --- a/packages/stream_chat_flutter/lib/platform_widget_builder/src/platform_widget.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:stream_chat_flutter/platform_widget_builder/src/platform_widget_base.dart'; - -/// A widget that will only be built for the specific Platforms: -/// -/// See [PlatformWidgetBuilder] and [PlatformWidgetBase] for more. -/// -/// Also see: [DesktopWidget] and [DesktopWidgetBase]. -class PlatformWidget extends PlatformWidgetBase { - /// Builds a [PlatformWidget]. - const PlatformWidget({ - super.key, - this.desktop, - this.mobile, - this.web, - }); - - /// The mobile widget to build. - final PlatformBuilder? mobile; - - /// The desktop widget to build. - final PlatformBuilder? desktop; - - /// The web widget to build. - final PlatformBuilder? web; - - @override - Widget createDesktopWidget(BuildContext context) => - desktop?.call(context) ?? const SizedBox.shrink(); - - @override - Widget createMobileWidget(BuildContext context) => - mobile?.call(context) ?? const SizedBox.shrink(); - - @override - Widget createWebWidget(BuildContext context) => - web?.call(context) ?? const SizedBox.shrink(); -} diff --git a/packages/stream_chat_flutter/lib/platform_widget_builder/src/platform_widget_base.dart b/packages/stream_chat_flutter/lib/platform_widget_builder/src/platform_widget_base.dart deleted file mode 100644 index 0487bd4396..0000000000 --- a/packages/stream_chat_flutter/lib/platform_widget_builder/src/platform_widget_base.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; - -/// A generic widget builder function. -typedef PlatformBuilder = T Function( - BuildContext context, -); - -/// An abstract class used as a building block for creating [PlatformWidget]s. -/// -/// This class broadly covers the platforms by combining Android and iOS -/// together as a "mobile" category, macOS, Windows, and Linux together as a -/// "desktop" category. This is useful is cases where a widget is expected to -/// be the same for the various mobile and desktop categories, and would -/// therefore be tedious to return the same widget more than once for the -/// specified category. This is unlike [DesktopWidgetBase], which more -/// specifically targets the various platforms in the "desktop" category. -/// -/// This class utilizes generics to define the types of widgets it expects to -/// build: -/// * M = Mobile -/// * D = Desktop -/// * W = Web -abstract class PlatformWidgetBase extends StatelessWidget { - /// Builds a [PlatformWidgetBase]. - const PlatformWidgetBase({ - super.key, - }); - - @override - Widget build(BuildContext context) { - final platform = defaultTargetPlatform; - if (platform == TargetPlatform.android || platform == TargetPlatform.iOS) { - return createMobileWidget(context); - } else if (platform == TargetPlatform.macOS || - platform == TargetPlatform.windows || - platform == TargetPlatform.linux) { - return createDesktopWidget(context); - } else { - return createWebWidget(context); - } - } - - /// Builds a `M` mobile widget. - M createMobileWidget(BuildContext context); - - /// Builds a `D` desktop widget. - D createDesktopWidget(BuildContext context); - - /// Builds a `W` web widget. - W createWebWidget(BuildContext context); -} diff --git a/packages/stream_chat_flutter/lib/platform_widget_builder/src/platform_widget_builder.dart b/packages/stream_chat_flutter/lib/platform_widget_builder/src/platform_widget_builder.dart deleted file mode 100644 index 13b0a13cda..0000000000 --- a/packages/stream_chat_flutter/lib/platform_widget_builder/src/platform_widget_builder.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:stream_chat_flutter/platform_widget_builder/src/platform_widget.dart'; - -/// A widget-building function that includes the child widget. -typedef PlatformTargetBuilder = Widget? Function( - BuildContext context, - Widget? child, -)?; - -/// A widget that utilizes [PlatformTargetBuilder]s to build different widgets -/// for each specified platform. -/// -/// In the following real-world example, only the `mobile` builder is used -/// to ensure the child widget is only built for Android and iOS: -/// ``` -/// PlatformWidgetBuilder( -/// mobile: (context, child) => _buildFilePickerSection(), -/// ), -/// ``` -class PlatformWidgetBuilder extends StatelessWidget { - /// Builds a [PlatformWidgetBuilder]. - const PlatformWidgetBuilder({ - super.key, - this.child, - this.mobile, - this.desktop, - this.web, - }); - - /// The child widget. - final Widget? child; - - /// The widget to build for mobile platforms. - final PlatformTargetBuilder? mobile; - - /// The widget to build for desktop platforms. - final PlatformTargetBuilder? desktop; - - /// The widget to build for web platforms. - final PlatformTargetBuilder? web; - - @override - Widget build(BuildContext context) { - return PlatformWidget( - desktop: (context) => desktop?.call(context, child), - mobile: (context) => mobile?.call(context, child), - web: (context) => web?.call(context, child), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/scrollable_positioned_list/LICENSE b/packages/stream_chat_flutter/lib/scrollable_positioned_list/LICENSE deleted file mode 100644 index 7cc8ade111..0000000000 --- a/packages/stream_chat_flutter/lib/scrollable_positioned_list/LICENSE +++ /dev/null @@ -1,26 +0,0 @@ -Copyright 2018 the Dart project authors, Inc. All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/packages/stream_chat_flutter/lib/scrollable_positioned_list/scrollable_positioned_list.dart b/packages/stream_chat_flutter/lib/scrollable_positioned_list/scrollable_positioned_list.dart deleted file mode 100644 index c275b61157..0000000000 --- a/packages/stream_chat_flutter/lib/scrollable_positioned_list/scrollable_positioned_list.dart +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -export 'src/indexed_key.dart'; -export 'src/item_positions_listener.dart'; -export 'src/scrollable_positioned_list.dart'; diff --git a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/element_registry.dart b/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/element_registry.dart deleted file mode 100644 index 03f274f387..0000000000 --- a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/element_registry.dart +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/widgets.dart'; - -/// A registry to track some [Element]s in the tree. -class RegistryWidget extends StatefulWidget { - /// Creates a [RegistryWidget]. - const RegistryWidget({super.key, this.elementNotifier, required this.child}); - - /// The widget below this widget in the tree. - final Widget child; - - /// Contains the current set of all [Element]s created by - /// [RegisteredElementWidget]s in the tree below this widget. - /// - /// Note that if there is another [RegistryWidget] in this widget's subtree - /// that registry, and not this one, will collect elements in its subtree. - final ValueNotifier?>? elementNotifier; - - @override - State createState() => _RegistryWidgetState(); -} - -/// A widget whose [Element] will be added its nearest ancestor -/// [RegistryWidget]. -class RegisteredElementWidget extends ProxyWidget { - /// Creates a [RegisteredElementWidget]. - const RegisteredElementWidget({super.key, required super.child}); - - @override - Element createElement() => _RegisteredElement(this); -} - -class _RegistryWidgetState extends State { - final Set registeredElements = {}; - - @override - Widget build(BuildContext context) => _InheritedRegistryWidget( - state: this, - child: widget.child, - ); -} - -class _InheritedRegistryWidget extends InheritedWidget { - const _InheritedRegistryWidget({ - required this.state, - required super.child, - }); - - final _RegistryWidgetState state; - - @override - bool updateShouldNotify(InheritedWidget oldWidget) => true; -} - -class _RegisteredElement extends ProxyElement { - _RegisteredElement(super.widget); - - @override - void notifyClients(ProxyWidget oldWidget) {} - - late _RegistryWidgetState _registryWidgetState; - - @override - void mount(Element? parent, dynamic newSlot) { - super.mount(parent, newSlot); - final _inheritedRegistryWidget = - dependOnInheritedWidgetOfExactType<_InheritedRegistryWidget>()!; - _registryWidgetState = _inheritedRegistryWidget.state; - _registryWidgetState.registeredElements.add(this); - _registryWidgetState.widget.elementNotifier?.value = - _registryWidgetState.registeredElements; - } - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - final _inheritedRegistryWidget = - dependOnInheritedWidgetOfExactType<_InheritedRegistryWidget>()!; - _registryWidgetState = _inheritedRegistryWidget.state; - _registryWidgetState.registeredElements.add(this); - _registryWidgetState.widget.elementNotifier?.value = - _registryWidgetState.registeredElements; - } - - @override - void unmount() { - _registryWidgetState.registeredElements.remove(this); - _registryWidgetState.widget.elementNotifier?.value = - _registryWidgetState.registeredElements; - super.unmount(); - } -} diff --git a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/indexed_key.dart b/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/indexed_key.dart deleted file mode 100644 index 23b6c845a3..0000000000 --- a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/indexed_key.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/foundation.dart'; - -/// {@template indexed_key} -/// Creates an indexed key that delegates its [operator==] to the given key. -/// -/// It contains an index used in [ScrollablePositionedList]. -/// {@endtemplate} -class IndexedKey extends LocalKey { - /// {@macro indexed_key} - const IndexedKey(this.key, this.index); - - /// The key to which this this delegates its [operator==]. - final Key? key; - - /// Index used to show position in a list. - final int index; - - @override - bool operator ==(Object other) { - if (other.runtimeType != runtimeType) return false; - return other is IndexedKey && other.key == key; - } - - @override - int get hashCode => Object.hash(runtimeType, key); - - @override - String toString() => '(IndexedKey) index: $index, key: $key'; -} diff --git a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/item_positions_listener.dart b/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/item_positions_listener.dart deleted file mode 100644 index d2752a00b2..0000000000 --- a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/item_positions_listener.dart +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/foundation.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/item_positions_notifier.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/scrollable_positioned_list.dart'; - -/// Provides a listenable iterable of [itemPositions] of items that are on -/// screen and their locations. -abstract class ItemPositionsListener { - /// Creates an [ItemPositionsListener] that can be used by a - /// [ScrollablePositionedList] to return the current position of items. - factory ItemPositionsListener.create() => ItemPositionsNotifier(); - - /// The position of items that are at least partially visible in the viewport. - ValueListenable> get itemPositions; -} - -/// Position information for an item in the list. -class ItemPosition { - /// Create an [ItemPosition]. - const ItemPosition({ - required this.index, - required this.itemLeadingEdge, - required this.itemTrailingEdge, - }); - - /// Index of the item. - final int index; - - /// Distance in proportion of the viewport's main axis length from the leading - /// edge of the viewport to the leading edge of the item. - /// - /// May be negative if the item is partially visible. - final double itemLeadingEdge; - - /// Distance in proportion of the viewport's main axis length from the leading - /// edge of the viewport to the trailing edge of the item. - /// - /// May be greater than one if the item is partially visible. - final double itemTrailingEdge; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is ItemPosition && - runtimeType == other.runtimeType && - index == other.index && - itemLeadingEdge == other.itemLeadingEdge && - itemTrailingEdge == other.itemTrailingEdge; - - @override - int get hashCode => - 31 * (31 * (index.hashCode + 7) + itemLeadingEdge.hashCode) + - itemTrailingEdge.hashCode; - - @override - String toString() => - '''ItemPosition(index: $index, itemLeadingEdge: $itemLeadingEdge, itemTrailingEdge: $itemTrailingEdge)'''; -} diff --git a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/item_positions_notifier.dart b/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/item_positions_notifier.dart deleted file mode 100644 index 8759ddeadb..0000000000 --- a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/item_positions_notifier.dart +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/foundation.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/item_positions_listener.dart'; - -/// Internal implementation of [ItemPositionsListener]. -class ItemPositionsNotifier implements ItemPositionsListener { - @override - final ValueNotifier> itemPositions = ValueNotifier([]); -} diff --git a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/positioned_list.dart b/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/positioned_list.dart deleted file mode 100644 index df08329cf8..0000000000 --- a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/positioned_list.dart +++ /dev/null @@ -1,399 +0,0 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter/scheduler.dart'; -import 'package:flutter/widgets.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/element_registry.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/indexed_key.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/item_positions_listener.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/item_positions_notifier.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/scroll_view.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/wrapping.dart'; - -/// A list of widgets similar to [ListView], except scroll control -/// and position reporting is based on index rather than pixel offset. -/// -/// [PositionedList] lays out children in the same way as [ListView]. -/// -/// The list can be displayed with the item at [positionIndex] positioned at a -/// particular [alignment]. See [ItemScrollController.jumpTo] for an -/// explanation of alignment. -/// -/// All other parameters are the same as specified in [ListView]. -class PositionedList extends StatefulWidget { - /// Create a [PositionedList]. - const PositionedList({ - super.key, - required this.itemCount, - required this.itemBuilder, - this.separatorBuilder, - this.controller, - this.itemPositionsNotifier, - this.positionedIndex = 0, - this.alignment = 0, - this.scrollDirection = Axis.vertical, - this.reverse = false, - this.shrinkWrap = false, - this.physics, - this.padding, - this.cacheExtent, - this.semanticChildCount, - this.addSemanticIndexes = true, - this.addRepaintBoundaries = true, - this.addAutomaticKeepAlives = true, - this.findChildIndexCallback, - this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, - }) : assert( - (positionedIndex == 0) || (positionedIndex < itemCount), - 'positionedIndex must be 0 or a value less than itemCount', - ); - - /// Number of items the [itemBuilder] can produce. - final int itemCount; - - /// Called to build children for the list with - /// 0 <= index < itemCount. - final IndexedWidgetBuilder itemBuilder; - - /// If not null, called to build separators for between each item in the list. - /// Called with 0 <= index < itemCount - 1. - final IndexedWidgetBuilder? separatorBuilder; - - /// An object that can be used to control the position to which this scroll - /// view is scrolled. - final ScrollController? controller; - - /// Notifier that reports the items laid out in the list after each frame. - final ItemPositionsNotifier? itemPositionsNotifier; - - /// Index of an item to initially align to a position within the viewport - /// defined by [alignment]. - final int positionedIndex; - - /// Determines where the leading edge of the item at [positionedIndex] - /// should be placed. - /// - /// See [ItemScrollController.jumpTo] for an explanation of alignment. - final double alignment; - - /// The axis along which the scroll view scrolls. - /// - /// Defaults to [Axis.vertical]. - final Axis scrollDirection; - - /// Whether the view scrolls in the reading direction. - /// - /// Defaults to false. - /// - /// See [ScrollView.reverse]. - final bool reverse; - - /// {@template flutter.widgets.scroll_view.shrinkWrap} - /// Whether the extent of the scroll view in the [scrollDirection] should be - /// determined by the contents being viewed. - /// - /// Defaults to false. - /// - /// See [ScrollView.shrinkWrap]. - /// {@endtemplate} - final bool shrinkWrap; - - /// How the scroll view should respond to user input. - /// - /// For example, determines how the scroll view continues to animate after the - /// user stops dragging the scroll view. - /// - /// See [ScrollView.physics]. - final ScrollPhysics? physics; - - /// {@macro flutter.widgets.scrollable.cacheExtent} - final double? cacheExtent; - - /// The number of children that will contribute semantic information. - /// - /// See [ScrollView.semanticChildCount] for more information. - final int? semanticChildCount; - - /// Whether to wrap each child in an [IndexedSemantics]. - /// - /// See [SliverChildBuilderDelegate.addSemanticIndexes]. - final bool addSemanticIndexes; - - /// The amount of space by which to inset the children. - final EdgeInsets? padding; - - /// Whether to wrap each child in a [RepaintBoundary]. - /// - /// See [SliverChildBuilderDelegate.addRepaintBoundaries]. - final bool addRepaintBoundaries; - - /// Whether to wrap each child in an [AutomaticKeepAlive]. - /// - /// See [SliverChildBuilderDelegate.addAutomaticKeepAlives]. - final bool addAutomaticKeepAlives; - - /// Called to find the new index of a child based on its key in case of reordering. - /// - /// If not provided, a child widget may not map to its existing [RenderObject] - /// when the order of children returned from the children builder changes. - /// This may result in state-loss. - /// - /// This callback should take an input [Key], and it should return the - /// index of the child element with that associated key, or null if not found. - /// - /// See [SliverChildBuilderDelegate.findChildIndexCallback]. - final ChildIndexGetter? findChildIndexCallback; - - /// Defines how this [ScrollView] will dismiss the keyboard automatically. - /// - /// See [ScrollView.keyboardDismissBehavior]. - final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior; - - @override - State createState() => _PositionedListState(); -} - -class _PositionedListState extends State { - final Key _centerKey = UniqueKey(); - - final registeredElements = ValueNotifier?>(null); - late final ScrollController scrollController; - - bool updateScheduled = false; - - @override - void initState() { - super.initState(); - scrollController = widget.controller ?? ScrollController(); - scrollController.addListener(_schedulePositionNotificationUpdate); - _schedulePositionNotificationUpdate(); - } - - @override - void dispose() { - scrollController.removeListener(_schedulePositionNotificationUpdate); - super.dispose(); - } - - @override - void didUpdateWidget(PositionedList oldWidget) { - super.didUpdateWidget(oldWidget); - _schedulePositionNotificationUpdate(); - } - - @override - Widget build(BuildContext context) => RegistryWidget( - elementNotifier: registeredElements, - child: UnboundedCustomScrollView( - anchor: widget.alignment, - center: _centerKey, - controller: scrollController, - scrollDirection: widget.scrollDirection, - reverse: widget.reverse, - cacheExtent: widget.cacheExtent, - physics: widget.physics, - shrinkWrap: widget.shrinkWrap, - semanticChildCount: widget.semanticChildCount ?? widget.itemCount, - keyboardDismissBehavior: widget.keyboardDismissBehavior, - slivers: [ - if (widget.positionedIndex > 0) - SliverPadding( - padding: _leadingSliverPadding, - sliver: SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) => widget.separatorBuilder == null - ? _buildItem(widget.positionedIndex - (index + 1)) - : _buildSeparatedListElement( - widget.positionedIndex * 2 - (index + 1), - ), - childCount: widget.separatorBuilder == null - ? widget.positionedIndex - : widget.positionedIndex * 2, - addSemanticIndexes: false, - addRepaintBoundaries: widget.addRepaintBoundaries, - addAutomaticKeepAlives: widget.addAutomaticKeepAlives, - findChildIndexCallback: widget.findChildIndexCallback, - ), - ), - ), - SliverPadding( - key: _centerKey, - padding: _centerSliverPadding, - sliver: SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) => widget.separatorBuilder == null - ? _buildItem(index + widget.positionedIndex) - : _buildSeparatedListElement( - index + widget.positionedIndex * 2, - ), - childCount: widget.itemCount != 0 ? 1 : 0, - addSemanticIndexes: false, - addRepaintBoundaries: widget.addRepaintBoundaries, - addAutomaticKeepAlives: widget.addAutomaticKeepAlives, - findChildIndexCallback: widget.findChildIndexCallback, - ), - ), - ), - if (widget.positionedIndex >= 0 && - widget.positionedIndex < widget.itemCount - 1) - SliverPadding( - padding: _trailingSliverPadding, - sliver: SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) => widget.separatorBuilder == null - ? _buildItem(index + widget.positionedIndex + 1) - : _buildSeparatedListElement( - index + widget.positionedIndex * 2 + 1, - ), - childCount: widget.separatorBuilder == null - ? widget.itemCount - widget.positionedIndex - 1 - : 2 * (widget.itemCount - widget.positionedIndex - 1), - addSemanticIndexes: false, - addRepaintBoundaries: widget.addRepaintBoundaries, - addAutomaticKeepAlives: widget.addAutomaticKeepAlives, - findChildIndexCallback: widget.findChildIndexCallback, - ), - ), - ), - ], - ), - ); - - Widget _buildSeparatedListElement(int index) { - if (index.isEven) { - return _buildItem(index ~/ 2); - } else { - return widget.separatorBuilder!(context, index ~/ 2); - } - } - - Widget _buildItem(int index) { - final child = widget.itemBuilder(context, index); - return RegisteredElementWidget( - key: IndexedKey(child.key, index), - child: widget.addSemanticIndexes - ? IndexedSemantics(index: index, child: child) - : child, - ); - } - - EdgeInsets get _leadingSliverPadding => - (widget.scrollDirection == Axis.vertical - ? widget.reverse - ? widget.padding?.copyWith(top: 0) - : widget.padding?.copyWith(bottom: 0) - : widget.reverse - ? widget.padding?.copyWith(left: 0) - : widget.padding?.copyWith(right: 0)) ?? - EdgeInsets.zero; - - EdgeInsets get _centerSliverPadding => widget.scrollDirection == Axis.vertical - ? widget.reverse - ? widget.padding?.copyWith( - top: widget.positionedIndex == widget.itemCount - 1 - ? widget.padding!.top - : 0, - bottom: - widget.positionedIndex == 0 ? widget.padding!.bottom : 0, - ) ?? - EdgeInsets.zero - : widget.padding?.copyWith( - top: widget.positionedIndex == 0 ? widget.padding!.top : 0, - bottom: widget.positionedIndex == widget.itemCount - 1 - ? widget.padding!.bottom - : 0, - ) ?? - EdgeInsets.zero - : widget.reverse - ? widget.padding?.copyWith( - left: widget.positionedIndex == widget.itemCount - 1 - ? widget.padding!.left - : 0, - right: widget.positionedIndex == 0 ? widget.padding!.right : 0, - ) ?? - EdgeInsets.zero - : widget.padding?.copyWith( - left: widget.positionedIndex == 0 ? widget.padding!.left : 0, - right: widget.positionedIndex == widget.itemCount - 1 - ? widget.padding!.right - : 0, - ) ?? - EdgeInsets.zero; - - EdgeInsets get _trailingSliverPadding => - widget.scrollDirection == Axis.vertical - ? widget.reverse - ? widget.padding?.copyWith(bottom: 0) ?? EdgeInsets.zero - : widget.padding?.copyWith(top: 0) ?? EdgeInsets.zero - : widget.reverse - ? widget.padding?.copyWith(right: 0) ?? EdgeInsets.zero - : widget.padding?.copyWith(left: 0) ?? EdgeInsets.zero; - - void _schedulePositionNotificationUpdate() { - if (!updateScheduled) { - updateScheduled = true; - SchedulerBinding.instance.addPostFrameCallback((_) { - final elements = registeredElements.value; - if (elements == null) { - updateScheduled = false; - return; - } - final positions = []; - RenderViewportBase? viewport; - for (final element in elements) { - final box = element.renderObject! as RenderBox; - viewport ??= RenderAbstractViewport.of(box) as RenderViewportBase?; - var anchor = 0.0; - if (viewport is RenderViewport) { - anchor = viewport.anchor; - } - - if (viewport is CustomRenderViewport) { - anchor = viewport.anchor; - } - - final key = element.widget.key! as IndexedKey; - // Skip this element if `box` has never been laid out. - if (!box.hasSize) continue; - if (widget.scrollDirection == Axis.vertical) { - final reveal = viewport!.getOffsetToReveal(box, 0).offset; - if (!reveal.isFinite) continue; - final itemOffset = - reveal - viewport.offset.pixels + anchor * viewport.size.height; - positions.add(ItemPosition( - index: key.index, - itemLeadingEdge: itemOffset.round() / - scrollController.position.viewportDimension, - itemTrailingEdge: (itemOffset + box.size.height).round() / - scrollController.position.viewportDimension, - )); - } else { - final itemOffset = - box.localToGlobal(Offset.zero, ancestor: viewport).dx; - if (!itemOffset.isFinite) continue; - positions.add(ItemPosition( - index: key.index, - itemLeadingEdge: (widget.reverse - ? scrollController.position.viewportDimension - - (itemOffset + box.size.width) - : itemOffset) - .round() / - scrollController.position.viewportDimension, - itemTrailingEdge: (widget.reverse - ? scrollController.position.viewportDimension - - itemOffset - : (itemOffset + box.size.width)) - .round() / - scrollController.position.viewportDimension, - )); - } - } - widget.itemPositionsNotifier?.itemPositions.value = positions; - updateScheduled = false; - }); - } - } -} diff --git a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/post_mount_callback.dart b/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/post_mount_callback.dart deleted file mode 100644 index c79c275e7f..0000000000 --- a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/post_mount_callback.dart +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/widgets.dart'; - -/// Widget whose [Element] calls a callback when the element is mounted. -class PostMountCallback extends StatelessWidget { - /// Creates a [PostMountCallback] widget. - const PostMountCallback({required this.child, this.callback, super.key}); - - /// The widget below this widget in the tree. - final Widget child; - - /// Callback to call when the element for this widget is mounted. - final void Function()? callback; - - @override - StatelessElement createElement() => _PostMountCallbackElement(this); - - @override - Widget build(BuildContext context) => child; -} - -class _PostMountCallbackElement extends StatelessElement { - _PostMountCallbackElement(PostMountCallback super.widget); - - @override - void mount(Element? parent, dynamic newSlot) { - super.mount(parent, newSlot); - final postMountCallback = widget as PostMountCallback; - postMountCallback.callback?.call(); - } -} diff --git a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/scroll_view.dart b/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/scroll_view.dart deleted file mode 100644 index 911512495b..0000000000 --- a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/scroll_view.dart +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/rendering.dart'; -import 'package:flutter/widgets.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/viewport.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/wrapping.dart'; - -/// {@template unbounded_custom_scroll_view} -/// A version of [CustomScrollView] that allows does not constrict the extents -/// to be within 0 and 1. See [CustomScrollView] for more information. -/// {@endtemplate} -class UnboundedCustomScrollView extends CustomScrollView { - /// {@macro unbounded_custom_scroll_view} - const UnboundedCustomScrollView({ - super.key, - super.scrollDirection, - super.reverse, - super.controller, - super.primary, - super.physics, - bool shrinkWrap = false, - super.center, - double anchor = 0.0, - super.cacheExtent, - super.slivers, - super.semanticChildCount, - super.dragStartBehavior, - super.keyboardDismissBehavior, - }) : _shrinkWrap = shrinkWrap, - _anchor = anchor, - super(shrinkWrap: false); - - final bool _shrinkWrap; - - // [CustomScrollView] enforces constraints on [CustomScrollView.anchor], so - // we need our own version. - final double _anchor; - - @override - double get anchor => _anchor; - - /// Build the viewport. - @override - @protected - Widget buildViewport( - BuildContext context, - ViewportOffset offset, - AxisDirection axisDirection, - List slivers, - ) { - if (_shrinkWrap) { - return CustomShrinkWrappingViewport( - axisDirection: axisDirection, - offset: offset, - slivers: slivers, - cacheExtent: cacheExtent, - center: center, - anchor: anchor, - ); - } - return UnboundedViewport( - axisDirection: axisDirection, - offset: offset, - slivers: slivers, - cacheExtent: cacheExtent, - center: center, - anchor: anchor, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/scrollable_positioned_list.dart b/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/scrollable_positioned_list.dart deleted file mode 100644 index af44d52562..0000000000 --- a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/scrollable_positioned_list.dart +++ /dev/null @@ -1,631 +0,0 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; -import 'dart:math'; - -import 'package:collection/collection.dart' show IterableExtension; -import 'package:flutter/scheduler.dart'; -import 'package:flutter/widgets.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/item_positions_listener.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/item_positions_notifier.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/positioned_list.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/post_mount_callback.dart'; - -/// Number of screens to scroll when scrolling a long distance. -const int _screenScrollCount = 2; - -/// A scrollable list of widgets similar to [ListView], except scroll control -/// and position reporting is based on index rather than pixel offset. -/// -/// [ScrollablePositionedList] lays out children in the same way as [ListView]. -/// -/// The list can be displayed with the item at [initialScrollIndex] positioned -/// at a particular [initialAlignment]. -/// -/// The [itemScrollController] can be used to scroll or jump to particular items -/// in the list. The [itemPositionsNotifier] can be used to get a list of items -/// currently laid out by the list. -/// -/// All other parameters are the same as specified in [ListView]. -class ScrollablePositionedList extends StatefulWidget { - /// Create a [ScrollablePositionedList] whose items are provided by - /// [itemBuilder]. - const ScrollablePositionedList.builder({ - required this.itemCount, - required this.itemBuilder, - super.key, - this.itemScrollController, - this.shrinkWrap = false, - ItemPositionsListener? itemPositionsListener, - this.initialScrollIndex = 0, - this.initialAlignment = 0, - this.scrollDirection = Axis.vertical, - this.reverse = false, - this.physics, - this.semanticChildCount, - this.padding, - this.addSemanticIndexes = true, - this.addAutomaticKeepAlives = true, - this.addRepaintBoundaries = true, - this.minCacheExtent, - this.findChildIndexCallback, - this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, - }) : itemPositionsNotifier = itemPositionsListener as ItemPositionsNotifier?, - separatorBuilder = null; - - /// Create a [ScrollablePositionedList] whose items are provided by - /// [itemBuilder] and separators provided by [separatorBuilder]. - const ScrollablePositionedList.separated({ - required this.itemCount, - required this.itemBuilder, - required IndexedWidgetBuilder this.separatorBuilder, - super.key, - this.shrinkWrap = false, - this.itemScrollController, - ItemPositionsListener? itemPositionsListener, - this.initialScrollIndex = 0, - this.initialAlignment = 0, - this.scrollDirection = Axis.vertical, - this.reverse = false, - this.physics, - this.semanticChildCount, - this.padding, - this.addSemanticIndexes = true, - this.addAutomaticKeepAlives = true, - this.addRepaintBoundaries = true, - this.minCacheExtent, - this.findChildIndexCallback, - this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, - }) : itemPositionsNotifier = itemPositionsListener as ItemPositionsNotifier?; - - /// Number of items the [itemBuilder] can produce. - final int itemCount; - - /// Called to build children for the list with - /// 0 <= index < itemCount. - final IndexedWidgetBuilder itemBuilder; - - /// Called to build separators for between each item in the list. - /// Called with 0 <= index < itemCount - 1. - final IndexedWidgetBuilder? separatorBuilder; - - /// Controller for jumping or scrolling to an item. - final ItemScrollController? itemScrollController; - - /// Notifier that reports the items laid out in the list after each frame. - final ItemPositionsNotifier? itemPositionsNotifier; - - /// Index of an item to initially align within the viewport. - final int initialScrollIndex; - - /// Determines where the leading edge of the item at [initialScrollIndex] - /// should be placed. - /// - /// See [ItemScrollController.jumpTo] for an explanation of alignment. - final double initialAlignment; - - /// The axis along which the scroll view scrolls. - /// - /// Defaults to [Axis.vertical]. - final Axis scrollDirection; - - /// Whether the view scrolls in the reading direction. - /// - /// Defaults to false. - /// - /// See [ScrollView.reverse]. - final bool reverse; - - /// {@template flutter.widgets.scroll_view.shrinkWrap} - /// Whether the extent of the scroll view in the [scrollDirection] should be - /// determined by the contents being viewed. - /// - /// Defaults to false. - /// - /// See [ScrollView.shrinkWrap]. - /// {@endtemplate} - final bool shrinkWrap; - - /// How the scroll view should respond to user input. - /// - /// For example, determines how the scroll view continues to animate after the - /// user stops dragging the scroll view. - /// - /// See [ScrollView.physics]. - final ScrollPhysics? physics; - - /// The number of children that will contribute semantic information. - /// - /// See [ScrollView.semanticChildCount] for more information. - final int? semanticChildCount; - - /// The amount of space by which to inset the children. - final EdgeInsets? padding; - - /// Whether to wrap each child in an [IndexedSemantics]. - /// - /// See [SliverChildBuilderDelegate.addSemanticIndexes]. - final bool addSemanticIndexes; - - /// Whether to wrap each child in an [AutomaticKeepAlive]. - /// - /// See [SliverChildBuilderDelegate.addAutomaticKeepAlives]. - final bool addAutomaticKeepAlives; - - /// Whether to wrap each child in a [RepaintBoundary]. - /// - /// See [SliverChildBuilderDelegate.addRepaintBoundaries]. - final bool addRepaintBoundaries; - - /// The minimum cache extent used by the underlying scroll lists. - /// See [ScrollView.cacheExtent]. - /// - /// Note that the [ScrollablePositionedList] uses two lists to simulate long - /// scrolls, so using the [ScrollController.scrollTo] method may result - /// in builds of widgets that would otherwise already be built in the - /// cache extent. - final double? minCacheExtent; - - /// Called to find the new index of a child based on its key in case of reordering. - /// - /// If not provided, a child widget may not map to its existing [RenderObject] - /// when the order of children returned from the children builder changes. - /// This may result in state-loss. - /// - /// This callback should take an input [Key], and it should return the - /// index of the child element with that associated key, or null if not found. - /// - /// See [SliverChildBuilderDelegate.findChildIndexCallback]. - final ChildIndexGetter? findChildIndexCallback; - - /// Defines how this [ScrollView] will dismiss the keyboard automatically. - /// - /// See [ScrollView.keyboardDismissBehavior]. - final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior; - - @override - State createState() => _ScrollablePositionedListState(); -} - -/// Controller to jump or scroll to a particular position in a -/// [ScrollablePositionedList]. -class ItemScrollController { - /// Whether any ScrollablePositionedList objects are attached this object. - /// - /// If `false`, then [jumpTo] and [scrollTo] must not be called. - bool get isAttached => _scrollableListState != null; - - _ScrollablePositionedListState? _scrollableListState; - - /// Immediately, without animation, reconfigure the list so that the item at - /// [index]'s leading edge is at the given [alignment]. - /// - /// The [alignment] specifies the desired position for the leading edge of the - /// item. The [alignment] is expected to be a value in the range \[0.0, 1.0\] - /// and represents a proportion along the main axis of the viewport. - /// - /// For a vertically scrolling view that is not reversed: - /// * 0 aligns the top edge of the item with the top edge of the view. - /// * 1 aligns the top edge of the item with the bottom of the view. - /// * 0.5 aligns the top edge of the item with the center of the view. - /// - /// For a horizontally scrolling view that is not reversed: - /// * 0 aligns the left edge of the item with the left edge of the view - /// * 1 aligns the left edge of the item with the right edge of the view. - /// * 0.5 aligns the left edge of the item with the center of the view. - void jumpTo({required int index, double alignment = 0}) { - _scrollableListState!._jumpTo(index: index, alignment: alignment); - } - - /// Animate the list over [duration] using the given [curve] such that the - /// item at [index] ends up with its leading edge at the given [alignment]. - /// See [jumpTo] for an explanation of alignment. - /// - /// The [duration] must be greater than 0; otherwise, use [jumpTo]. - /// - /// When item position is not available, because it's too far, the scroll - /// is composed into three phases: - /// - /// 1. The currently displayed list view starts scrolling. - /// 2. Another list view, which scrolls with the same speed, fades over the - /// first one and shows items that are close to the scroll target. - /// 3. The second list view scrolls and stops on the target. - /// - /// The [opacityAnimationWeights] can be used to apply custom weights to these - /// three stages of this animation. The default weights, `[40, 20, 40]`, are - /// good with default [Curves.linear]. Different weights might be better for - /// other cases. For example, if you use [Curves.easeOut], consider setting - /// [opacityAnimationWeights] to `[20, 20, 60]`. - /// - /// See [TweenSequenceItem.weight] for more info. - Future scrollTo({ - required int index, - double alignment = 0, - required Duration duration, - Curve curve = Curves.linear, - List opacityAnimationWeights = const [40, 20, 40], - }) { - assert( - _scrollableListState != null, - '''ScrollController must be attached to a ScrollablePositionedList to scroll.''', - ); - assert( - opacityAnimationWeights.length == 3, - 'opacityAnimationWeights must have exactly three elements.', - ); - assert(duration > Duration.zero, 'Duration must be greater than zero.'); - return _scrollableListState!._scrollTo( - index: index, - alignment: alignment, - duration: duration, - curve: curve, - opacityAnimationWeights: opacityAnimationWeights, - ); - } - - void _attach(_ScrollablePositionedListState scrollableListState) { - assert( - _scrollableListState == null, - '''ScrollController must not be attached to multiple ScrollablePositionedLists.''', - ); - _scrollableListState = scrollableListState; - } - - void _detach() { - _scrollableListState = null; - } -} - -class _ScrollablePositionedListState extends State - with TickerProviderStateMixin { - /// Details for the primary (active) [ListView]. - _ListDisplayDetails primary = _ListDisplayDetails(const ValueKey('Ping')); - - /// Details for the secondary (transitional) [ListView] that is temporarily - /// shown when scrolling a long distance. - _ListDisplayDetails secondary = _ListDisplayDetails(const ValueKey('Pong')); - - final opacity = ProxyAnimation(const AlwaysStoppedAnimation(0)); - - void Function() startAnimationCallback = () {}; - - bool _isTransitioning = false; - - AnimationController? _animationController; - - @override - void initState() { - super.initState(); - final initialPosition = PageStorage.of(context).readState(context); - primary - ..target = initialPosition?.index ?? widget.initialScrollIndex - ..alignment = initialPosition?.itemLeadingEdge ?? widget.initialAlignment; - if (widget.itemCount > 0 && primary.target > widget.itemCount - 1) { - primary.target = widget.itemCount - 1; - } - widget.itemScrollController?._attach(this); - primary.itemPositionsNotifier.itemPositions.addListener(_updatePositions); - secondary.itemPositionsNotifier.itemPositions.addListener(_updatePositions); - } - - @override - void deactivate() { - widget.itemScrollController?._detach(); - super.deactivate(); - } - - @override - void dispose() { - primary.itemPositionsNotifier.itemPositions - .removeListener(_updatePositions); - secondary.itemPositionsNotifier.itemPositions - .removeListener(_updatePositions); - _animationController?.dispose(); - super.dispose(); - } - - @override - void didUpdateWidget(ScrollablePositionedList oldWidget) { - super.didUpdateWidget(oldWidget); - if (oldWidget.itemScrollController?._scrollableListState == this) { - oldWidget.itemScrollController?._detach(); - } - if (widget.itemScrollController?._scrollableListState != this) { - widget.itemScrollController?._detach(); - widget.itemScrollController?._attach(this); - } - - if (widget.itemCount == 0) { - primary.target = 0; - secondary.target = 0; - } else { - if (primary.target > widget.itemCount - 1) { - primary.target = widget.itemCount - 1; - } - if (secondary.target > widget.itemCount - 1) { - secondary.target = widget.itemCount - 1; - } - } - } - - @override - Widget build(BuildContext context) { - return LayoutBuilder( - builder: (context, constraints) { - final cacheExtent = _cacheExtent(constraints); - return GestureDetector( - onPanDown: (_) => _stopScroll(canceled: true), - excludeFromSemantics: true, - child: Stack( - children: [ - PostMountCallback( - key: primary.key, - callback: startAnimationCallback, - child: FadeTransition( - opacity: ReverseAnimation(opacity), - child: NotificationListener( - onNotification: (_) => _isTransitioning, - child: PositionedList( - itemBuilder: widget.itemBuilder, - separatorBuilder: widget.separatorBuilder, - itemCount: widget.itemCount, - positionedIndex: primary.target, - controller: primary.scrollController, - itemPositionsNotifier: primary.itemPositionsNotifier, - scrollDirection: widget.scrollDirection, - reverse: widget.reverse, - cacheExtent: cacheExtent, - alignment: primary.alignment, - physics: widget.physics, - shrinkWrap: widget.shrinkWrap, - addSemanticIndexes: widget.addSemanticIndexes, - semanticChildCount: widget.semanticChildCount, - padding: widget.padding, - addAutomaticKeepAlives: widget.addAutomaticKeepAlives, - addRepaintBoundaries: widget.addRepaintBoundaries, - findChildIndexCallback: widget.findChildIndexCallback, - keyboardDismissBehavior: widget.keyboardDismissBehavior, - ), - ), - ), - ), - if (_isTransitioning) - PostMountCallback( - key: secondary.key, - callback: startAnimationCallback, - child: FadeTransition( - opacity: opacity, - child: NotificationListener( - onNotification: (_) => false, - child: PositionedList( - itemBuilder: widget.itemBuilder, - separatorBuilder: widget.separatorBuilder, - itemCount: widget.itemCount, - itemPositionsNotifier: secondary.itemPositionsNotifier, - positionedIndex: secondary.target, - controller: secondary.scrollController, - scrollDirection: widget.scrollDirection, - reverse: widget.reverse, - cacheExtent: cacheExtent, - alignment: secondary.alignment, - physics: widget.physics, - shrinkWrap: widget.shrinkWrap, - addSemanticIndexes: widget.addSemanticIndexes, - semanticChildCount: widget.semanticChildCount, - padding: widget.padding, - addAutomaticKeepAlives: widget.addAutomaticKeepAlives, - addRepaintBoundaries: widget.addRepaintBoundaries, - findChildIndexCallback: widget.findChildIndexCallback, - keyboardDismissBehavior: widget.keyboardDismissBehavior, - ), - ), - ), - ), - ], - ), - ); - }, - ); - } - - double _cacheExtent(BoxConstraints constraints) => max( - (widget.scrollDirection == Axis.vertical - ? constraints.maxHeight - : constraints.maxWidth) * - _screenScrollCount, - widget.minCacheExtent ?? 0, - ); - - void _jumpTo({required int index, required double alignment}) { - _stopScroll(canceled: true); - if (index > widget.itemCount - 1) { - index = widget.itemCount - 1; - } - setState(() { - primary.scrollController.jumpTo(0); - primary - ..target = index - ..alignment = alignment; - }); - } - - Future _scrollTo({ - required int index, - required double alignment, - required Duration duration, - Curve curve = Curves.linear, - required List opacityAnimationWeights, - }) async { - if (index > widget.itemCount - 1) { - index = widget.itemCount - 1; - } - if (_isTransitioning) { - final scrollCompleter = Completer(); - _stopScroll(canceled: true); - SchedulerBinding.instance.addPostFrameCallback((_) async { - await _startScroll( - index: index, - alignment: alignment, - duration: duration, - curve: curve, - opacityAnimationWeights: opacityAnimationWeights, - ); - scrollCompleter.complete(); - }); - await scrollCompleter.future; - } else { - await _startScroll( - index: index, - alignment: alignment, - duration: duration, - curve: curve, - opacityAnimationWeights: opacityAnimationWeights, - ); - } - } - - Future _startScroll({ - required int index, - required double alignment, - required Duration duration, - Curve curve = Curves.linear, - required List opacityAnimationWeights, - }) async { - final direction = index > primary.target ? 1 : -1; - final itemPosition = - primary.itemPositionsNotifier.itemPositions.value.firstWhereOrNull( - (ItemPosition itemPosition) => itemPosition.index == index, - ); - if (itemPosition != null) { - // Scroll directly. - final localScrollAmount = itemPosition.itemLeadingEdge * - primary.scrollController.position.viewportDimension; - await primary.scrollController.animateTo( - primary.scrollController.offset + - localScrollAmount - - alignment * primary.scrollController.position.viewportDimension, - duration: duration, - curve: curve, - ); - } else { - final scrollAmount = _screenScrollCount * - primary.scrollController.position.viewportDimension; - final startCompleter = Completer(); - final endCompleter = Completer(); - startAnimationCallback = () { - SchedulerBinding.instance.addPostFrameCallback((_) { - startAnimationCallback = () {}; - _animationController?.dispose(); - _animationController = - AnimationController(vsync: this, duration: duration)..forward(); - opacity.parent = _opacityAnimation(opacityAnimationWeights) - .animate(_animationController!); - secondary.scrollController.jumpTo(-direction * - (_screenScrollCount * - primary.scrollController.position.viewportDimension - - alignment * - secondary.scrollController.position.viewportDimension)); - - startCompleter.complete(primary.scrollController.animateTo( - primary.scrollController.offset + direction * scrollAmount, - duration: duration, - curve: curve, - )); - endCompleter.complete(secondary.scrollController - .animateTo(0, duration: duration, curve: curve)); - }); - }; - setState(() { - // TODO: _startScroll can be re-entrant, which invalidates this assert. - // assert(!_isTransitioning); - secondary - ..target = index - ..alignment = alignment; - _isTransitioning = true; - }); - await Future.wait([startCompleter.future, endCompleter.future]); - _stopScroll(); - } - } - - void _stopScroll({bool canceled = false}) { - if (!_isTransitioning) { - return; - } - - if (canceled) { - if (primary.scrollController.hasClients) { - primary.scrollController.jumpTo(primary.scrollController.offset); - } - if (secondary.scrollController.hasClients) { - secondary.scrollController.jumpTo(secondary.scrollController.offset); - } - } - - if (mounted) { - setState(() { - if (opacity.value >= 0.5) { - // Secondary [ListView] is more visible than the primary; make it the - // new primary. - final temp = primary; - primary = secondary; - secondary = temp; - } - _isTransitioning = false; - opacity.parent = const AlwaysStoppedAnimation(0); - }); - } - } - - Animatable _opacityAnimation(List opacityAnimationWeights) { - const startOpacity = 0.0; - const endOpacity = 1.0; - return TweenSequence(>[ - TweenSequenceItem( - tween: ConstantTween(startOpacity), - weight: opacityAnimationWeights[0], - ), - TweenSequenceItem( - tween: Tween(begin: startOpacity, end: endOpacity), - weight: opacityAnimationWeights[1], - ), - TweenSequenceItem( - tween: ConstantTween(endOpacity), - weight: opacityAnimationWeights[2], - ), - ]); - } - - void _updatePositions() { - final itemPositions = primary.itemPositionsNotifier.itemPositions.value - .where((ItemPosition position) => - position.itemLeadingEdge < 1 && position.itemTrailingEdge > 0); - if (itemPositions.isNotEmpty) { - PageStorage.of(context).writeState( - context, - itemPositions.reduce((value, element) => - value.itemLeadingEdge < element.itemLeadingEdge ? value : element), - ); - } - widget.itemPositionsNotifier?.itemPositions.value = itemPositions; - } -} - -class _ListDisplayDetails { - _ListDisplayDetails(this.key); - - final itemPositionsNotifier = ItemPositionsNotifier(); - final scrollController = ScrollController(keepScrollOffset: false); - - /// The index of the item to scroll to. - int target = 0; - - /// The desired alignment for [target]. - /// - /// See [ItemScrollController.jumpTo] for an explanation of alignment. - double alignment = 0; - - final Key key; -} diff --git a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/viewport.dart b/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/viewport.dart deleted file mode 100644 index aac9acc9e0..0000000000 --- a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/viewport.dart +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:math' as math; - -import 'package:flutter/rendering.dart'; -import 'package:flutter/widgets.dart'; - -/// {@template unbounded_viewport} -/// A render object that is bigger on the inside. -/// -/// Version of [Viewport] with some modifications to how extents are -/// computed to allow scroll extents outside 0 to 1. See [Viewport] -/// for more information. -/// {@endtemplate} -class UnboundedViewport extends Viewport { - /// {@macro unbounded_viewport} - UnboundedViewport({ - super.key, - super.axisDirection, - super.crossAxisDirection, - double anchor = 0.0, - required super.offset, - super.center, - super.cacheExtent, - super.slivers, - }) : _anchor = anchor; - - // [Viewport] enforces constraints on [Viewport.anchor], so we need our own - // version. - final double _anchor; - - @override - double get anchor => _anchor; - - @override - RenderViewport createRenderObject(BuildContext context) { - return UnboundedRenderViewport( - axisDirection: axisDirection, - crossAxisDirection: crossAxisDirection ?? - Viewport.getDefaultCrossAxisDirection(context, axisDirection), - anchor: anchor, - offset: offset, - cacheExtent: cacheExtent, - ); - } -} - -/// A render object that is bigger on the inside. -/// -/// Version of [RenderViewport] with some modifications to how extents are -/// computed to allow scroll extents outside 0 to 1. See [RenderViewport] -/// for more information. -/// -// Differences from [RenderViewport] are marked with a //***** Differences -// comment. -class UnboundedRenderViewport extends RenderViewport { - /// Creates a viewport for [RenderSliver] objects. - UnboundedRenderViewport({ - super.axisDirection, - required super.crossAxisDirection, - required super.offset, - double anchor = 0.0, - super.children, - super.center, - super.cacheExtent, - }) : _anchor = anchor; - - static const int _maxLayoutCycles = 10; - - double _anchor; - - // Out-of-band data computed during layout. - late double _minScrollExtent; - late double _maxScrollExtent; - bool _hasVisualOverflow = false; - - /// This value is set during layout based on the [CacheExtentStyle]. - /// - /// When the style is [CacheExtentStyle.viewport], it is the main axis extent - /// of the viewport multiplied by the requested cache extent, which is still - /// expressed in pixels. - double? _calculatedCacheExtent; - - @override - double get anchor => _anchor; - - @override - set anchor(double value) { - if (value == _anchor) return; - _anchor = value; - markNeedsLayout(); - } - - @override - void performResize() { - super.performResize(); - // TODO: Figure out why this override is needed as a result of - // https://github.com/flutter/flutter/pull/61973 and see if it can be - // removed somehow. - switch (axis) { - case Axis.vertical: - offset.applyViewportDimension(size.height); - break; - case Axis.horizontal: - offset.applyViewportDimension(size.width); - break; - } - } - - @override - Rect describeSemanticsClip(RenderSliver? child) { - if (_calculatedCacheExtent == null) { - return semanticBounds; - } - - switch (axis) { - case Axis.vertical: - return Rect.fromLTRB( - semanticBounds.left, - semanticBounds.top - _calculatedCacheExtent!, - semanticBounds.right, - semanticBounds.bottom + _calculatedCacheExtent!, - ); - default: - return Rect.fromLTRB( - semanticBounds.left - _calculatedCacheExtent!, - semanticBounds.top, - semanticBounds.right + _calculatedCacheExtent!, - semanticBounds.bottom, - ); - } - } - - @override - void performLayout() { - if (center == null) { - assert( - firstChild == null, - 'A RenderViewport with no center render object must have no children.', - ); - _minScrollExtent = 0.0; - _maxScrollExtent = 0.0; - _hasVisualOverflow = false; - offset.applyContentDimensions(0, 0); - return; - } - assert( - center!.parent == this, - '''The "center" property of a RenderViewport must be a child of the viewport.''', - ); - - late double mainAxisExtent; - late double crossAxisExtent; - switch (axis) { - case Axis.vertical: - mainAxisExtent = size.height; - crossAxisExtent = size.width; - break; - case Axis.horizontal: - mainAxisExtent = size.width; - crossAxisExtent = size.height; - break; - } - - final centerOffsetAdjustment = center!.centerOffsetAdjustment; - - double correction; - var count = 0; - do { - correction = _attemptLayout( - mainAxisExtent, - crossAxisExtent, - offset.pixels + centerOffsetAdjustment, - ); - if (correction != 0.0) { - offset.correctBy(correction); - } else { - // *** Difference from [RenderViewport]. - final top = _minScrollExtent + mainAxisExtent * anchor; - final bottom = _maxScrollExtent - mainAxisExtent * (1.0 - anchor); - final maxScrollOffset = math.max(math.min(0, top), bottom); - final minScrollOffset = math.min(top, maxScrollOffset); - if (offset.applyContentDimensions(minScrollOffset, maxScrollOffset)) { - break; - } - // *** End of difference from [RenderViewport]. - } - count += 1; - } while (count < _maxLayoutCycles); - assert(() { - if (count >= _maxLayoutCycles) { - assert(count != 1); - throw FlutterError( - 'A RenderViewport exceeded its maximum number of layout cycles.\n' - 'RenderViewport render objects, during layout, can retry if either their ' - 'slivers or their ViewportOffset decide that the offset should be corrected ' - 'to take into account information collected during that layout.\n' - 'In the case of this RenderViewport object, however, this happened $count ' - 'times and still there was no consensus on the scroll offset. This usually ' - 'indicates a bug. Specifically, it means that one of the following three ' - 'problems is being experienced by the RenderViewport object:\n' - ' * One of the RenderSliver children or the ViewportOffset have a bug such' - ' that they always think that they need to correct the offset regardless.\n' - ' * Some combination of the RenderSliver children and the ViewportOffset' - ' have a bad interaction such that one applies a correction then another' - ' applies a reverse correction, leading to an infinite loop of corrections.\n' - ' * There is a pathological case that would eventually resolve, but it is' - ' so complicated that it cannot be resolved in any reasonable number of' - ' layout passes.', - ); - } - return true; - }()); - } - - double _attemptLayout( - double mainAxisExtent, - double crossAxisExtent, - double correctedOffset, - ) { - assert(!mainAxisExtent.isNaN, 'The main axis extent cannot be NaN.'); - assert(mainAxisExtent >= 0.0, 'The main axis extent cannot be negative.'); - assert(crossAxisExtent.isFinite, 'The cross axis extent must be finite.'); - assert(crossAxisExtent >= 0.0, 'The cross axis extent cannot be negative.'); - assert(correctedOffset.isFinite, 'The corrected offset must be finite.'); - _minScrollExtent = 0.0; - _maxScrollExtent = 0.0; - _hasVisualOverflow = false; - - // centerOffset is the offset from the leading edge of the RenderViewport - // to the zero scroll offset (the line between the forward slivers and the - // reverse slivers). - final centerOffset = mainAxisExtent * anchor - correctedOffset; - final reverseDirectionRemainingPaintExtent = - centerOffset.clamp(0.0, mainAxisExtent); - final forwardDirectionRemainingPaintExtent = - (mainAxisExtent - centerOffset).clamp(0.0, mainAxisExtent); - - switch (cacheExtentStyle) { - case CacheExtentStyle.pixel: - _calculatedCacheExtent = cacheExtent; - break; - case CacheExtentStyle.viewport: - _calculatedCacheExtent = mainAxisExtent * cacheExtent!; - break; - } - - final fullCacheExtent = mainAxisExtent + 2 * _calculatedCacheExtent!; - final centerCacheOffset = centerOffset + _calculatedCacheExtent!; - final reverseDirectionRemainingCacheExtent = - centerCacheOffset.clamp(0.0, fullCacheExtent); - final forwardDirectionRemainingCacheExtent = - (fullCacheExtent - centerCacheOffset).clamp(0.0, fullCacheExtent); - - final leadingNegativeChild = childBefore(center!); - - if (leadingNegativeChild != null) { - // negative scroll offsets - final result = layoutChildSequence( - child: leadingNegativeChild, - scrollOffset: math.max(mainAxisExtent, centerOffset) - mainAxisExtent, - overlap: 0, - layoutOffset: forwardDirectionRemainingPaintExtent, - remainingPaintExtent: reverseDirectionRemainingPaintExtent, - mainAxisExtent: mainAxisExtent, - crossAxisExtent: crossAxisExtent, - growthDirection: GrowthDirection.reverse, - advance: childBefore, - remainingCacheExtent: reverseDirectionRemainingCacheExtent, - cacheOrigin: (mainAxisExtent - centerOffset) - .clamp(-_calculatedCacheExtent!, 0.0), - ); - if (result != 0.0) return -result; - } - - // positive scroll offsets - return layoutChildSequence( - child: center, - scrollOffset: math.max(0, -centerOffset), - overlap: leadingNegativeChild == null ? math.min(0, -centerOffset) : 0.0, - layoutOffset: centerOffset >= mainAxisExtent - ? centerOffset - : reverseDirectionRemainingPaintExtent, - remainingPaintExtent: forwardDirectionRemainingPaintExtent, - mainAxisExtent: mainAxisExtent, - crossAxisExtent: crossAxisExtent, - growthDirection: GrowthDirection.forward, - advance: childAfter, - remainingCacheExtent: forwardDirectionRemainingCacheExtent, - cacheOrigin: centerOffset.clamp(-_calculatedCacheExtent!, 0.0), - ); - } - - @override - bool get hasVisualOverflow => _hasVisualOverflow; - - @override - void updateOutOfBandData( - GrowthDirection growthDirection, - SliverGeometry childLayoutGeometry, - ) { - switch (growthDirection) { - case GrowthDirection.forward: - _maxScrollExtent += childLayoutGeometry.scrollExtent; - break; - case GrowthDirection.reverse: - _minScrollExtent -= childLayoutGeometry.scrollExtent; - break; - } - if (childLayoutGeometry.hasVisualOverflow) _hasVisualOverflow = true; - } -} diff --git a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/wrapping.dart b/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/wrapping.dart deleted file mode 100644 index c3a8129ce7..0000000000 --- a/packages/stream_chat_flutter/lib/scrollable_positioned_list/src/wrapping.dart +++ /dev/null @@ -1,1066 +0,0 @@ -import 'dart:math' as math; - -import 'package:flutter/rendering.dart'; -import 'package:flutter/widgets.dart'; - -/// A widget that is bigger on the inside and shrink wraps its children in the -/// main axis. -/// -/// [ShrinkWrappingViewport] displays a subset of its children according to its -/// own dimensions and the given [offset]. As the offset varies, different -/// children are visible through the viewport. -/// -/// [ShrinkWrappingViewport] differs from [Viewport] in that [Viewport] expands -/// to fill the main axis whereas [ShrinkWrappingViewport] sizes itself to match -/// its children in the main axis. This shrink wrapping behavior is expensive -/// because the children, and hence the viewport, could potentially change size -/// whenever the [offset] changes (e.g., because of a collapsing header). -/// -/// [ShrinkWrappingViewport] cannot contain box children directly. Instead, use -/// a [SliverList], [SliverFixedExtentList], [SliverGrid], or a -/// [SliverToBoxAdapter], for example. -/// -/// See also: -/// -/// * [ListView], [PageView], [GridView], and [CustomScrollView], which combine -/// [Scrollable] and [ShrinkWrappingViewport] into widgets that are easier to -/// use. -/// * [SliverToBoxAdapter], which allows a box widget to be placed inside a -/// sliver context (the opposite of this widget). -/// * [Viewport], a viewport that does not shrink-wrap its contents. -class CustomShrinkWrappingViewport extends CustomViewport { - /// Creates a widget that is bigger on the inside and shrink wraps its - /// children in the main axis. - /// - /// The viewport listens to the [offset], which means you do not need to - /// rebuild this widget when the [offset] changes. - /// - /// The [offset] argument must not be null. - CustomShrinkWrappingViewport({ - super.key, - super.axisDirection, - super.crossAxisDirection, - double anchor = 0.0, - required super.offset, - List? children, - super.center, - super.cacheExtent, - super.slivers, - }) : _anchor = anchor; - - // [Viewport] enforces constraints on [Viewport.anchor], so we need our own - // version. - final double _anchor; - - @override - double get anchor => _anchor; - - @override - CustomRenderShrinkWrappingViewport createRenderObject(BuildContext context) { - return CustomRenderShrinkWrappingViewport( - axisDirection: axisDirection, - crossAxisDirection: crossAxisDirection ?? - Viewport.getDefaultCrossAxisDirection(context, axisDirection), - offset: offset, - anchor: anchor, - cacheExtent: cacheExtent, - ); - } - - @override - void updateRenderObject( - BuildContext context, - CustomRenderShrinkWrappingViewport renderObject, - ) { - renderObject - ..axisDirection = axisDirection - ..crossAxisDirection = crossAxisDirection ?? - Viewport.getDefaultCrossAxisDirection(context, axisDirection) - ..anchor = anchor - ..offset = offset - ..cacheExtent = cacheExtent - ..cacheExtentStyle = cacheExtentStyle - ..clipBehavior = clipBehavior; - } -} - -/// A render object that is bigger on the inside and shrink wraps its children -/// in the main axis. -/// -/// [RenderShrinkWrappingViewport] displays a subset of its children according -/// to its own dimensions and the given [offset]. As the offset varies, different -/// children are visible through the viewport. -/// -/// [RenderShrinkWrappingViewport] differs from [RenderViewport] in that -/// [RenderViewport] expands to fill the main axis whereas -/// [RenderShrinkWrappingViewport] sizes itself to match its children in the -/// main axis. This shrink wrapping behavior is expensive because the children, -/// and hence the viewport, could potentially change size whenever the [offset] -/// changes (e.g., because of a collapsing header). -/// -/// [RenderShrinkWrappingViewport] cannot contain [RenderBox] children directly. -/// Instead, use a [RenderSliverList], [RenderSliverFixedExtentList], -/// [RenderSliverGrid], or a [RenderSliverToBoxAdapter], for example. -/// -/// See also: -/// -/// * [RenderViewport], a viewport that does not shrink-wrap its contents. -/// * [RenderSliver], which explains more about the Sliver protocol. -/// * [RenderBox], which explains more about the Box protocol. -/// * [RenderSliverToBoxAdapter], which allows a [RenderBox] object to be -/// placed inside a [RenderSliver] (the opposite of this class). -class CustomRenderShrinkWrappingViewport extends CustomRenderViewport { - /// Creates a viewport (for [RenderSliver] objects) that shrink-wraps its - /// contents. - /// - /// The [offset] must be specified. For testing purposes, consider passing a - /// [ViewportOffset.zero] or [ViewportOffset.fixed]. - CustomRenderShrinkWrappingViewport({ - super.axisDirection, - required super.crossAxisDirection, - required super.offset, - double anchor = 0.0, - super.children, - super.center, - super.cacheExtent, - }) : _anchor = anchor; - - double _anchor; - - @override - double get anchor => _anchor; - - @override - bool get sizedByParent => false; - - double lastMainAxisExtent = -1; - - @override - set anchor(double value) { - if (value == _anchor) return; - _anchor = value; - markNeedsLayout(); - } - - late double _shrinkWrapExtent; - - /// This value is set during layout based on the [CacheExtentStyle]. - /// - /// When the style is [CacheExtentStyle.viewport], it is the main axis extent - /// of the viewport multiplied by the requested cache extent, which is still - /// expressed in pixels. - double? _calculatedCacheExtent; - - /// While List in a wrapping container, eg. ListView,the mainAxisExtent will - /// be infinite. This time need to change mainAxisExtent to this value. - final double _maxMainAxisExtent = double.maxFinite; - - @override - void performLayout() { - if (center == null) { - assert(firstChild == null, 'center must be null if children are present'); - _minScrollExtent = 0.0; - _maxScrollExtent = 0.0; - _hasVisualOverflow = false; - offset.applyContentDimensions(0, 0); - return; - } - - assert(center!.parent == this, 'center must be a child of the viewport'); - - final constraints = this.constraints; - if (firstChild == null) { - switch (axis) { - case Axis.vertical: - assert( - constraints.hasBoundedWidth, - 'Vertical viewport was given ' - 'unbounded width.\n' - 'Viewports expand in the cross axis to fill their container and ' - 'constrain their children to match their extent in the cross axis. ' - 'In this case, a vertical viewport was given an unlimited amount ' - 'of horizontal space in which to expand.', - ); - size = Size(constraints.maxWidth, constraints.minHeight); - break; - case Axis.horizontal: - assert( - constraints.hasBoundedHeight, - 'Horizontal viewport was given ' - 'unbounded height.\n' - 'Viewports expand in the cross axis to fill their container and ' - 'constrain their children to match their extent in the cross axis. ' - 'In this case, a horizontal viewport was given an unlimited amount ' - 'of vertical space in which to expand.', - ); - size = Size(constraints.minWidth, constraints.maxHeight); - break; - } - offset.applyViewportDimension(0); - _maxScrollExtent = 0.0; - _shrinkWrapExtent = 0.0; - _hasVisualOverflow = false; - offset.applyContentDimensions(0, 0); - return; - } - - double mainAxisExtent; - final double crossAxisExtent; - switch (axis) { - case Axis.vertical: - assert( - constraints.hasBoundedWidth, - 'Vertical viewport was given ' - 'unbounded width.\n' - 'Viewports expand in the cross axis to fill their container and ' - 'constrain their children to match their extent in the cross axis. ' - 'In this case, a vertical viewport was given an unlimited amount ' - 'of horizontal space in which to expand.', - ); - mainAxisExtent = constraints.maxHeight; - crossAxisExtent = constraints.maxWidth; - break; - case Axis.horizontal: - assert( - constraints.hasBoundedHeight, - 'Horizontal viewport was given ' - 'unbounded height.\n' - 'Viewports expand in the cross axis to fill their container and ' - 'constrain their children to match their extent in the cross axis. ' - 'In this case, a horizontal viewport was given an unlimited amount ' - 'of vertical space in which to expand.', - ); - mainAxisExtent = constraints.maxWidth; - crossAxisExtent = constraints.maxHeight; - break; - } - - if (mainAxisExtent.isInfinite) { - mainAxisExtent = _maxMainAxisExtent; - } - - final centerOffsetAdjustment = center!.centerOffsetAdjustment; - - double correction; - double effectiveExtent; - do { - correction = _attemptLayout( - mainAxisExtent, - crossAxisExtent, - offset.pixels + centerOffsetAdjustment, - ); - if (correction != 0.0) { - offset.correctBy(correction); - } else { - switch (axis) { - case Axis.vertical: - effectiveExtent = constraints.constrainHeight(_shrinkWrapExtent); - break; - case Axis.horizontal: - effectiveExtent = constraints.constrainWidth(_shrinkWrapExtent); - break; - } - // *** Difference from [RenderViewport]. - final top = _minScrollExtent + mainAxisExtent * anchor; - final bottom = _maxScrollExtent - mainAxisExtent * (1.0 - anchor); - - final maxScrollOffset = math.max(math.min(0, top), bottom); - final minScrollOffset = math.min(top, maxScrollOffset); - - final didAcceptViewportDimension = - offset.applyViewportDimension(effectiveExtent); - final didAcceptContentDimension = - offset.applyContentDimensions(minScrollOffset, maxScrollOffset); - if (didAcceptViewportDimension && didAcceptContentDimension) { - break; - } - } - } while (true); - switch (axis) { - case Axis.vertical: - size = - constraints.constrainDimensions(crossAxisExtent, effectiveExtent); - break; - case Axis.horizontal: - size = - constraints.constrainDimensions(effectiveExtent, crossAxisExtent); - break; - } - } - - double _attemptLayout( - double mainAxisExtent, - double crossAxisExtent, - double correctedOffset, - ) { - assert(!mainAxisExtent.isNaN, 'The maxExtent of $this has not been set.'); - assert(mainAxisExtent >= 0.0, 'The maxExtent of $this is negative.'); - assert( - crossAxisExtent.isFinite, - 'The crossAxisExtent of $this is not finite.', - ); - assert(crossAxisExtent >= 0.0, 'The crossAxisExtent of $this is negative.'); - assert( - correctedOffset.isFinite, - 'The correctedOffset of $this is not finite.', - ); - _minScrollExtent = 0.0; - _maxScrollExtent = 0.0; - _hasVisualOverflow = false; - _shrinkWrapExtent = 0.0; - - // centerOffset is the offset from the leading edge of the RenderViewport - // to the zero scroll offset (the line between the forward slivers and the - // reverse slivers). - final centerOffset = mainAxisExtent * anchor - correctedOffset; - final reverseDirectionRemainingPaintExtent = - centerOffset.clamp(0.0, mainAxisExtent); - final forwardDirectionRemainingPaintExtent = - (mainAxisExtent - centerOffset).clamp(0.0, mainAxisExtent); - - switch (cacheExtentStyle) { - case CacheExtentStyle.pixel: - _calculatedCacheExtent = cacheExtent; - break; - case CacheExtentStyle.viewport: - _calculatedCacheExtent = mainAxisExtent * cacheExtent!; - break; - } - - final fullCacheExtent = mainAxisExtent + 2 * _calculatedCacheExtent!; - final centerCacheOffset = centerOffset + _calculatedCacheExtent!; - final reverseDirectionRemainingCacheExtent = - centerCacheOffset.clamp(0.0, fullCacheExtent); - final forwardDirectionRemainingCacheExtent = - (fullCacheExtent - centerCacheOffset).clamp(0.0, fullCacheExtent); - - final leadingNegativeChild = childBefore(center!); - - if (leadingNegativeChild != null) { - // negative scroll offsets - final result = layoutChildSequence( - child: leadingNegativeChild, - scrollOffset: math.max(mainAxisExtent, centerOffset) - mainAxisExtent, - overlap: 0, - layoutOffset: forwardDirectionRemainingPaintExtent, - remainingPaintExtent: reverseDirectionRemainingPaintExtent, - mainAxisExtent: mainAxisExtent, - crossAxisExtent: crossAxisExtent, - growthDirection: GrowthDirection.reverse, - advance: childBefore, - remainingCacheExtent: reverseDirectionRemainingCacheExtent, - cacheOrigin: (mainAxisExtent - centerOffset) - .clamp(-_calculatedCacheExtent!, 0.0), - ); - if (result != 0.0) return -result; - } - - // positive scroll offsets - return layoutChildSequence( - child: center, - scrollOffset: math.max(0, -centerOffset), - overlap: leadingNegativeChild == null ? math.min(0, -centerOffset) : 0.0, - layoutOffset: centerOffset >= mainAxisExtent - ? centerOffset - : reverseDirectionRemainingPaintExtent, - remainingPaintExtent: forwardDirectionRemainingPaintExtent, - mainAxisExtent: mainAxisExtent, - crossAxisExtent: crossAxisExtent, - growthDirection: GrowthDirection.forward, - advance: childAfter, - remainingCacheExtent: forwardDirectionRemainingCacheExtent, - cacheOrigin: centerOffset.clamp(-_calculatedCacheExtent!, 0.0), - ); - } - - @override - bool get hasVisualOverflow => _hasVisualOverflow; - - @override - void updateOutOfBandData( - GrowthDirection growthDirection, - SliverGeometry childLayoutGeometry, - ) { - switch (growthDirection) { - case GrowthDirection.forward: - _maxScrollExtent += childLayoutGeometry.scrollExtent; - break; - case GrowthDirection.reverse: - _minScrollExtent -= childLayoutGeometry.scrollExtent; - break; - } - if (childLayoutGeometry.hasVisualOverflow) _hasVisualOverflow = true; - _shrinkWrapExtent += childLayoutGeometry.maxPaintExtent; - growSize = _shrinkWrapExtent; - } - - @override - String labelForChild(int index) => 'child $index'; -} - -/// A widget that is bigger on the inside. -/// -/// [Viewport] is the visual workhorse of the scrolling machinery. It displays a -/// subset of its children according to its own dimensions and the given -/// [offset]. As the offset varies, different children are visible through -/// the viewport. -/// -/// [Viewport] hosts a bidirectional list of slivers, anchored on a [center] -/// sliver, which is placed at the zero scroll offset. The center widget is -/// displayed in the viewport according to the [anchor] property. -/// -/// Slivers that are earlier in the child list than [center] are displayed in -/// reverse order in the reverse [axisDirection] starting from the [center]. For -/// example, if the [axisDirection] is [AxisDirection.down], the first sliver -/// before [center] is placed above the [center]. The slivers that are later in -/// the child list than [center] are placed in order in the [axisDirection]. For -/// example, in the preceding scenario, the first sliver after [center] is -/// placed below the [center]. -/// -/// [Viewport] cannot contain box children directly. Instead, use a -/// [SliverList], [SliverFixedExtentList], [SliverGrid], or a -/// [SliverToBoxAdapter], for example. -/// -/// See also: -/// -/// * [ListView], [PageView], [GridView], and [CustomScrollView], which combine -/// [Scrollable] and [Viewport] into widgets that are easier to use. -/// * [SliverToBoxAdapter], which allows a box widget to be placed inside a -/// sliver context (the opposite of this widget). -/// * [ShrinkWrappingViewport], a variant of [Viewport] that shrink-wraps its -/// contents along the main axis. -abstract class CustomViewport extends MultiChildRenderObjectWidget { - /// Creates a widget that is bigger on the inside. - /// - /// The viewport listens to the [offset], which means you do not need to - /// rebuild this widget when the [offset] changes. - /// - /// The [offset] argument must not be null. - /// - /// The [cacheExtent] must be specified if the [cacheExtentStyle] is - /// not [CacheExtentStyle.pixel]. - CustomViewport({ - super.key, - this.axisDirection = AxisDirection.down, - this.crossAxisDirection, - this.anchor = 0.0, - required this.offset, - this.center, - this.cacheExtent, - this.cacheExtentStyle = CacheExtentStyle.pixel, - this.clipBehavior = Clip.hardEdge, - List slivers = const [], - }) : assert( - center == null || - slivers.where((Widget child) => child.key == center).length == 1, - 'There should be at most one child with the same key as the center child: $center', - ), - assert( - cacheExtentStyle != CacheExtentStyle.viewport || cacheExtent != null, - 'A cacheExtent is required when using cacheExtentStyle.viewport', - ), - super(children: slivers); - - /// The direction in which the [offset]'s [ViewportOffset.pixels] increases. - /// - /// For example, if the [axisDirection] is [AxisDirection.down], a scroll - /// offset of zero is at the top of the viewport and increases towards the - /// bottom of the viewport. - final AxisDirection axisDirection; - - /// The direction in which child should be laid out in the cross axis. - /// - /// If the [axisDirection] is [AxisDirection.down] or [AxisDirection.up], this - /// property defaults to [AxisDirection.left] if the ambient [Directionality] - /// is [TextDirection.rtl] and [AxisDirection.right] if the ambient - /// [Directionality] is [TextDirection.ltr]. - /// - /// If the [axisDirection] is [AxisDirection.left] or [AxisDirection.right], - /// this property defaults to [AxisDirection.down]. - final AxisDirection? crossAxisDirection; - - /// The relative position of the zero scroll offset. - /// - /// For example, if [anchor] is 0.5 and the [axisDirection] is - /// [AxisDirection.down] or [AxisDirection.up], then the zero scroll offset is - /// vertically centered within the viewport. If the [anchor] is 1.0, and the - /// [axisDirection] is [AxisDirection.right], then the zero scroll offset is - /// on the left edge of the viewport. - final double anchor; - - /// Which part of the content inside the viewport should be visible. - /// - /// The [ViewportOffset.pixels] value determines the scroll offset that the - /// viewport uses to select which part of its content to display. As the user - /// scrolls the viewport, this value changes, which changes the content that - /// is displayed. - /// - /// Typically a [ScrollPosition]. - final ViewportOffset offset; - - /// The first child in the [GrowthDirection.forward] growth direction. - /// - /// Children after [center] will be placed in the [axisDirection] relative to - /// the [center]. Children before [center] will be placed in the opposite of - /// the [axisDirection] relative to the [center]. - /// - /// The [center] must be the key of a child of the viewport. - final Key? center; - - /// {@macro flutter.rendering.RenderViewportBase.cacheExtent} - /// - /// See also: - /// - /// * [cacheExtentStyle], which controls the units of the [cacheExtent]. - final double? cacheExtent; - - /// {@macro flutter.rendering.RenderViewportBase.cacheExtentStyle} - final CacheExtentStyle cacheExtentStyle; - - /// {@macro flutter.material.Material.clipBehavior} - /// - /// Defaults to [Clip.hardEdge]. - final Clip clipBehavior; - - /// Given a [BuildContext] and an [AxisDirection], determine the correct cross - /// axis direction. - /// - /// This depends on the [Directionality] if the `axisDirection` is vertical; - /// otherwise, the default cross axis direction is downwards. - static AxisDirection getDefaultCrossAxisDirection( - BuildContext context, - AxisDirection axisDirection, - ) { - switch (axisDirection) { - case AxisDirection.up: - assert(debugCheckHasDirectionality( - context, - why: - "to determine the cross-axis direction when the viewport has an 'up' axisDirection", - alternative: - "Alternatively, consider specifying the 'crossAxisDirection' argument on the Viewport.", - )); - return textDirectionToAxisDirection(Directionality.of(context)); - case AxisDirection.right: - return AxisDirection.down; - case AxisDirection.down: - assert(debugCheckHasDirectionality( - context, - why: - "to determine the cross-axis direction when the viewport has a 'down' axisDirection", - alternative: - "Alternatively, consider specifying the 'crossAxisDirection' argument on the Viewport.", - )); - return textDirectionToAxisDirection(Directionality.of(context)); - case AxisDirection.left: - return AxisDirection.down; - } - } - - @override - CustomRenderViewport createRenderObject(BuildContext context); - - @override - _ViewportElement createElement() => _ViewportElement(this); - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(EnumProperty('axisDirection', axisDirection)) - ..add(EnumProperty( - 'crossAxisDirection', - crossAxisDirection, - defaultValue: null, - )) - ..add(DoubleProperty('anchor', anchor)) - ..add(DiagnosticsProperty('offset', offset)); - if (center != null) { - properties.add(DiagnosticsProperty('center', center)); - } else if (children.isNotEmpty && children.first.key != null) { - properties.add(DiagnosticsProperty( - 'center', - children.first.key, - tooltip: 'implicit', - )); - } - properties - ..add(DiagnosticsProperty('cacheExtent', cacheExtent)) - ..add(DiagnosticsProperty( - 'cacheExtentStyle', - cacheExtentStyle, - )); - } -} - -class _ViewportElement extends MultiChildRenderObjectElement { - /// Creates an element that uses the given widget as its configuration. - _ViewportElement(CustomViewport super.widget); - - @override - CustomViewport get widget => super.widget as CustomViewport; - - @override - CustomRenderViewport get renderObject => - super.renderObject as CustomRenderViewport; - - @override - void mount(Element? parent, dynamic newSlot) { - super.mount(parent, newSlot); - _updateCenter(); - } - - @override - void update(MultiChildRenderObjectWidget newWidget) { - super.update(newWidget); - _updateCenter(); - } - - void _updateCenter() { - if (widget.center != null) { - renderObject.center = children - .singleWhere((Element element) => element.widget.key == widget.center) - .renderObject as RenderSliver?; - } else if (children.isNotEmpty) { - renderObject.center = children.first.renderObject as RenderSliver?; - } else { - renderObject.center = null; - } - } - - @override - void debugVisitOnstageChildren(ElementVisitor visitor) { - children.where((Element e) { - final renderSliver = e.renderObject! as RenderSliver; - return renderSliver.geometry!.visible; - }).forEach(visitor); - } -} - -class CustomSliverPhysicalContainerParentData - extends SliverPhysicalContainerParentData { - /// The position of the child relative to the zero scroll offset. - /// - /// The number of pixels from from the zero scroll offset of the parent sliver - /// (the line at which its [SliverConstraints.scrollOffset] is zero) to the - /// side of the child closest to that offset. A [layoutOffset] can be null - /// when it cannot be determined. The value will be set after layout. - /// - /// In a typical list, this does not change as the parent is scrolled. - /// - /// Defaults to null. - double? layoutOffset; - - GrowthDirection? growthDirection; -} - -/// A render object that is bigger on the inside. -/// -/// [RenderViewport] is the visual workhorse of the scrolling machinery. It -/// displays a subset of its children according to its own dimensions and the -/// given [offset]. As the offset varies, different children are visible through -/// the viewport. -/// -/// [RenderViewport] hosts a bidirectional list of slivers, anchored on a -/// [center] sliver, which is placed at the zero scroll offset. The center -/// widget is displayed in the viewport according to the [anchor] property. -/// -/// Slivers that are earlier in the child list than [center] are displayed in -/// reverse order in the reverse [axisDirection] starting from the [center]. For -/// example, if the [axisDirection] is [AxisDirection.down], the first sliver -/// before [center] is placed above the [center]. The slivers that are later in -/// the child list than [center] are placed in order in the [axisDirection]. For -/// example, in the preceding scenario, the first sliver after [center] is -/// placed below the [center]. -/// -/// [RenderViewport] cannot contain [RenderBox] children directly. Instead, use -/// a [RenderSliverList], [RenderSliverFixedExtentList], [RenderSliverGrid], or -/// a [RenderSliverToBoxAdapter], for example. -/// -/// See also: -/// -/// * [RenderSliver], which explains more about the Sliver protocol. -/// * [RenderBox], which explains more about the Box protocol. -/// * [RenderSliverToBoxAdapter], which allows a [RenderBox] object to be -/// placed inside a [RenderSliver] (the opposite of this class). -/// * [RenderShrinkWrappingViewport], a variant of [RenderViewport] that -/// shrink-wraps its contents along the main axis. -abstract class CustomRenderViewport - extends RenderViewportBase { - /// Creates a viewport for [RenderSliver] objects. - /// - /// If the [center] is not specified, then the first child in the `children` - /// list, if any, is used. - /// - /// The [offset] must be specified. For testing purposes, consider passing a - /// [ViewportOffset.zero] or [ViewportOffset.fixed]. - CustomRenderViewport({ - super.axisDirection, - required super.crossAxisDirection, - required super.offset, - double anchor = 0.0, - List? children, - RenderSliver? center, - super.cacheExtent, - super.cacheExtentStyle, - super.clipBehavior, - }) : assert( - anchor >= 0.0 && anchor <= 1.0, - 'Anchor must be between 0.0 and 1.0.', - ), - assert( - cacheExtentStyle != CacheExtentStyle.viewport || cacheExtent != null, - 'A cacheExtent is required when using CacheExtentStyle.viewport.', - ), - _center = center { - addAll(children); - if (center == null && firstChild != null) _center = firstChild; - } - - /// If a [RenderAbstractViewport] overrides - /// [RenderObject.describeSemanticsConfiguration] to add the [SemanticsTag] - /// [useTwoPaneSemantics] to its [SemanticsConfiguration], two semantics nodes - /// will be used to represent the viewport with its associated scrolling - /// actions in the semantics tree. - /// - /// Two semantics nodes (an inner and an outer node) are necessary to exclude - /// certain child nodes (via the [excludeFromScrolling] tag) from the - /// scrollable area for semantic purposes: The [SemanticsNode]s of children - /// that should be excluded from scrolling will be attached to the outer node. - /// The semantic scrolling actions and the [SemanticsNode]s of scrollable - /// children will be attached to the inner node, which itself is a child of - /// the outer node. - /// - /// See also: - /// - /// * [RenderViewportBase.describeSemanticsConfiguration], which adds this - /// tag to its [SemanticsConfiguration]. - static const SemanticsTag useTwoPaneSemantics = - SemanticsTag('RenderViewport.twoPane'); - - /// When a top-level [SemanticsNode] below a [RenderAbstractViewport] is - /// tagged with [excludeFromScrolling] it will not be part of the scrolling - /// area for semantic purposes. - /// - /// This behavior is only active if the [RenderAbstractViewport] - /// tagged its [SemanticsConfiguration] with [useTwoPaneSemantics]. - /// Otherwise, the [excludeFromScrolling] tag is ignored. - /// - /// As an example, a [RenderSliver] that stays on the screen within a - /// [Scrollable] even though the user has scrolled past it (e.g. a pinned app - /// bar) can tag its [SemanticsNode] with [excludeFromScrolling] to indicate - /// that it should no longer be considered for semantic actions related to - /// scrolling. - static const SemanticsTag excludeFromScrolling = - SemanticsTag('RenderViewport.excludeFromScrolling'); - - @override - void setupParentData(RenderObject child) { - if (child.parentData is! CustomSliverPhysicalContainerParentData) { - child.parentData = CustomSliverPhysicalContainerParentData(); - } - } - - /// The relative position of the zero scroll offset. - /// - /// For example, if [anchor] is 0.5 and the [axisDirection] is - /// [AxisDirection.down] or [AxisDirection.up], then the zero scroll offset is - /// vertically centered within the viewport. If the [anchor] is 1.0, and the - /// [axisDirection] is [AxisDirection.right], then the zero scroll offset is - /// on the left edge of the viewport. - double get anchor; - - set anchor(double value); - - /// The first child in the [GrowthDirection.forward] growth direction. - /// - /// This child that will be at the position defined by [anchor] when the - /// [ViewportOffset.pixels] of [offset] is `0`. - /// - /// Children after [center] will be placed in the [axisDirection] relative to - /// the [center]. Children before [center] will be placed in the opposite of - /// the [axisDirection] relative to the [center]. - /// - /// The [center] must be a child of the viewport. - RenderSliver? get center => _center; - RenderSliver? _center; - - set center(RenderSliver? value) { - if (value == _center) return; - _center = value; - markNeedsLayout(); - } - - @override - bool get sizedByParent => true; - - @override - Size computeDryLayout(BoxConstraints constraints) { - assert(() { - if (!constraints.hasBoundedHeight || !constraints.hasBoundedWidth) { - switch (axis) { - case Axis.vertical: - if (!constraints.hasBoundedHeight) { - throw FlutterError.fromParts([ - ErrorSummary('Vertical viewport was given unbounded height.'), - ErrorDescription( - 'Viewports expand in the scrolling direction to fill their container. ' - 'In this case, a vertical viewport was given an unlimited amount of ' - 'vertical space in which to expand. This situation typically happens ' - 'when a scrollable widget is nested inside another scrollable widget.', - ), - ErrorHint( - 'If this widget is always nested in a scrollable widget there ' - 'is no need to use a viewport because there will always be enough ' - 'vertical space for the children. In this case, consider using a ' - 'Column instead. Otherwise, consider using the "shrinkWrap" property ' - '(or a ShrinkWrappingViewport) to size the height of the viewport ' - 'to the sum of the heights of its children.', - ), - ]); - } - if (!constraints.hasBoundedWidth) { - throw FlutterError( - 'Vertical viewport was given unbounded width.\n' - 'Viewports expand in the cross axis to fill their container and ' - 'constrain their children to match their extent in the cross axis. ' - 'In this case, a vertical viewport was given an unlimited amount of ' - 'horizontal space in which to expand.', - ); - } - break; - case Axis.horizontal: - if (!constraints.hasBoundedWidth) { - throw FlutterError.fromParts([ - ErrorSummary( - 'Horizontal viewport was given unbounded width.', - ), - ErrorDescription( - 'Viewports expand in the scrolling direction to fill their container. ' - 'In this case, a horizontal viewport was given an unlimited amount of ' - 'horizontal space in which to expand. This situation typically happens ' - 'when a scrollable widget is nested inside another scrollable widget.', - ), - ErrorHint( - 'If this widget is always nested in a scrollable widget there ' - 'is no need to use a viewport because there will always be enough ' - 'horizontal space for the children. In this case, consider using a ' - 'Row instead. Otherwise, consider using the "shrinkWrap" property ' - '(or a ShrinkWrappingViewport) to size the width of the viewport ' - 'to the sum of the widths of its children.', - ), - ]); - } - if (!constraints.hasBoundedHeight) { - throw FlutterError( - 'Horizontal viewport was given unbounded height.\n' - 'Viewports expand in the cross axis to fill their container and ' - 'constrain their children to match their extent in the cross axis. ' - 'In this case, a horizontal viewport was given an unlimited amount of ' - 'vertical space in which to expand.', - ); - } - break; - } - } - return true; - }()); - return constraints.biggest; - } - - // Out-of-band data computed during layout. - late double _minScrollExtent; - late double _maxScrollExtent; - bool _hasVisualOverflow = false; - - double growSize = 0; - - @override - bool get hasVisualOverflow => _hasVisualOverflow; - - @override - void updateOutOfBandData( - GrowthDirection growthDirection, - SliverGeometry childLayoutGeometry, - ) { - switch (growthDirection) { - case GrowthDirection.forward: - _maxScrollExtent += childLayoutGeometry.scrollExtent; - break; - case GrowthDirection.reverse: - _minScrollExtent -= childLayoutGeometry.scrollExtent; - break; - } - if (childLayoutGeometry.hasVisualOverflow) _hasVisualOverflow = true; - } - - @override - void updateChildLayoutOffset( - RenderSliver child, - double layoutOffset, - GrowthDirection growthDirection, - ) { - final childParentData = - child.parentData! as CustomSliverPhysicalContainerParentData; - childParentData - ..layoutOffset = layoutOffset - ..growthDirection = growthDirection; - } - - @override - Offset paintOffsetOf(RenderSliver child) { - final childParentData = - child.parentData! as CustomSliverPhysicalContainerParentData; - return computeAbsolutePaintOffset( - child, - childParentData.layoutOffset!, - childParentData.growthDirection!, - ); - } - - @override - double scrollOffsetOf(RenderSliver child, double scrollOffsetWithinChild) { - assert( - child.parent == this, - 'The "child" argument must be a child of this RenderViewport.', - ); - final growthDirection = child.constraints.growthDirection; - switch (growthDirection) { - case GrowthDirection.forward: - var scrollOffsetToChild = 0.0; - var current = center; - while (current != child) { - scrollOffsetToChild += current!.geometry!.scrollExtent; - current = childAfter(current); - } - return scrollOffsetToChild + scrollOffsetWithinChild; - case GrowthDirection.reverse: - var scrollOffsetToChild = 0.0; - var current = childBefore(center!); - while (current != child) { - scrollOffsetToChild -= current!.geometry!.scrollExtent; - current = childBefore(current); - } - return scrollOffsetToChild - scrollOffsetWithinChild; - } - } - - @override - double maxScrollObstructionExtentBefore(RenderSliver child) { - assert( - child.parent == this, - 'The "child" argument must be a child of this RenderViewport.', - ); - final growthDirection = child.constraints.growthDirection; - switch (growthDirection) { - case GrowthDirection.forward: - var pinnedExtent = 0.0; - var current = center; - while (current != child) { - pinnedExtent += current!.geometry!.maxScrollObstructionExtent; - current = childAfter(current); - } - return pinnedExtent; - case GrowthDirection.reverse: - var pinnedExtent = 0.0; - var current = childBefore(center!); - while (current != child) { - pinnedExtent += current!.geometry!.maxScrollObstructionExtent; - current = childBefore(current); - } - return pinnedExtent; - } - } - - @override - void applyPaintTransform(RenderObject child, Matrix4 transform) { - final offset = paintOffsetOf(child as RenderSliver); - transform.translate(offset.dx, offset.dy); - } - - @override - double computeChildMainAxisPosition( - RenderSliver child, - double parentMainAxisPosition, - ) { - final childParentData = - child.parentData! as CustomSliverPhysicalContainerParentData; - switch (applyGrowthDirectionToAxisDirection( - child.constraints.axisDirection, - child.constraints.growthDirection, - )) { - case AxisDirection.down: - case AxisDirection.right: - return parentMainAxisPosition - childParentData.layoutOffset!; - case AxisDirection.up: - return (size.height - parentMainAxisPosition) - - childParentData.layoutOffset!; - case AxisDirection.left: - return (size.width - parentMainAxisPosition) - - childParentData.layoutOffset!; - } - } - - @override - int get indexOfFirstChild { - assert(center != null, 'RenderViewport does not have any children.'); - assert( - center!.parent == this, - 'center is not a child of this RenderViewport', - ); - assert( - firstChild != null, - 'center is the only child of this RenderViewport', - ); - var count = 0; - var child = center; - while (child != firstChild) { - count -= 1; - child = childBefore(child!); - } - return count; - } - - @override - String labelForChild(int index) { - if (index == 0) return 'center child'; - return 'child $index'; - } - - @override - Iterable get childrenInPaintOrder sync* { - if (firstChild == null) return; - var child = firstChild; - while (child != center) { - yield child!; - child = childAfter(child); - } - child = lastChild; - while (true) { - yield child!; - if (child == center) return; - child = childBefore(child); - } - } - - @override - Iterable get childrenInHitTestOrder sync* { - if (firstChild == null) return; - var child = center; - while (child != null) { - yield child; - child = childAfter(child); - } - child = childBefore(center!); - while (child != null) { - yield child; - child = childBefore(child); - } - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties.add(DoubleProperty('anchor', anchor)); - } -} diff --git a/packages/stream_chat_flutter/lib/src/ai_assistant/ai_typing_indicator_view.dart b/packages/stream_chat_flutter/lib/src/ai_assistant/ai_typing_indicator_view.dart deleted file mode 100644 index ebafb3ef2a..0000000000 --- a/packages/stream_chat_flutter/lib/src/ai_assistant/ai_typing_indicator_view.dart +++ /dev/null @@ -1,204 +0,0 @@ -import 'package:flutter/material.dart'; - -/// {@template aiTypingIndicatorView} -/// A widget that displays a typing indicator for the AI. -/// -/// This widget is used to indicate the various states of the AI such as -/// [AI_STATE_THINKING], [AI_STATE_CHECKING_SOURCES] etc. -/// -/// The widget displays a text and a series of animated dots. -/// -/// ```dart -/// AITypingIndicatorView( -/// text: 'AI is thinking', -/// ); -/// ``` -/// -/// see also: -/// - [AnimatedDots] which is used to display the animated dots. -/// {@endtemplate} -class AITypingIndicatorView extends StatelessWidget { - /// {@macro aiTypingIndicatorView} - const AITypingIndicatorView({ - super.key, - required this.text, - this.textStyle, - this.dotColor, - this.dotCount = 3, - this.dotSize = 8, - }); - - /// The text to display in the widget. - /// - /// Typically this is the state of the AI such as "AI is thinking", - final String text; - - /// The style to use for the text. - /// - /// If not provided, the default text style is used. - final TextStyle? textStyle; - - /// The color of the animated dots displayed next to the text. - /// - /// If not provided, the color of the [textStyle] is used if available or - /// [Colors.black] is used. - final Color? dotColor; - - /// The number of animated dots to display next to the text. - /// - /// Defaults to 3. - final int dotCount; - - /// The size of the animated dots displayed next to the text. - /// - /// Defaults to 8. - final double dotSize; - - @override - Widget build(BuildContext context) { - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text(text, style: textStyle), - const SizedBox(width: 8), - AnimatedDots( - size: dotSize, - count: dotCount, - color: dotColor ?? textStyle?.color ?? Colors.black, - ), - ], - ); - } -} - -/// {@template animatedDots} -/// A widget that displays a series of animated dots. -/// -/// The dots are animated to scale up and down in size and fade in and out in -/// opacity. -/// -/// The widget is typically used to indicate that someone is typing. -/// {@endtemplate} -class AnimatedDots extends StatelessWidget { - /// {@macro animatedDots} - const AnimatedDots({ - super.key, - this.count = 3, - this.size = 8, - this.spacing = 4, - this.color = Colors.black, - }); - - /// The number of dots to display. - /// - /// Defaults to 3. - final int count; - - /// The size of each dot. - /// - /// Defaults to 8. - final double size; - - /// The spacing between each dot. - /// - /// Defaults to 4. - final double spacing; - - /// The color of the dots. - /// - /// Defaults to [Colors.black]. - final Color color; - - @override - Widget build(BuildContext context) { - return Row( - spacing: 4, - mainAxisSize: MainAxisSize.min, - children: [ - ...List.generate( - count, - (index) => _AnimatedDot( - key: ValueKey(index), - index: index, - size: size, - color: color, - ), - ), - ], - ); - } -} - -class _AnimatedDot extends StatefulWidget { - const _AnimatedDot({ - super.key, - required this.index, - this.size = 8, - this.color = Colors.black, - }); - - final int index; - final double size; - final Color color; - - @override - State<_AnimatedDot> createState() => _AnimatedDotState(); -} - -class _AnimatedDotState extends State<_AnimatedDot> - with SingleTickerProviderStateMixin<_AnimatedDot> { - late final AnimationController _repeatingController; - - @override - void initState() { - super.initState(); - _repeatingController = AnimationController( - vsync: this, - duration: const Duration(milliseconds: 800), - )..addStatusListener( - (status) { - if (status == AnimationStatus.completed) { - if (mounted) _repeatingController.reverse(); - } else if (status == AnimationStatus.dismissed) { - if (mounted) _repeatingController.forward(); - } - }, - ); - - Future.delayed( - Duration(milliseconds: 200 * widget.index), - () { - if (mounted) _repeatingController.forward(); - }, - ); - } - - @override - void dispose() { - _repeatingController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final animation = CurvedAnimation( - parent: _repeatingController, - curve: Curves.easeInOut, - ); - - return ScaleTransition( - scale: Tween(begin: 0.5, end: 1).animate(animation), - child: FadeTransition( - opacity: Tween(begin: 0.3, end: 1).animate(animation), - child: Container( - width: widget.size, - height: widget.size, - decoration: BoxDecoration( - color: widget.color, - shape: BoxShape.circle, - ), - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/ai_assistant/stream_typewriter_builder.dart b/packages/stream_chat_flutter/lib/src/ai_assistant/stream_typewriter_builder.dart deleted file mode 100644 index e004784bf6..0000000000 --- a/packages/stream_chat_flutter/lib/src/ai_assistant/stream_typewriter_builder.dart +++ /dev/null @@ -1,248 +0,0 @@ -import 'dart:async'; -import 'dart:math'; - -import 'package:flutter/widgets.dart'; - -/// {@template typewriterState} -/// The current typing state of a typewriter. -/// {@endtemplate} -enum TypewriterState { - /// The typewriter is not typing. - idle, - - /// The typewriter is currently typing. - typing, - - /// The typewriter is paused at the current char index. - paused, - - /// The typewriter has stopped typing and has reset the current char index. - stopped, -} - -/// {@template typewriterValue} -/// A value class that holds the current text and typing state of a typewriter. -/// -/// The [text] field holds the current text that has been typed out. The [state] -/// field holds the current typing state of the typewriter. -/// {@endtemplate} -class TypewriterValue { - /// {@macro typewriterValue} - const TypewriterValue({ - this.text = '', - this.state = TypewriterState.idle, - }); - - /// The current text that has been typed out. - final String text; - - /// The current typing state of the typewriter. - final TypewriterState state; - - /// Creates a copy of this [TypewriterValue] with the given fields replaced - /// by the new values. - TypewriterValue copyWith({ - String? text, - TypewriterState? state, - }) { - return TypewriterValue( - text: text ?? this.text, - state: state ?? this.state, - ); - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - return other is TypewriterValue && - other.text == text && - other.state == state; - } - - @override - int get hashCode => text.hashCode ^ state.hashCode; -} - -/// {@template typewriterController} -/// A controller for a [StreamTypewriterBuilder]. It allows you to control the -/// typing state of the typewriter. You can start, pause, and stop typing the -/// target text. -/// -/// To use a [TypewriterController], simply create one and pass it to a -/// [StreamTypewriterBuilder]. The builder will listen to the controller and -/// rebuild whenever the value changes. You can then control the typing state -/// by calling [startTyping], [pauseTyping], and [stopTyping] on the controller. -/// -/// ```dart -/// final controller = TypewriterController(text: 'Hello, World!'); -/// -/// @override -/// Widget build(BuildContext context) { -/// return StreamTypewriterBuilder( -/// controller: controller, -/// builder: (context, value, child) { -/// return Text(value.text); -/// }, -/// ); -/// } -/// -/// // Start typing the target text. -/// controller.startTyping(); -/// -/// // Pause typing at the current char index. -/// controller.pauseTyping(); -/// -/// // Stop typing and reset the current char index. -/// controller.stopTyping(); -/// -/// // Update the target text. -/// controller.updateText('Hello, Flutter!'); -/// ``` -/// {@endtemplate} -class TypewriterController extends ValueNotifier { - /// {@macro typewriterController} - TypewriterController({ - String text = '', - this.typingSpeed = const Duration(milliseconds: 10), - }) : super(TypewriterValue(text: text)) { - // Set the target text and the current char index. - _targetText = value.text.characters; - _currentCharIndex = _targetText.length - 1; - } - - /// The speed at which the text should be typed out. - /// - /// Defaults to `10 milliseconds` per character. - final Duration typingSpeed; - - Timer? _timer; - - late int _currentCharIndex; - late Characters _targetText; - - /// Cancels the current typing timer and displays the target text immediately. - /// - /// This is useful when you want to display the target text immediately - /// without typing it out. - set text(String newText) { - _timer?.cancel(); - _targetText = newText.characters; - _currentCharIndex = _targetText.length; - value = value.copyWith(text: newText, state: TypewriterState.idle); - } - - /// Updates the target text to [newText]. - /// - /// If the controller is currently typing, the new text will be typed out - /// automatically. If it is not typing, the new text will be typed out only - /// if [autoStart] is true. - void updateText(String newText, {bool autoStart = true}) { - // Update the target text. - _targetText = newText.characters; - - // Start typing the new text if autoStart is true. - // - // This is only needed if the controller is currently not typing. If it is - // typing, the new text will be typed out automatically. - if (autoStart) startTyping(); - } - - /// Starts typing the target text. - /// - /// If the target text is already being typed out or is already all typed out, - /// this method does nothing. - /// - /// To pause or stop typing, call [pauseTyping] or [stopTyping] respectively. - void startTyping() { - // If already typing, return. - if (value.state == TypewriterState.typing) return; - - // If target text is already all typed out, return. - if (_currentCharIndex >= _targetText.length) return; - - value = value.copyWith(state: TypewriterState.typing); - - _timer = Timer.periodic(typingSpeed, (timer) { - if (_currentCharIndex < _targetText.length) { - _currentCharIndex = min(_currentCharIndex + 1, _targetText.length); - final newDisplayedText = _targetText.take(_currentCharIndex).string; - value = value.copyWith(text: newDisplayedText); - } else { - timer.cancel(); - value = value.copyWith(state: TypewriterState.idle); - } - }); - } - - /// Pauses typing at the current char index. - /// - /// To resume typing, call [startTyping]. - void pauseTyping() { - if (value.state != TypewriterState.typing) return; - - _timer?.cancel(); - value = value.copyWith(state: TypewriterState.paused); - } - - /// Stops typing and resets the current char index. - /// - /// To start typing again, call [startTyping]. - void stopTyping() { - _timer?.cancel(); - value = value.copyWith(state: TypewriterState.stopped); - - _currentCharIndex = 0; - } - - @override - void dispose() { - _timer?.cancel(); - super.dispose(); - } -} - -/// {@template typewriterWidgetBuilder} -/// A widget builder for a [StreamTypewriterBuilder]. It allows you to build a -/// widget depending on the [TypewriterValue]'s value. -/// {@endtemplate} -typedef TypewriterWidgetBuilder = Widget Function( - BuildContext context, - TypewriterValue value, - Widget? child, -); - -/// {@template streamTypewriterBuilder} -/// A widget that listens to a [TypewriterController] and rebuilds whenever the -/// value changes. It allows you to build a widget depending on the controller's -/// value. -/// {@endtemplate} -class StreamTypewriterBuilder extends StatelessWidget { - /// {@macro streamTypewriterBuilder} - const StreamTypewriterBuilder({ - super.key, - required this.controller, - required this.builder, - this.child, - }); - - /// The TypewriterController to listen to. - final TypewriterController controller; - - /// The builder to build the widget depending on the controller's value. - final TypewriterWidgetBuilder builder; - - /// The child widget to pass to the builder. - /// - /// This is typically used to pass a widget that does not depend on the - /// controller's value. - final Widget? child; - - @override - Widget build(BuildContext context) { - return ValueListenableBuilder( - valueListenable: controller, - builder: builder, - child: child, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/ai_assistant/streaming_message_view.dart b/packages/stream_chat_flutter/lib/src/ai_assistant/streaming_message_view.dart deleted file mode 100644 index 4422b5d86a..0000000000 --- a/packages/stream_chat_flutter/lib/src/ai_assistant/streaming_message_view.dart +++ /dev/null @@ -1,90 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_markdown/flutter_markdown.dart'; -import 'package:stream_chat_flutter/src/ai_assistant/stream_typewriter_builder.dart'; -import 'package:stream_chat_flutter/src/misc/markdown_message.dart'; -import 'package:stream_chat_flutter/src/utils/device_segmentation.dart'; -import 'package:stream_chat_flutter/src/utils/helpers.dart'; - -/// {@template streamingMessageView} -/// A widget that displays a message in a streaming fashion. The message is -/// displayed as if it is being typed out by a typewriter. -/// {@endtemplate} -class StreamingMessageView extends StatefulWidget { - /// {@macro streamingMessageView} - const StreamingMessageView({ - super.key, - required this.text, - this.onTapLink, - this.typingSpeed = const Duration(milliseconds: 10), - this.onTypewriterStateChanged, - }); - - /// The text to display in the widget. - final String text; - - /// The speed at which the text is typed out. - /// - /// Defaults to 10 milliseconds per character. - final Duration typingSpeed; - - /// Called when the user taps a link in the message. - final MarkdownTapLinkCallback? onTapLink; - - /// A callback that is called whenever the typewriter state changes. - final ValueChanged? onTypewriterStateChanged; - - @override - State createState() => _StreamingMessageViewState(); -} - -class _StreamingMessageViewState extends State { - late String _displayText; - late final TypewriterController _controller; - - void _onTypewriterValueChanged() { - final value = _controller.value; - widget.onTypewriterStateChanged?.call(value.state); - setState(() => _displayText = value.text); - } - - @override - void initState() { - super.initState(); - _controller = TypewriterController( - text: widget.text, - typingSpeed: widget.typingSpeed, - )..addListener(_onTypewriterValueChanged); - - _displayText = _controller.value.text; - } - - @override - void didUpdateWidget(covariant StreamingMessageView oldWidget) { - super.didUpdateWidget(oldWidget); - if (widget.text != oldWidget.text) { - _controller.updateText(widget.text); - } - } - - @override - void dispose() { - _controller - ..removeListener(_onTypewriterValueChanged) - ..dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return StreamMarkdownMessage( - data: _displayText, - selectable: isDesktopDeviceOrWeb, - onTapLink: switch (widget.onTapLink) { - final onTapLink? => onTapLink, - _ => (String link, String? href, String title) { - if (href != null) launchURL(context, href); - }, - }, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/attachment.dart deleted file mode 100644 index 0b73a93c7c..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/attachment.dart +++ /dev/null @@ -1,7 +0,0 @@ -export 'attachment_upload_state_builder.dart'; -export 'file_attachment.dart'; -export 'gallery_attachment.dart'; -export 'giphy_attachment.dart'; -export 'image_attachment.dart'; -export 'url_attachment.dart'; -export 'video_attachment.dart'; diff --git a/packages/stream_chat_flutter/lib/src/attachment/attachment_upload_state_builder.dart b/packages/stream_chat_flutter/lib/src/attachment/attachment_upload_state_builder.dart deleted file mode 100644 index e6fd757f0b..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/attachment_upload_state_builder.dart +++ /dev/null @@ -1,253 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamAttachmentUploadStateBuilder} -/// Widget to display attachment upload state -/// {@endtemplate} -class StreamAttachmentUploadStateBuilder extends StatelessWidget { - /// {@macro streamAttachmentUploadStateBuilder} - const StreamAttachmentUploadStateBuilder({ - super.key, - required this.message, - required this.attachment, - this.preparingBuilder, - this.inProgressBuilder, - this.successBuilder, - this.failedBuilder, - }); - - /// The message that [attachment] is associated with - final Message message; - - /// The attachment currently being handled - final Attachment attachment; - - /// Widget to display when preparing to upload the [attachment] - final PreparingBuilder? preparingBuilder; - - /// {@macro inProgressBuilder} - final InProgressBuilder? inProgressBuilder; - - /// {@macro successBuilder} - final SuccessBuilder? successBuilder; - - /// {@macro failedBuilder} - final FailedBuilder? failedBuilder; - - @override - Widget build(BuildContext context) { - if (message.state.isCompleted) { - return const Offstage(); - } - - final messageId = message.id; - final attachmentId = attachment.id; - - final inProgress = inProgressBuilder ?? - (context, int sent, int total) { - return _InProgressState( - sent: sent, - total: total, - attachmentId: attachmentId, - ); - }; - - final failed = failedBuilder ?? - (context, error) { - return _FailedState( - error: error, - messageId: messageId, - attachmentId: attachmentId, - ); - }; - - final success = successBuilder ?? (context) => _SuccessState(); - - final preparing = preparingBuilder ?? - (context) => _PreparingState(attachmentId: attachmentId); - - return attachment.uploadState.when( - preparing: () => preparing(context), - inProgress: (sent, total) => inProgress(context, sent, total), - success: () => success(context), - failed: (error) => failed(context, error), - ); - } -} - -class _IconButton extends StatelessWidget { - const _IconButton({ - this.icon, - this.onPressed, - }); - - final Widget? icon; - final VoidCallback? onPressed; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 24, - width: 24, - child: RawMaterialButton( - elevation: 0, - highlightElevation: 0, - focusElevation: 0, - hoverElevation: 0, - onPressed: onPressed, - fillColor: StreamChatTheme.of(context).colorTheme.overlayDark, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - child: icon, - ), - ); - } -} - -class _PreparingState extends StatelessWidget { - const _PreparingState({required this.attachmentId}); - - final String attachmentId; - - @override - Widget build(BuildContext context) { - final channel = StreamChannel.of(context).channel; - return Column( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Align( - alignment: Alignment.topRight, - child: _IconButton( - icon: StreamSvgIcon( - icon: StreamSvgIcons.close, - color: StreamChatTheme.of(context).colorTheme.barsBg, - ), - onPressed: () => channel.cancelAttachmentUpload(attachmentId), - ), - ), - Align( - alignment: Alignment.topRight, - child: StreamUploadProgressIndicator( - uploaded: 0, - total: double.maxFinite.toInt(), - ), - ), - ], - ); - } -} - -class _InProgressState extends StatelessWidget { - const _InProgressState({ - required this.sent, - required this.total, - required this.attachmentId, - }); - - final int sent; - final int total; - final String attachmentId; - - @override - Widget build(BuildContext context) { - final channel = StreamChannel.of(context).channel; - return Column( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Align( - alignment: Alignment.topRight, - child: _IconButton( - icon: StreamSvgIcon( - icon: StreamSvgIcons.close, - color: StreamChatTheme.of(context).colorTheme.barsBg, - ), - onPressed: () => channel.cancelAttachmentUpload(attachmentId), - ), - ), - Align( - alignment: Alignment.topRight, - child: StreamUploadProgressIndicator( - uploaded: sent, - total: total, - ), - ), - ], - ); - } -} - -class _FailedState extends StatelessWidget { - const _FailedState({ - this.error, - required this.messageId, - required this.attachmentId, - }); - - final String? error; - final String messageId; - final String attachmentId; - - @override - Widget build(BuildContext context) { - final channel = StreamChannel.of(context).channel; - final theme = StreamChatTheme.of(context); - return Column( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - _IconButton( - icon: StreamSvgIcon( - size: 14, - icon: StreamSvgIcons.retry, - color: theme.colorTheme.barsBg, - ), - onPressed: () { - channel.retryAttachmentUpload(messageId, attachmentId); - }, - ), - Center( - child: DecoratedBox( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - // ignore: deprecated_member_use - color: theme.colorTheme.overlayDark.withOpacity(0.6), - ), - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 6, - horizontal: 12, - ), - child: Text( - context.translations.uploadErrorLabel, - textAlign: TextAlign.center, - style: theme.textTheme.footnote.copyWith( - color: theme.colorTheme.barsBg, - ), - ), - ), - ), - ), - ], - ); - } -} - -class _SuccessState extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Align( - alignment: Alignment.topRight, - child: CircleAvatar( - backgroundColor: StreamChatTheme.of(context).colorTheme.overlayDark, - maxRadius: 12, - child: StreamSvgIcon( - icon: StreamSvgIcons.check, - color: StreamChatTheme.of(context).colorTheme.barsBg, - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/attachment_widget_catalog.dart b/packages/stream_chat_flutter/lib/src/attachment/attachment_widget_catalog.dart deleted file mode 100644 index 5e51c7333b..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/attachment_widget_catalog.dart +++ /dev/null @@ -1,64 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:flutter/widgets.dart'; -import 'package:stream_chat_flutter/src/attachment/builder/attachment_widget_builder.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// {@template attachmentWidgetCatalog} -/// A widget catalog which determines which attachment widget should be build -/// for a given [Message] and [Attachment] based on the list of [builders]. -/// -/// This is used by the [MessageWidget] to build the widget for the -/// [Message.attachments]. If you want to customize the widget used to show -/// attachments, you can use this to add your own attachment builder. -/// {@endtemplate} -/// -/// See also: -/// -/// * [StreamAttachmentWidgetBuilder], which is used to build a widget for a -/// given [Message] and [Attachment]. -/// * [MessageWidget] which uses the [AttachmentWidgetCatalog] to build the -/// widget for the [Message.attachments]. -class AttachmentWidgetCatalog { - /// {@macro attachmentWidgetCatalog} - const AttachmentWidgetCatalog({required this.builders}); - - /// The list of builders to use to build the widget. - /// - /// The order of the builders is important. The first builder that can handle - /// the message and attachments will be used to build the widget. - final List builders; - - /// Builds a widget for the given [message] and [attachments]. - /// - /// It iterates through the list of builders and uses the first builder - /// that can handle the message and attachments. - /// - /// Throws an [Exception] if no builder is found for the message. - Widget build(BuildContext context, Message message) { - assert(!message.isDeleted, 'Cannot build attachment for deleted message'); - - assert( - message.attachments.isNotEmpty, - 'Cannot build attachment for message without attachments', - ); - - // The list of attachments to build the widget for. - final attachments = message.attachments.grouped; - for (final builder in builders) { - if (builder.canHandle(message, attachments)) { - return builder.build(context, message, attachments); - } - } - - throw Exception('No builder found for $message and $attachments'); - } -} - -extension on List { - /// Groups the attachments by their type. - Map> get grouped { - return groupBy(where((it) { - return it.type != null; - }), (attachment) => attachment.type!); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/builder/attachment_widget_builder.dart b/packages/stream_chat_flutter/lib/src/attachment/builder/attachment_widget_builder.dart deleted file mode 100644 index 851610d5b5..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/builder/attachment_widget_builder.dart +++ /dev/null @@ -1,177 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -part 'fallback_attachment_builder.dart'; -part 'file_attachment_builder.dart'; -part 'gallery_attachment_builder.dart'; -part 'giphy_attachment_builder.dart'; -part 'image_attachment_builder.dart'; -part 'mixed_attachment_builder.dart'; -part 'url_attachment_builder.dart'; -part 'video_attachment_builder.dart'; -part 'voice_recording_attachment_playlist_builder.dart'; -part 'voice_recording_attachment_builder/voice_recording_attachment_builder.dart'; - -/// {@template streamAttachmentWidgetTapCallback} -/// Signature for a function that's called when the user taps on an attachment. -/// {@endtemplate} -typedef StreamAttachmentWidgetTapCallback = void Function( - Message message, - Attachment attachment, -); - -/// {@template attachmentWidgetBuilder} -/// A builder which is used to build a widget for a given [Message] and -/// [Attachment]'s. This can also be used to show custom attachments. -/// {@endtemplate} -/// -/// See also: -/// -/// * [AttachmentWidgetBuilderManager], which is used to manage a list of -/// [StreamAttachmentWidgetBuilder]'s. -abstract class StreamAttachmentWidgetBuilder { - /// {@macro attachmentWidgetBuilder} - const StreamAttachmentWidgetBuilder(); - - /// The default list of builders used by the [AttachmentWidgetCatalog]. - /// - /// This list contains the following builders in order: - /// * [MixedAttachmentBuilder] - /// * [GalleryAttachmentBuilder] - /// * [GiphyAttachmentBuilder] - /// * [FileAttachmentBuilder] - /// * [ImageAttachmentBuilder] - /// * [VideoAttachmentBuilder] - /// * [UrlAttachmentBuilder] - /// * [FallbackAttachmentBuilder] - /// - /// You can use this list as a starting point for your own list of builders. - /// - /// Example: - /// - /// ```dart - /// final myBuilders = StreamAttachmentWidgetBuilder.defaultBuilders( - /// customAttachmentBuilders: [ - /// MyCustomAttachmentBuilder(), - /// MyOtherCustomAttachmentBuilder(), - /// ] - /// ); - /// ``` - /// - /// **Note**: The order of the builders in the list is important. The first - /// builder that returns `true` from [canHandle] will be used to build the - /// widget. - static List defaultBuilders({ - required Message message, - ShapeBorder? shape, - EdgeInsetsGeometry padding = const EdgeInsets.all(4), - StreamAttachmentWidgetTapCallback? onAttachmentTap, - List? customAttachmentBuilders, - }) { - return [ - ...?customAttachmentBuilders, - - // Handles a mix of image, gif, video, url and file attachments. - MixedAttachmentBuilder( - padding: padding, - onAttachmentTap: onAttachmentTap, - ), - - // Handles a mix of image, gif, and video attachments. - GalleryAttachmentBuilder( - shape: shape, - padding: padding, - runSpacing: padding.vertical / 2, - spacing: padding.horizontal / 2, - onAttachmentTap: onAttachmentTap, - ), - - // Handles file attachments. - FileAttachmentBuilder( - shape: shape, - padding: padding, - onAttachmentTap: onAttachmentTap, - ), - - // Handles giphy attachments. - GiphyAttachmentBuilder( - shape: shape, - padding: padding, - onAttachmentTap: onAttachmentTap, - ), - - // Handles image attachments. - ImageAttachmentBuilder( - shape: shape, - padding: padding, - onAttachmentTap: onAttachmentTap, - ), - - // Handles video attachments. - VideoAttachmentBuilder( - shape: shape, - padding: padding, - onAttachmentTap: onAttachmentTap, - ), - - // Handles voice recording attachments. - VoiceRecordingAttachmentPlaylistBuilder( - shape: shape, - padding: padding, - onAttachmentTap: onAttachmentTap, - ), - - // We don't handle URL attachments if the message is a reply. - if (message.quotedMessage == null) - UrlAttachmentBuilder( - shape: shape, - padding: padding, - onAttachmentTap: onAttachmentTap, - ), - - // Fallback builder should always be the last builder in the list. - const FallbackAttachmentBuilder(), - ]; - } - - /// Determines whether this builder can handle the given [message] and - /// [attachments]. If this returns `true`, [build] will be called. - /// Otherwise, the next builder in the list will be called. - bool canHandle(Message message, Map> attachments); - - /// Builds a widget for the given [message] and [attachments]. - /// This will only be called if [canHandle] returns `true`. - Widget build( - BuildContext context, - Message message, - Map> attachments, - ); - - /// Asserts that this builder can handle the given [message] and - /// [attachments]. - /// - /// This is used to ensure that the [defaultBuilders] are used correctly. - /// - /// **Note**: This method is only called in debug mode. - bool debugAssertCanHandle( - Message message, - Map> attachments, - ) { - assert(() { - if (!canHandle(message, attachments)) { - throw FlutterError.fromParts([ - ErrorSummary( - 'A $runtimeType was used to build a attachment for a message, but ' - 'it cant handle the message.', - ), - ErrorDescription( - 'The builders in the list must be checked in order. Check the ' - 'documentation for $runtimeType for more details.', - ), - ]); - } - return true; - }(), ''); - return true; - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/builder/fallback_attachment_builder.dart b/packages/stream_chat_flutter/lib/src/attachment/builder/fallback_attachment_builder.dart deleted file mode 100644 index 0226682fb6..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/builder/fallback_attachment_builder.dart +++ /dev/null @@ -1,33 +0,0 @@ -part of 'attachment_widget_builder.dart'; - -/// {@template fallbackAttachmentBuilder} -/// A widget builder for when no other builder can handle the attachments. -/// -/// Saves you from getting an error when you have an attachment type that is not -/// supported by the SDK. -/// {@endtemplate} -class FallbackAttachmentBuilder extends StreamAttachmentWidgetBuilder { - /// {@macro fallbackAttachmentBuilder} - const FallbackAttachmentBuilder(); - - @override - bool canHandle( - Message message, - Map> attachments, - ) { - // Always returns True because this builder will be used as a fallback when - // no other builder can handle the attachments. - return true; - } - - @override - Widget build( - BuildContext context, - Message message, - Map> attachments, - ) { - // Returns an empty widget because this builder will be used as a fallback - // when no other builder can handle the attachments. - return const SizedBox.shrink(); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/builder/file_attachment_builder.dart b/packages/stream_chat_flutter/lib/src/attachment/builder/file_attachment_builder.dart deleted file mode 100644 index 1b05939250..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/builder/file_attachment_builder.dart +++ /dev/null @@ -1,86 +0,0 @@ -part of 'attachment_widget_builder.dart'; - -/// {@template fileAttachmentBuilder} -/// A widget builder for [AttachmentType.file] attachment type. -/// {@endtemplate} -class FileAttachmentBuilder extends StreamAttachmentWidgetBuilder { - /// {@macro fileAttachmentBuilder} - const FileAttachmentBuilder({ - this.shape, - this.backgroundColor, - this.constraints = const BoxConstraints(), - this.padding = const EdgeInsets.all(4), - this.onAttachmentTap, - }); - - /// The shape of the file attachment. - final ShapeBorder? shape; - - /// The background color of the file attachment. - final Color? backgroundColor; - - /// The constraints to apply to the file attachment widget. - final BoxConstraints constraints; - - /// The padding to apply to the file attachment widget. - final EdgeInsetsGeometry padding; - - /// The callback to call when the attachment is tapped. - final StreamAttachmentWidgetTapCallback? onAttachmentTap; - - @override - bool canHandle( - Message message, - Map> attachments, - ) { - final files = attachments[AttachmentType.file]; - return files != null && files.isNotEmpty; - } - - @override - Widget build( - BuildContext context, - Message message, - Map> attachments, - ) { - assert(debugAssertCanHandle(message, attachments), ''); - - final files = attachments[AttachmentType.file]!; - - Widget _buildFileAttachment(Attachment file) { - VoidCallback? onTap; - if (onAttachmentTap != null) { - onTap = () => onAttachmentTap!(message, file); - } - - return InkWell( - onTap: onTap, - child: StreamFileAttachment( - file: file, - message: message, - shape: shape, - constraints: constraints, - backgroundColor: backgroundColor, - ), - ); - } - - Widget child; - if (files.length == 1) { - child = _buildFileAttachment(files.first); - } else { - child = Column( - // Add a small vertical padding between each attachment. - spacing: padding.vertical / 2, - children: [ - for (final file in files) _buildFileAttachment(file), - ], - ); - } - - return Padding( - padding: padding, - child: child, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/builder/gallery_attachment_builder.dart b/packages/stream_chat_flutter/lib/src/attachment/builder/gallery_attachment_builder.dart deleted file mode 100644 index cd2bf22e92..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/builder/gallery_attachment_builder.dart +++ /dev/null @@ -1,126 +0,0 @@ -part of 'attachment_widget_builder.dart'; - -const _kDefaultGalleryConstraints = BoxConstraints.tightFor( - width: 256, - height: 195, -); - -/// {@template galleryAttachmentBuilder} -/// A widget builder for [AttachmentType.image], [AttachmentType.video] and -/// [AttachmentType.giphy] attachment types. -/// -/// This builder will render a [StreamGalleryAttachment] widget when the message -/// has more than one image or video or giphy attachment. -/// {@endtemplate} -class GalleryAttachmentBuilder extends StreamAttachmentWidgetBuilder { - /// {@macro galleryAttachmentBuilder} - const GalleryAttachmentBuilder({ - this.shape, - this.padding = const EdgeInsets.all(2), - this.spacing = 2, - this.runSpacing = 2, - this.constraints = _kDefaultGalleryConstraints, - this.onAttachmentTap, - }); - - /// The shape of the gallery attachment. - final ShapeBorder? shape; - - /// The constraints to apply to the gallery attachment widget. - final BoxConstraints constraints; - - /// The padding to apply to the gallery attachment widget. - final EdgeInsetsGeometry padding; - - /// How much space to place between children in a run in the main axis. - /// - /// For example, if [spacing] is 10.0, the children will be spaced at least - /// 10.0 logical pixels apart in the main axis. - /// - /// Defaults to 2.0. - final double spacing; - - /// How much space to place between the runs themselves in the cross axis. - /// - /// For example, if [runSpacing] is 10.0, the runs will be spaced at least - /// 10.0 logical pixels apart in the cross axis. - /// - /// Defaults to 2.0. - final double runSpacing; - - /// The callback to call when the attachment is tapped. - final StreamAttachmentWidgetTapCallback? onAttachmentTap; - - @override - bool canHandle( - Message message, - Map> attachments, - ) { - final images = attachments[AttachmentType.image]; - if (images != null && images.length > 1) return true; - - final videos = attachments[AttachmentType.video]; - if (videos != null && videos.length > 1) return true; - - final giphys = attachments[AttachmentType.giphy]; - if (giphys != null && giphys.length > 1) return true; - - if (images != null && videos != null) return true; - if (images != null && giphys != null) return true; - if (videos != null && giphys != null) return true; - - return false; - } - - @override - Widget build( - BuildContext context, - Message message, - Map> attachments, - ) { - assert(debugAssertCanHandle(message, attachments), ''); - - final galleryAttachments = [...attachments.values.expand((it) => it)]; - - return Padding( - padding: padding, - child: StreamGalleryAttachment( - shape: shape, - message: message, - spacing: spacing, - runSpacing: runSpacing, - constraints: constraints, - attachments: galleryAttachments, - itemBuilder: (context, index) { - final attachment = galleryAttachments[index]; - - VoidCallback? onTap; - if (onAttachmentTap != null) { - onTap = () => onAttachmentTap!(message, attachment); - } - - return InkWell( - onTap: onTap, - child: Stack( - children: [ - StreamMediaAttachmentThumbnail( - media: attachment, - width: constraints.maxWidth, - height: constraints.maxHeight, - fit: BoxFit.cover, - ), - Padding( - padding: const EdgeInsets.all(8), - child: StreamAttachmentUploadStateBuilder( - message: message, - attachment: attachment, - ), - ), - ], - ), - ); - }, - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/builder/giphy_attachment_builder.dart b/packages/stream_chat_flutter/lib/src/attachment/builder/giphy_attachment_builder.dart deleted file mode 100644 index 2220ae9240..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/builder/giphy_attachment_builder.dart +++ /dev/null @@ -1,73 +0,0 @@ -part of 'attachment_widget_builder.dart'; - -const _kDefaultGiphyConstraints = BoxConstraints( - minWidth: 170, - maxWidth: 256, - minHeight: 100, - maxHeight: 300, -); - -/// {@template giphyAttachmentBuilder} -/// A widget builder for [AttachmentType.giphy] attachment type. -/// -/// This builder is used when a message contains only a single giphy attachment. -/// {@endtemplate} -class GiphyAttachmentBuilder extends StreamAttachmentWidgetBuilder { - /// {@macro giphyAttachmentBuilder} - const GiphyAttachmentBuilder({ - this.shape, - this.padding = const EdgeInsets.all(2), - this.constraints = _kDefaultGiphyConstraints, - this.onAttachmentTap, - }); - - /// The shape of the giphy attachment. - final ShapeBorder? shape; - - /// The constraints to apply to the giphy attachment widget. - final BoxConstraints constraints; - - /// The padding to apply to the giphy attachment widget. - final EdgeInsetsGeometry padding; - - /// The callback to call when the attachment is tapped. - final StreamAttachmentWidgetTapCallback? onAttachmentTap; - - @override - bool canHandle( - Message message, - Map> attachments, - ) { - final giphyAttachments = attachments[AttachmentType.giphy]; - return giphyAttachments != null && giphyAttachments.length == 1; - } - - @override - Widget build( - BuildContext context, - Message message, - Map> attachments, - ) { - assert(debugAssertCanHandle(message, attachments), ''); - - final giphy = attachments[AttachmentType.giphy]!.first; - - VoidCallback? onTap; - if (onAttachmentTap != null) { - onTap = () => onAttachmentTap!(message, giphy); - } - - return Padding( - padding: padding, - child: InkWell( - onTap: onTap, - child: StreamGiphyAttachment( - message: message, - constraints: constraints, - giphy: giphy, - shape: shape, - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/builder/image_attachment_builder.dart b/packages/stream_chat_flutter/lib/src/attachment/builder/image_attachment_builder.dart deleted file mode 100644 index 14aa7c687c..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/builder/image_attachment_builder.dart +++ /dev/null @@ -1,73 +0,0 @@ -part of 'attachment_widget_builder.dart'; - -const _kDefaultImageConstraints = BoxConstraints( - minWidth: 170, - maxWidth: 256, - minHeight: 100, - maxHeight: 300, -); - -/// {@template imageAttachmentBuilder} -/// A widget builder for [AttachmentType.image] attachment type. -/// -/// This builder is used when a message contains only a single image attachment. -/// {@endtemplate} -class ImageAttachmentBuilder extends StreamAttachmentWidgetBuilder { - /// {@macro imageAttachmentBuilder} - const ImageAttachmentBuilder({ - this.shape, - this.padding = const EdgeInsets.all(2), - this.constraints = _kDefaultImageConstraints, - this.onAttachmentTap, - }); - - /// The shape of the image attachment. - final ShapeBorder? shape; - - /// The constraints to apply to the image attachment widget. - final BoxConstraints constraints; - - /// The padding to apply to the image attachment widget. - final EdgeInsetsGeometry padding; - - /// The callback to call when the attachment is tapped. - final StreamAttachmentWidgetTapCallback? onAttachmentTap; - - @override - bool canHandle( - Message message, - Map> attachments, - ) { - final images = attachments[AttachmentType.image]; - return images != null && images.length == 1; - } - - @override - Widget build( - BuildContext context, - Message message, - Map> attachments, - ) { - assert(debugAssertCanHandle(message, attachments), ''); - - final image = attachments[AttachmentType.image]!.first; - - VoidCallback? onTap; - if (onAttachmentTap != null) { - onTap = () => onAttachmentTap!(message, image); - } - - return Padding( - padding: padding, - child: InkWell( - onTap: onTap, - child: StreamImageAttachment( - shape: shape, - message: message, - constraints: constraints, - image: image, - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/builder/mixed_attachment_builder.dart b/packages/stream_chat_flutter/lib/src/attachment/builder/mixed_attachment_builder.dart deleted file mode 100644 index 121c78e707..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/builder/mixed_attachment_builder.dart +++ /dev/null @@ -1,142 +0,0 @@ -part of 'attachment_widget_builder.dart'; - -/// {@template mixedAttachmentBuilder} -/// A widget builder for Mixed attachment type. -/// -/// This builder is used when a message contains a mix of media type and file -/// or url preview attachments. -/// -/// This builder will render first the url preview or file attachment and then -/// the media attachments. -/// {@endtemplate} -class MixedAttachmentBuilder extends StreamAttachmentWidgetBuilder { - /// {@macro mixedAttachmentBuilder} - MixedAttachmentBuilder({ - this.padding = const EdgeInsets.all(4), - StreamAttachmentWidgetTapCallback? onAttachmentTap, - }) : _imageAttachmentBuilder = ImageAttachmentBuilder( - padding: EdgeInsets.zero, - onAttachmentTap: onAttachmentTap, - ), - _videoAttachmentBuilder = VideoAttachmentBuilder( - padding: EdgeInsets.zero, - onAttachmentTap: onAttachmentTap, - ), - _giphyAttachmentBuilder = GiphyAttachmentBuilder( - padding: EdgeInsets.zero, - onAttachmentTap: onAttachmentTap, - ), - _galleryAttachmentBuilder = GalleryAttachmentBuilder( - padding: EdgeInsets.zero, - onAttachmentTap: onAttachmentTap, - ), - _fileAttachmentBuilder = FileAttachmentBuilder( - padding: EdgeInsets.zero, - onAttachmentTap: onAttachmentTap, - ), - _urlAttachmentBuilder = UrlAttachmentBuilder( - padding: EdgeInsets.zero, - onAttachmentTap: onAttachmentTap, - ), - _voiceRecordingAttachmentPlaylistBuilder = - VoiceRecordingAttachmentPlaylistBuilder( - padding: EdgeInsets.zero, - onAttachmentTap: onAttachmentTap, - ); - - /// The padding to apply to the mixed attachment widget. - final EdgeInsetsGeometry padding; - - late final StreamAttachmentWidgetBuilder _imageAttachmentBuilder; - late final StreamAttachmentWidgetBuilder _videoAttachmentBuilder; - late final StreamAttachmentWidgetBuilder _giphyAttachmentBuilder; - late final StreamAttachmentWidgetBuilder _galleryAttachmentBuilder; - late final StreamAttachmentWidgetBuilder _fileAttachmentBuilder; - late final StreamAttachmentWidgetBuilder _urlAttachmentBuilder; - late final StreamAttachmentWidgetBuilder - _voiceRecordingAttachmentPlaylistBuilder; - - @override - bool canHandle( - Message message, - Map> attachments, - ) { - final types = {...attachments.keys}; - - final mediaTypes = { - AttachmentType.image, - AttachmentType.video, - AttachmentType.giphy, - }; - - final otherTypes = { - AttachmentType.file, - AttachmentType.urlPreview, - AttachmentType.voiceRecording, - }; - - // Check if there's at least one media type and one other type - final hasMedia = types.intersection(mediaTypes).isNotEmpty; - final hasOther = types.intersection(otherTypes).isNotEmpty; - - return hasMedia && hasOther || types.intersection(otherTypes).length > 1; - } - - @override - Widget build( - BuildContext context, - Message message, - Map> attachments, - ) { - assert(debugAssertCanHandle(message, attachments), ''); - - final urls = attachments[AttachmentType.urlPreview]; - final files = attachments[AttachmentType.file]; - final voiceRecordings = attachments[AttachmentType.voiceRecording]; - final images = attachments[AttachmentType.image]; - final videos = attachments[AttachmentType.video]; - final giphys = attachments[AttachmentType.giphy]; - - final shouldBuildGallery = [...?images, ...?videos, ...?giphys].length > 1; - - return Padding( - padding: padding, - child: Column( - spacing: padding.vertical / 2, - mainAxisSize: MainAxisSize.min, - children: [ - if (urls != null) - _urlAttachmentBuilder.build(context, message, { - AttachmentType.urlPreview: urls, - }), - if (files != null) - _fileAttachmentBuilder.build(context, message, { - AttachmentType.file: files, - }), - if (voiceRecordings != null) - _voiceRecordingAttachmentPlaylistBuilder.build(context, message, { - AttachmentType.voiceRecording: voiceRecordings, - }), - if (shouldBuildGallery) - _galleryAttachmentBuilder.build(context, message, { - if (images != null) AttachmentType.image: images, - if (videos != null) AttachmentType.video: videos, - if (giphys != null) AttachmentType.giphy: giphys, - }) - else if (images != null && images.length == 1) - _imageAttachmentBuilder.build(context, message, { - AttachmentType.image: images, - }) - else if (videos != null && videos.length == 1) - _videoAttachmentBuilder.build(context, message, { - AttachmentType.video: videos, - }) - else if (giphys != null && giphys.length == 1) - _giphyAttachmentBuilder.build(context, message, { - AttachmentType.giphy: giphys, - }), - ], - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/builder/url_attachment_builder.dart b/packages/stream_chat_flutter/lib/src/attachment/builder/url_attachment_builder.dart deleted file mode 100644 index ab464d9ed0..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/builder/url_attachment_builder.dart +++ /dev/null @@ -1,103 +0,0 @@ -part of 'attachment_widget_builder.dart'; - -const _kDefaultUrlAttachmentConstraints = BoxConstraints(maxWidth: 256); - -/// {@template urlAttachmentBuilder} -/// A widget builder for url attachment type. -/// -/// This is used to show url attachments with a preview. e.g. youtube, twitter, -/// etc. -/// {@endtemplate} -class UrlAttachmentBuilder extends StreamAttachmentWidgetBuilder { - /// {@macro urlAttachmentBuilder} - const UrlAttachmentBuilder({ - this.shape, - this.padding = const EdgeInsets.all(8), - this.constraints = _kDefaultUrlAttachmentConstraints, - this.onAttachmentTap, - }); - - /// The shape of the url attachment. - final ShapeBorder? shape; - - /// The constraints to apply to the url attachment widget. - final BoxConstraints constraints; - - /// The padding to apply to the url attachment widget. - final EdgeInsetsGeometry padding; - - /// The callback to call when the attachment is tapped. - final StreamAttachmentWidgetTapCallback? onAttachmentTap; - - @override - bool canHandle( - Message message, - Map> attachments, - ) { - final urls = attachments[AttachmentType.urlPreview]; - return urls != null && urls.isNotEmpty; - } - - @override - Widget build( - BuildContext context, - Message message, - Map> attachments, - ) { - assert(debugAssertCanHandle(message, attachments), ''); - - final urlPreviews = attachments[AttachmentType.urlPreview]!; - - final client = StreamChat.of(context).client; - final isMyMessage = message.user?.id == client.state.currentUser?.id; - - final streamChatTheme = StreamChatTheme.of(context); - final messageTheme = isMyMessage - ? streamChatTheme.ownMessageTheme - : streamChatTheme.otherMessageTheme; - - Widget _buildUrlPreview(Attachment urlPreview) { - VoidCallback? onTap; - if (onAttachmentTap != null) { - onTap = () => onAttachmentTap!(message, urlPreview); - } - - final host = Uri.parse(urlPreview.titleLink!).host; - final splitList = host.split('.'); - final hostName = splitList.length == 3 ? splitList[1] : splitList[0]; - final hostDisplayName = urlPreview.authorName?.capitalize() ?? - getWebsiteName(hostName.toLowerCase()) ?? - hostName.capitalize(); - - return InkWell( - onTap: onTap, - child: StreamUrlAttachment( - message: message, - urlAttachment: urlPreview, - hostDisplayName: hostDisplayName, - messageTheme: messageTheme, - constraints: constraints, - shape: shape, - ), - ); - } - - Widget child; - if (urlPreviews.length == 1) { - child = _buildUrlPreview(urlPreviews.first); - } else { - child = Column( - // Add a small vertical padding between each attachment. - spacing: padding.vertical / 2, - children: [ - for (final urlPreview in urlPreviews) _buildUrlPreview(urlPreview), - ], - ); - } - - return Padding( - padding: padding, - child: child, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/builder/video_attachment_builder.dart b/packages/stream_chat_flutter/lib/src/attachment/builder/video_attachment_builder.dart deleted file mode 100644 index 45f92ef8b8..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/builder/video_attachment_builder.dart +++ /dev/null @@ -1,72 +0,0 @@ -part of 'attachment_widget_builder.dart'; - -const _kDefaultVideoConstraints = BoxConstraints.tightFor( - width: 256, - height: 195, -); - -/// {@template videoAttachmentBuilder} -/// A widget builder for [AttachmentType.video] attachment type. -/// -/// This builder is used when a message contains only a single video attachment. -/// {@endtemplate} -class VideoAttachmentBuilder extends StreamAttachmentWidgetBuilder { - /// {@macro videoAttachmentBuilder} - const VideoAttachmentBuilder({ - this.shape, - this.padding = const EdgeInsets.all(2), - this.constraints = _kDefaultVideoConstraints, - this.onAttachmentTap, - }); - - /// The shape of the video attachment. - final ShapeBorder? shape; - - /// The constraints to apply to the video attachment widget. - final BoxConstraints constraints; - - /// The padding to apply to the video attachment widget. - final EdgeInsetsGeometry padding; - - /// The callback to call when the attachment is tapped. - final StreamAttachmentWidgetTapCallback? onAttachmentTap; - - @override - bool canHandle( - Message message, - Map> attachments, - ) { - final videos = attachments[AttachmentType.video]; - if (videos != null && videos.length == 1) return true; - - return false; - } - - @override - Widget build( - BuildContext context, - Message message, - Map> attachments, - ) { - assert(debugAssertCanHandle(message, attachments), ''); - - final video = attachments[AttachmentType.video]!.first; - - VoidCallback? onTap; - if (onAttachmentTap != null) { - onTap = () => onAttachmentTap!(message, video); - } - - return Padding( - padding: padding, - child: InkWell( - onTap: onTap, - child: StreamVideoAttachment( - message: message, - constraints: constraints, - video: video, - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/builder/voice_recording_attachment_builder/stream_voice_recording_list_player.dart b/packages/stream_chat_flutter/lib/src/attachment/builder/voice_recording_attachment_builder/stream_voice_recording_list_player.dart deleted file mode 100644 index d13e2ec620..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/builder/voice_recording_attachment_builder/stream_voice_recording_list_player.dart +++ /dev/null @@ -1,139 +0,0 @@ -// coverage:ignore-file - -import 'dart:async'; - -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:just_audio/just_audio.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template StreamVoiceRecordingListPlayer} -/// Display many audios and displays a list of AudioPlayerMessage. -/// {@endtemplate} -@Deprecated('Use StreamVoiceRecordingAttachmentPlaylist instead') -class StreamVoiceRecordingListPlayer extends StatefulWidget { - /// {@macro StreamVoiceRecordingListPlayer} - const StreamVoiceRecordingListPlayer({ - super.key, - required this.playList, - this.attachmentBorderRadiusGeometry, - this.constraints, - }); - - /// List of audio attachments. - final List playList; - - /// The border radius of each audio. - final BorderRadiusGeometry? attachmentBorderRadiusGeometry; - - /// Constraints of audio attachments - final BoxConstraints? constraints; - - @override - State createState() => - _StreamVoiceRecordingListPlayerState(); -} - -@Deprecated("Use 'StreamVoiceRecordingAttachmentPlaylist' instead") -class _StreamVoiceRecordingListPlayerState - extends State { - final _player = AudioPlayer(); - late StreamSubscription _playerStateChangedSubscription; - - Widget _createAudioPlayer(int index, PlayListItem item) { - final url = item.assetUrl; - Widget child; - - if (url == null) { - child = const StreamVoiceRecordingLoading(); - } else { - child = StreamVoiceRecordingPlayer( - player: _player, - duration: item.duration, - waveBars: item.waveForm, - index: index, - ); - } - - final theme = - StreamChatTheme.of(context).voiceRecordingTheme.listPlayerTheme; - - return Container( - margin: theme.margin, - constraints: widget.constraints, - decoration: BoxDecoration( - color: theme.backgroundColor, - border: Border.all( - color: theme.borderColor!, - ), - borderRadius: - widget.attachmentBorderRadiusGeometry ?? theme.borderRadius, - ), - child: child, - ); - } - - void _playerStateListener(PlayerState state) async { - if (state.processingState == ProcessingState.completed) { - await _player.stop(); - await _player.seek(Duration.zero, index: 0); - } - } - - @override - void initState() { - super.initState(); - - _playerStateChangedSubscription = - _player.playerStateStream.listen(_playerStateListener); - } - - @override - void dispose() { - super.dispose(); - - _playerStateChangedSubscription.cancel(); - _player.dispose(); - } - - @override - Widget build(BuildContext context) { - final playList = widget.playList - .where((attachment) => attachment.assetUrl != null) - .map((attachment) => AudioSource.uri(Uri.parse(attachment.assetUrl!))) - .toList(); - - final audioSource = ConcatenatingAudioSource(children: playList); - - _player - ..setShuffleModeEnabled(false) - ..setLoopMode(LoopMode.off) - ..setAudioSource(audioSource, preload: false); - - return Column( - children: widget.playList.mapIndexed(_createAudioPlayer).toList(), - ); - } -} - -/// {@template PlayListItem} -/// Represents an audio attachment meta data. -/// {@endtemplate} -@Deprecated("Use 'PlaylistTrack' instead") -class PlayListItem { - /// {@macro PlayListItem} - const PlayListItem({ - this.assetUrl, - required this.duration, - required this.waveForm, - }); - - /// The url of the audio. - final String? assetUrl; - - /// The duration of the audio. - final Duration duration; - - /// The wave form of the audio. - final List waveForm; -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/builder/voice_recording_attachment_builder/stream_voice_recording_loading.dart b/packages/stream_chat_flutter/lib/src/attachment/builder/voice_recording_attachment_builder/stream_voice_recording_loading.dart deleted file mode 100644 index 7f26f1b6ff..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/builder/voice_recording_attachment_builder/stream_voice_recording_loading.dart +++ /dev/null @@ -1,33 +0,0 @@ -// coverage:ignore-file - -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template StreamVoiceRecordingLoading} -/// Loading widget for audio message. Use this when the url from the audio -/// message is still not available. One use situation in when the audio is -/// still being uploaded. -/// {@endtemplate} -@Deprecated('Will be removed in the next major version') -class StreamVoiceRecordingLoading extends StatelessWidget { - /// {@macro StreamVoiceRecordingLoading} - const StreamVoiceRecordingLoading({super.key}); - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context).voiceRecordingTheme.loadingTheme; - - return Padding( - padding: theme.padding!, - child: SizedBox( - height: theme.size!.height, - width: theme.size!.width, - child: CircularProgressIndicator( - // ignore: unnecessary_null_checks - strokeWidth: theme.strokeWidth!, - color: theme.color, - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/builder/voice_recording_attachment_builder/stream_voice_recording_player.dart b/packages/stream_chat_flutter/lib/src/attachment/builder/voice_recording_attachment_builder/stream_voice_recording_player.dart deleted file mode 100644 index 8c48a9af51..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/builder/voice_recording_attachment_builder/stream_voice_recording_player.dart +++ /dev/null @@ -1,319 +0,0 @@ -// coverage:ignore-file - -import 'dart:async'; -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:just_audio/just_audio.dart'; -import 'package:rxdart/rxdart.dart'; -import 'package:stream_chat_flutter/src/attachment/builder/voice_recording_attachment_builder/stream_voice_recording_slider.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template StreamVoiceRecordingPlayer} -/// Embedded player for audio messages. It displays the data for the audio -/// message and allow the user to interact with the player providing buttons -/// to play/pause, seek the audio and change the speed of reproduction. -/// -/// When waveBars are not provided they are shown as 0 bars. -/// {@endtemplate} -@Deprecated("Use 'StreamVoiceRecordingAttachment' instead") -class StreamVoiceRecordingPlayer extends StatefulWidget { - /// {@macro StreamVoiceRecordingPlayer} - const StreamVoiceRecordingPlayer({ - super.key, - required this.player, - required this.duration, - this.waveBars, - this.index = 0, - this.fileSize, - this.actionButton, - }); - - /// The player of the audio. - final AudioPlayer player; - - /// The wave bars of the recorded audio from 0 to 1. When not provided - /// this Widget shows then as small dots. - final List? waveBars; - - /// The duration of the audio. - final Duration duration; - - /// The index of the audio inside the play list. If not provided, this is - /// assumed to be zero. - final int index; - - /// The file size in bits. - final int? fileSize; - - /// An action button to be used. - final Widget? actionButton; - - @override - _StreamVoiceRecordingPlayerState createState() => - _StreamVoiceRecordingPlayerState(); -} - -@Deprecated("Use 'StreamVoiceRecordingAttachment' instead") -class _StreamVoiceRecordingPlayerState - extends State { - var _seeking = false; - - @override - void dispose() { - super.dispose(); - - widget.player.dispose(); - } - - @override - Widget build(BuildContext context) { - if (widget.duration != Duration.zero) { - return _content(widget.duration); - } else { - return StreamBuilder( - stream: widget.player.durationStream, - builder: (context, snapshot) { - if (snapshot.hasData) { - return _content(snapshot.data!); - } else if (snapshot.hasError) { - return const Center(child: Text('Error!!')); - } else { - return const StreamVoiceRecordingLoading(); - } - }, - ); - } - } - - Widget _content(Duration totalDuration) { - return Container( - padding: const EdgeInsets.all(8), - height: 60, - child: Row( - children: [ - SizedBox( - width: 36, - height: 36, - child: _controlButton(), - ), - Padding( - padding: const EdgeInsets.only(left: 8), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _timer(totalDuration), - _fileSizeWidget(widget.fileSize), - ], - ), - ), - _audioWaveSlider(totalDuration), - _speedAndActionButton(), - ], - ), - ); - } - - Widget _controlButton() { - final theme = StreamChatTheme.of(context).voiceRecordingTheme.playerTheme; - - return StreamBuilder( - initialData: false, - stream: _playingThisStream(), - builder: (context, snapshot) { - final playingThis = snapshot.data == true; - - final icon = playingThis ? theme.pauseIcon : theme.playIcon; - - final processingState = widget.player.playerStateStream - .map((event) => event.processingState); - - return StreamBuilder( - stream: processingState, - initialData: ProcessingState.idle, - builder: (context, snapshot) { - final state = snapshot.data ?? ProcessingState.idle; - if (state == ProcessingState.ready || - state == ProcessingState.idle || - !playingThis) { - return ElevatedButton( - style: ElevatedButton.styleFrom( - elevation: theme.buttonElevation, - padding: theme.buttonPadding, - backgroundColor: theme.buttonBackgroundColor, - shape: theme.buttonShape, - ), - child: Icon(icon, color: theme.iconColor), - onPressed: () { - if (playingThis) { - _pause(); - } else { - _play(); - } - }, - ); - } else { - return const CircularProgressIndicator(strokeWidth: 3); - } - }, - ); - }, - ); - } - - Widget _speedAndActionButton() { - final theme = StreamChatTheme.of(context).voiceRecordingTheme.playerTheme; - - final speedStream = _playingThisStream().flatMap((showSpeed) => - widget.player.speedStream.map((speed) => showSpeed ? speed : -1.0)); - - return StreamBuilder( - initialData: -1, - stream: speedStream, - builder: (context, snapshot) { - if (snapshot.hasData && snapshot.data! > 0) { - final speed = snapshot.data!; - return SizedBox( - width: theme.speedButtonSize!.width, - height: theme.speedButtonSize!.height, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - elevation: theme.speedButtonElevation, - backgroundColor: theme.speedButtonBackgroundColor, - padding: theme.speedButtonPadding, - shape: theme.speedButtonShape, - ), - child: Text( - '${speed}x', - style: theme.speedButtonTextStyle, - ), - onPressed: () { - setState(() { - if (speed == 2) { - widget.player.setSpeed(1); - } else { - widget.player.setSpeed(speed + 0.5); - } - }); - }, - ), - ); - } else { - if (widget.actionButton != null) { - return widget.actionButton!; - } else { - return SizedBox( - width: theme.speedButtonSize!.width, - height: theme.speedButtonSize!.height, - child: theme.fileTypeIcon, - ); - } - } - }, - ); - } - - Widget _fileSizeWidget(int? fileSize) { - final theme = StreamChatTheme.of(context).voiceRecordingTheme.playerTheme; - - if (fileSize != null) { - return Text( - fileSize.toHumanReadableSize(), - style: theme.fileSizeTextStyle, - ); - } else { - return const SizedBox.shrink(); - } - } - - Widget _timer(Duration totalDuration) { - final theme = StreamChatTheme.of(context).voiceRecordingTheme.playerTheme; - - return StreamBuilder( - stream: widget.player.positionStream, - builder: (context, snapshot) { - if (snapshot.hasData && - (widget.player.currentIndex == widget.index && - (widget.player.playing || - snapshot.data!.inMilliseconds > 0 || - _seeking))) { - return Text( - snapshot.data!.toMinutesAndSeconds(), - style: theme.timerTextStyle, - ); - } else { - return Text( - totalDuration.toMinutesAndSeconds(), - style: theme.timerTextStyle, - ); - } - }, - ); - } - - Widget _audioWaveSlider(Duration totalDuration) { - final positionStream = widget.player.currentIndexStream.flatMap( - (index) => widget.player.positionStream.map((duration) => _sliderValue( - duration, - totalDuration, - index, - )), - ); - - return Expanded( - child: StreamVoiceRecordingSlider( - waves: widget.waveBars ?? List.filled(50, 0), - progressStream: positionStream, - onChangeStart: (val) { - setState(() { - _seeking = true; - }); - }, - onChanged: (val) { - widget.player.pause(); - widget.player.seek( - totalDuration * val, - index: widget.index, - ); - }, - onChangeEnd: () { - setState(() { - _seeking = false; - }); - }, - ), - ); - } - - double _sliderValue( - Duration duration, - Duration totalDuration, - int? currentIndex, - ) { - if (widget.index != currentIndex) { - return 0; - } else { - return min(duration.inMicroseconds / totalDuration.inMicroseconds, 1); - } - } - - Stream _playingThisStream() { - return widget.player.playingStream.flatMap((playing) { - return widget.player.currentIndexStream.map( - (index) => playing && index == widget.index, - ); - }); - } - - Future _play() async { - if (widget.index != widget.player.currentIndex) { - widget.player.seek(Duration.zero, index: widget.index); - } - - widget.player.play(); - } - - Future _pause() { - return widget.player.pause(); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/builder/voice_recording_attachment_builder/stream_voice_recording_slider.dart b/packages/stream_chat_flutter/lib/src/attachment/builder/voice_recording_attachment_builder/stream_voice_recording_slider.dart deleted file mode 100644 index a3ed7ddbbb..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/builder/voice_recording_attachment_builder/stream_voice_recording_slider.dart +++ /dev/null @@ -1,239 +0,0 @@ -// coverage:ignore-file - -import 'dart:math'; - -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template StreamVoiceRecordingSlider} -/// A Widget that draws the audio wave bars for an audio inside a Slider. -/// This Widget is indeed to be used to control the position of an audio message -/// and to get feedback of the position. -/// {@endtemplate} -@Deprecated("Use 'StreamAudioWaveformSlider' instead") -class StreamVoiceRecordingSlider extends StatefulWidget { - /// {@macro StreamVoiceRecordingSlider} - const StreamVoiceRecordingSlider({ - super.key, - required this.waves, - required this.progressStream, - this.onChangeStart, - this.onChanged, - this.onChangeEnd, - this.customSliderButton, - this.customSliderButtonWidth, - }); - - /// The audio bars from 0.0 to 1.0. - final List waves; - - /// The progress of the audio. - final Stream progressStream; - - /// Callback called when Slider drag starts. - final Function(double)? onChangeStart; - - /// Callback called when Slider drag updates. - final Function(double)? onChanged; - - /// Callback called when Slider drag ends. - final Function()? onChangeEnd; - - /// A custom Slider button. Use this to substitute the default rounded - /// rectangle. - final Widget? customSliderButton; - - /// The width of the customSliderButton. This should match the width of the - /// provided Widget. - final double? customSliderButtonWidth; - - @override - _StreamVoiceRecordingSliderState createState() => - _StreamVoiceRecordingSliderState(); -} - -@Deprecated("Use 'StreamAudioWaveformSlider' instead") -class _StreamVoiceRecordingSliderState - extends State { - var _dragging = false; - final _initialWidth = 7.0; - final _finalWidth = 14.0; - final _initialHeight = 30.0; - final _finalHeight = 35.0; - - Duration get animationDuration => - _dragging ? Duration.zero : const Duration(milliseconds: 300); - - double get _currentWidth { - if (widget.customSliderButtonWidth != null) { - return widget.customSliderButtonWidth!; - } else { - return _dragging ? _finalWidth : _initialWidth; - } - } - - double get _currentHeight => _dragging ? _finalHeight : _initialHeight; - - double _progressToWidth( - BoxConstraints constraints, double progress, double horizontalPadding) { - final availableWidth = constraints.maxWidth - horizontalPadding * 2; - - return availableWidth * progress - _currentWidth / 2 + horizontalPadding; - } - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context).voiceRecordingTheme.sliderTheme; - - return StreamBuilder( - initialData: 0, - stream: widget.progressStream, - builder: (context, snapshot) { - final progress = snapshot.data ?? 0; - - final sliderButton = widget.customSliderButton ?? - Container( - width: _currentWidth, - height: _currentHeight, - decoration: BoxDecoration( - color: theme.buttonColor, - boxShadow: [ - theme.buttonShadow!, - ], - border: Border.all( - color: theme.buttonBorderColor!, - width: theme.buttonBorderWidth!, - ), - borderRadius: theme.buttonBorderRadius, - ), - ); - - return LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - return Stack( - alignment: Alignment.center, - children: [ - CustomPaint( - size: Size(constraints.maxWidth, constraints.maxHeight), - painter: _AudioBarsPainter( - bars: widget.waves, - spacingRatio: theme.spacingRatio, - barHeightRatio: theme.waveHeightRatio, - colorLeft: theme.waveColorPlayed!, - colorRight: theme.waveColorUnplayed!, - progressPercentage: progress, - padding: theme.horizontalPadding, - ), - ), - AnimatedPositioned( - duration: animationDuration, - left: _progressToWidth( - constraints, progress, theme.horizontalPadding), - curve: const ElasticOutCurve(1.05), - child: sliderButton, - ), - GestureDetector( - onHorizontalDragStart: (details) { - widget.onChangeStart - ?.call(details.localPosition.dx / constraints.maxWidth); - - setState(() { - _dragging = true; - }); - }, - onHorizontalDragEnd: (details) { - widget.onChangeEnd?.call(); - - setState(() { - _dragging = false; - }); - }, - onHorizontalDragUpdate: (details) { - widget.onChanged?.call( - min( - max(details.localPosition.dx / constraints.maxWidth, 0), - 1, - ), - ); - }, - ), - ], - ); - }, - ); - }, - ); - } -} - -class _AudioBarsPainter extends CustomPainter { - _AudioBarsPainter({ - required this.bars, - required this.progressPercentage, - this.colorLeft = Colors.blueAccent, - this.colorRight = Colors.grey, - this.spacingRatio = 0.01, - this.barHeightRatio = 1, - this.padding = 20, - }); - - final List bars; - final double progressPercentage; - final Color colorRight; - final Color colorLeft; - final double spacingRatio; - final double barHeightRatio; - final double padding; - - /// barWidth should include spacing, not only the width of the bar. - /// progressX should be the middle of the moving button of the slider, not - /// initial X position. - Color _barColor(double buttonCenter, double progressX) { - return (progressX > buttonCenter) ? colorLeft : colorRight; - } - - double _barHeight(double barValue, totalHeight) { - return max(barValue * totalHeight * barHeightRatio, 2); - } - - double _progressToWidth(double totalWidth, double progress) { - final availableWidth = totalWidth; - - return availableWidth * progress + padding; - } - - @override - void paint(Canvas canvas, Size size) { - final totalWidth = size.width - padding * 2; - - final spacingWidth = totalWidth * spacingRatio; - final totalBarWidth = totalWidth - spacingWidth * (bars.length - 1); - final barWidth = totalBarWidth / bars.length; - final barY = size.height / 2; - - bars.forEachIndexed((i, barValue) { - final barHeight = _barHeight(barValue, size.height); - final barX = i * (barWidth + spacingWidth) + barWidth / 2 + padding; - - final rect = RRect.fromRectAndRadius( - Rect.fromCenter( - center: Offset(barX, barY), - width: barWidth, - height: barHeight, - ), - const Radius.circular(50), - ); - - final paint = Paint() - ..color = _barColor( - barX + barWidth / 2, - _progressToWidth(totalWidth, progressPercentage), - ); - canvas.drawRRect(rect, paint); - }); - } - - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) => true; -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/builder/voice_recording_attachment_builder/voice_recording_attachment_builder.dart b/packages/stream_chat_flutter/lib/src/attachment/builder/voice_recording_attachment_builder/voice_recording_attachment_builder.dart deleted file mode 100644 index 412b653cc0..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/builder/voice_recording_attachment_builder/voice_recording_attachment_builder.dart +++ /dev/null @@ -1,35 +0,0 @@ -// coverage:ignore-file - -part of '../attachment_widget_builder.dart'; - -/// The default attachment builder for voice recordings -@Deprecated("Use 'VoiceRecordingAttachmentPlaylistBuilder' instead") -class VoiceRecordingAttachmentBuilder extends StreamAttachmentWidgetBuilder { - @override - bool canHandle(Message message, Map> attachments) { - final recordings = attachments[AttachmentType.voiceRecording]; - if (recordings != null && recordings.length == 1) return true; - - return false; - } - - @override - Widget build(BuildContext context, Message message, - Map> attachments) { - final recordings = attachments[AttachmentType.voiceRecording]!; - - return StreamVoiceRecordingListPlayer( - playList: recordings - .map( - (r) => PlayListItem( - assetUrl: r.assetUrl, - duration: r.duration, - waveForm: r.waveform, - ), - ) - .toList(), - attachmentBorderRadiusGeometry: BorderRadius.circular(16), - constraints: const BoxConstraints.tightFor(width: 400), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/builder/voice_recording_attachment_playlist_builder.dart b/packages/stream_chat_flutter/lib/src/attachment/builder/voice_recording_attachment_playlist_builder.dart deleted file mode 100644 index f53fea8642..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/builder/voice_recording_attachment_playlist_builder.dart +++ /dev/null @@ -1,64 +0,0 @@ -part of 'attachment_widget_builder.dart'; - -/// {@template voiceRecordingAttachmentPlaylistBuilder} -/// A [StreamAttachmentWidgetBuilder] for building a voice recording attachment -/// playlist widget. -/// -/// This widget is used to display a list of voice recordings in a message. -/// -/// The widget is built when the message has at least one voice recording -/// attachment. -/// {@endtemplate} -class VoiceRecordingAttachmentPlaylistBuilder - extends StreamAttachmentWidgetBuilder { - /// {@macro voiceRecordingAttachmentPlaylistBuilder} - const VoiceRecordingAttachmentPlaylistBuilder({ - this.shape, - this.padding = const EdgeInsets.all(16), - this.constraints = const BoxConstraints(), - this.onAttachmentTap, - }); - - /// The shape of the video attachment. - final ShapeBorder? shape; - - /// The padding to apply to the video attachment widget. - final EdgeInsetsGeometry padding; - - /// The constraints to apply to the video attachment widget. - final BoxConstraints constraints; - - /// The callback to call when the attachment is tapped. - final StreamAttachmentWidgetTapCallback? onAttachmentTap; - - @override - bool canHandle( - Message message, - Map> attachments, - ) { - final playlist = attachments[AttachmentType.voiceRecording]; - return playlist != null && playlist.isNotEmpty; - } - - @override - Widget build( - BuildContext context, - Message message, - Map> attachments, - ) { - assert(debugAssertCanHandle(message, attachments), ''); - - final playlist = attachments[AttachmentType.voiceRecording]!; - - return Padding( - padding: padding, - child: StreamVoiceRecordingAttachmentPlaylist( - shape: shape, - message: message, - voiceRecordings: playlist, - constraints: constraints, - separatorBuilder: (_, __) => SizedBox(height: padding.vertical / 2), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart deleted file mode 100644 index a6cf3a04a0..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart +++ /dev/null @@ -1,305 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/attachment/handler/stream_attachment_handler.dart'; -import 'package:stream_chat_flutter/src/attachment/thumbnail/file_attachment_thumbnail.dart'; -import 'package:stream_chat_flutter/src/icons/stream_svg_icon.dart'; -import 'package:stream_chat_flutter/src/indicators/upload_progress_indicator.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter/src/utils/utils.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// {@template streamFileAttachment} -/// Displays file attachments that have been sent in a chat. -/// -/// Used in [MessageWidget]. -/// {@endtemplate} -class StreamFileAttachment extends StatelessWidget { - /// {@macro streamFileAttachment} - const StreamFileAttachment({ - super.key, - required this.message, - required this.file, - this.title, - this.trailing, - this.shape, - this.backgroundColor, - this.constraints = const BoxConstraints(), - }); - - /// The [Message] that the file is attached to. - final Message message; - - /// The [Attachment] object containing the file information. - final Attachment file; - - /// The shape of the attachment. - /// - /// Defaults to [RoundedRectangleBorder] with a radius of 12. - final ShapeBorder? shape; - - /// The background color of the attachment. - /// - /// Defaults to [StreamChatTheme.colorTheme.barsBg]. - final Color? backgroundColor; - - /// The constraints to use when displaying the file. - final BoxConstraints constraints; - - /// Widget for displaying the title of the attachment. - /// (usually the file name) - final Widget? title; - - /// Widget for displaying at the end of the attachment. - /// (such as a download button) - final Widget? trailing; - - @override - Widget build(BuildContext context) { - final chatTheme = StreamChatTheme.of(context); - final textTheme = chatTheme.textTheme; - final colorTheme = chatTheme.colorTheme; - - final backgroundColor = this.backgroundColor ?? colorTheme.barsBg; - final shape = this.shape ?? - RoundedRectangleBorder( - side: BorderSide( - color: colorTheme.borders, - strokeAlign: BorderSide.strokeAlignOutside, - ), - borderRadius: BorderRadius.circular(12), - ); - - return Container( - constraints: constraints, - clipBehavior: Clip.hardEdge, - decoration: ShapeDecoration( - shape: shape, - color: backgroundColor, - ), - child: Row( - children: [ - Container( - width: 34, - height: 40, - margin: const EdgeInsets.all(8), - child: _FileTypeImage(file: file), - ), - const SizedBox(width: 8), - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - file.title ?? context.translations.fileText, - maxLines: 1, - style: textTheme.bodyBold, - overflow: TextOverflow.ellipsis, - ), - const SizedBox(height: 3), - _FileAttachmentSubtitle(attachment: file), - ], - ), - ), - const SizedBox(width: 8), - Material( - type: MaterialType.transparency, - child: trailing ?? - _Trailing( - attachment: file, - message: message, - ), - ), - ], - ), - ); - } -} - -class _FileTypeImage extends StatelessWidget { - const _FileTypeImage({required this.file}); - - final Attachment file; - - // TODO: Improve image memory. - // This is using the full image instead of a smaller version (thumbnail) - @override - Widget build(BuildContext context) { - Widget child = StreamFileAttachmentThumbnail( - file: file, - width: double.infinity, - height: double.infinity, - ); - - final mediaType = file.title?.mediaType; - final isImage = mediaType?.type == AttachmentType.image; - final isVideo = mediaType?.type == AttachmentType.video; - if (isImage || isVideo) { - final colorTheme = StreamChatTheme.of(context).colorTheme; - child = Container( - clipBehavior: Clip.hardEdge, - decoration: ShapeDecoration( - shape: RoundedRectangleBorder( - side: BorderSide( - color: colorTheme.borders, - strokeAlign: BorderSide.strokeAlignOutside, - ), - borderRadius: BorderRadius.circular(8), - ), - ), - child: child, - ); - } - - return child; - } -} - -class _Trailing extends StatelessWidget { - const _Trailing({ - required this.attachment, - required this.message, - }); - - final Attachment attachment; - final Message message; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - final channel = StreamChannel.of(context).channel; - final attachmentId = attachment.id; - - if (message.state.isCompleted) { - return IconButton( - icon: StreamSvgIcon( - icon: StreamSvgIcons.cloudDownload, - color: theme.colorTheme.textHighEmphasis, - ), - visualDensity: VisualDensity.compact, - splashRadius: 16, - onPressed: () async { - final assetUrl = attachment.assetUrl; - if (assetUrl != null) { - if (isMobileDeviceOrWeb) { - launchURL(context, assetUrl); - } else { - StreamAttachmentHandler.instance.downloadAttachment(attachment); - } - } - }, - ); - } - - return attachment.uploadState.when( - preparing: () => Padding( - padding: const EdgeInsets.all(8), - child: _TrailingButton( - icon: StreamSvgIcon( - icon: StreamSvgIcons.close, - color: theme.colorTheme.barsBg, - ), - fillColor: theme.colorTheme.overlayDark, - onPressed: () => channel.cancelAttachmentUpload(attachmentId), - ), - ), - inProgress: (_, __) => Padding( - padding: const EdgeInsets.all(8), - child: _TrailingButton( - icon: StreamSvgIcon( - icon: StreamSvgIcons.close, - color: theme.colorTheme.barsBg, - ), - fillColor: theme.colorTheme.overlayDark, - onPressed: () => channel.cancelAttachmentUpload(attachmentId), - ), - ), - success: () => Padding( - padding: const EdgeInsets.all(8), - child: CircleAvatar( - backgroundColor: theme.colorTheme.accentPrimary, - maxRadius: 12, - child: StreamSvgIcon( - icon: StreamSvgIcons.check, - color: theme.colorTheme.barsBg, - ), - ), - ), - failed: (_) => Padding( - padding: const EdgeInsets.all(8), - child: _TrailingButton( - icon: StreamSvgIcon( - icon: StreamSvgIcons.retry, - color: theme.colorTheme.barsBg, - ), - fillColor: theme.colorTheme.overlayDark, - onPressed: () => channel.retryAttachmentUpload( - message.id, - attachmentId, - ), - ), - ), - ); - } -} - -class _TrailingButton extends StatelessWidget { - const _TrailingButton({ - this.onPressed, - this.fillColor, - this.icon, - }); - - final VoidCallback? onPressed; - final Color? fillColor; - final Widget? icon; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 24, - width: 24, - child: RawMaterialButton( - elevation: 0, - highlightElevation: 0, - focusElevation: 0, - hoverElevation: 0, - onPressed: onPressed, - fillColor: fillColor, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), - child: icon, - ), - ); - } -} - -class _FileAttachmentSubtitle extends StatelessWidget { - const _FileAttachmentSubtitle({ - required this.attachment, - }); - - final Attachment attachment; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - final size = attachment.file?.size ?? attachment.extraData['file_size']; - final textStyle = theme.textTheme.footnote.copyWith( - color: theme.colorTheme.textLowEmphasis, - ); - return attachment.uploadState.when( - preparing: () => Text(fileSize(size), style: textStyle), - inProgress: (sent, total) => StreamUploadProgressIndicator( - uploaded: sent, - total: total, - showBackground: false, - textStyle: textStyle, - progressIndicatorColor: theme.colorTheme.accentPrimary, - ), - success: () => Text(fileSize(size), style: textStyle), - failed: (_) => Text( - context.translations.uploadErrorLabel, - style: textStyle, - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/gallery_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/gallery_attachment.dart deleted file mode 100644 index 22cbf84265..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/gallery_attachment.dart +++ /dev/null @@ -1,268 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/misc/flex_grid.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamGalleryAttachment} -/// Constructs a gallery of images, videos, and gifs from a list of attachments. -/// -/// This widget uses a [FlexGrid] to display the attachments in a grid format. -/// The grid will automatically resize based on the size of the attachment. -/// {@endtemplate} -/// -/// See also: -/// -/// * [StreamImageAttachmentThumbnail], which is used to display the image -/// thumbnails. -/// * [StreamVideoAttachmentThumbnail], which is used to display the video -/// thumbnails. -/// * [StreamGiphyAttachmentThumbnail], which is used to display the gif -/// thumbnails. -class StreamGalleryAttachment extends StatelessWidget { - /// {@macro streamGalleryAttachment} - const StreamGalleryAttachment({ - super.key, - required this.attachments, - required this.message, - this.shape, - this.constraints = const BoxConstraints(), - this.spacing = 2.0, - this.runSpacing = 2.0, - required this.itemBuilder, - }); - - /// List of attachments to show - final List attachments; - - /// The [Message] that the images are attached to - final Message message; - - /// The shape of the attachment. - /// - /// Defaults to [RoundedRectangleBorder] with a radius of 14. - final ShapeBorder? shape; - - /// The constraints of the [attachments] - final BoxConstraints constraints; - - /// How much space to place between children in a run in the main axis. - /// - /// For example, if [spacing] is 10.0, the children will be spaced at least - /// 10.0 logical pixels apart in the main axis. - /// - /// Defaults to 2.0. - final double spacing; - - /// How much space to place between the runs themselves in the cross axis. - /// - /// For example, if [runSpacing] is 10.0, the runs will be spaced at least - /// 10.0 logical pixels apart in the cross axis. - /// - /// Defaults to 2.0. - final double runSpacing; - - /// Item builder for the gallery. - final IndexedWidgetBuilder itemBuilder; - - @override - Widget build(BuildContext context) { - assert( - attachments.length >= 2, - 'Gallery should have at least 2 attachments, found ${attachments.length}', - ); - - final chatTheme = StreamChatTheme.of(context); - final colorTheme = chatTheme.colorTheme; - final shape = this.shape ?? - RoundedRectangleBorder( - side: BorderSide( - color: colorTheme.borders, - strokeAlign: BorderSide.strokeAlignOutside, - ), - borderRadius: BorderRadius.circular(14), - ); - - return Container( - constraints: constraints, - clipBehavior: Clip.hardEdge, - decoration: ShapeDecoration(shape: shape), - // Added a builder just for the sake of calculating the image count - // and building the appropriate layout based on the image count. - child: Builder( - builder: (context) { - final attachmentCount = attachments.length; - if (attachmentCount == 2) { - return _buildForTwo(context, attachments); - } - - if (attachmentCount == 3) { - return _buildForThree(context, attachments); - } - - return _buildForFourOrMore(context, attachments); - }, - ), - ); - } - - Widget _buildForTwo(BuildContext context, List attachments) { - final aspectRatio1 = attachments[0].originalSize?.aspectRatio; - final aspectRatio2 = attachments[1].originalSize?.aspectRatio; - - // check if one image is landscape and other is portrait or vice versa - final isLandscape1 = aspectRatio1 != null && aspectRatio1 > 1; - final isLandscape2 = aspectRatio2 != null && aspectRatio2 > 1; - - // Both the images are landscape. - if (isLandscape1 && isLandscape2) { - // ---------- - // | | - // ---------- - // | | - // ---------- - return FlexGrid( - pattern: const [ - [1], - [1], - ], - spacing: spacing, - runSpacing: runSpacing, - children: [ - itemBuilder(context, 0), - itemBuilder(context, 1), - ], - ); - } - - // Both the images are portrait. - if (!isLandscape1 && !isLandscape2) { - // ----------- - // | | | - // | | | - // | | | - // ----------- - return FlexGrid( - pattern: const [ - [1, 1], - ], - spacing: spacing, - runSpacing: runSpacing, - children: [ - itemBuilder(context, 0), - itemBuilder(context, 1), - ], - ); - } - - // Layout on the basis of isLandscape1. - // 1. True - // ----------- - // | | | - // | | | - // | | | - // ----------- - // - // 2. False - // ----------- - // | | | - // | | | - // | | | - // ----------- - return FlexGrid( - pattern: [ - if (isLandscape1) [2, 1] else [1, 2], - ], - spacing: spacing, - runSpacing: runSpacing, - children: [ - itemBuilder(context, 0), - itemBuilder(context, 1), - ], - ); - } - - Widget _buildForThree(BuildContext context, List attachments) { - final aspectRatio1 = attachments[0].originalSize?.aspectRatio; - final isLandscape1 = aspectRatio1 != null && aspectRatio1 > 1; - - // We layout on the basis of isLandscape1. - // 1. True - // ----------- - // | | - // | | - // |---------| - // | | | - // | | | - // ----------- - // - // 2. False - // ----------- - // | | | - // | | | - // | |----| - // | | | - // | | | - // ----------- - return FlexGrid( - pattern: const [ - [1], - [1, 1], - ], - spacing: spacing, - runSpacing: runSpacing, - reverse: !isLandscape1, - children: [ - itemBuilder(context, 0), - itemBuilder(context, 1), - itemBuilder(context, 2), - ], - ); - } - - Widget _buildForFourOrMore( - BuildContext context, List attachments) { - final pattern = >[]; - final children = []; - - for (var i = 0; i < attachments.length; i++) { - if (i.isEven) { - pattern.add([1]); - } else { - pattern.last.add(1); - } - - children.add(itemBuilder(context, i)); - } - - // ----------- - // | | | - // | | | - // ------------ - // | | | - // | | | - // ------------ - return FlexGrid( - pattern: pattern, - maxChildren: 4, - spacing: spacing, - runSpacing: runSpacing, - children: children, - overlayBuilder: (context, remaining) { - return IgnorePointer( - child: ColoredBox( - color: Colors.black38, - child: Center( - child: Text( - '+$remaining', - style: const TextStyle( - fontSize: 26, - color: Colors.white, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - ); - }, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart deleted file mode 100644 index e370cd00bf..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart +++ /dev/null @@ -1,105 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/misc/giphy_chip.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamGiphyAttachment} -/// Shows a GIF attachment in a [StreamMessageWidget]. -/// {@endtemplate} -class StreamGiphyAttachment extends StatelessWidget { - /// {@macro streamGiphyAttachment} - const StreamGiphyAttachment({ - super.key, - required this.message, - required this.giphy, - this.type = GiphyInfoType.original, - this.shape, - this.constraints = const BoxConstraints(), - }); - - /// The [Message] that the giphy is attached to. - final Message message; - - /// The [Attachment] object containing the giphy information. - final Attachment giphy; - - /// The type of giphy to display. - /// - /// Defaults to [GiphyInfoType.fixedHeight]. - final GiphyInfoType type; - - /// The shape of the attachment. - /// - /// Defaults to [RoundedRectangleBorder] with a radius of 14. - final ShapeBorder? shape; - - /// The constraints to use when displaying the giphy. - final BoxConstraints constraints; - - @override - Widget build(BuildContext context) { - BoxFit? fit; - final giphyInfo = giphy.giphyInfo(type); - - Size? giphySize; - if (giphyInfo != null) { - giphySize = Size(giphyInfo.width, giphyInfo.height); - } - - // If attachment size is available, we will tighten the constraints max - // size to the attachment size. - var constraints = this.constraints; - if (giphySize != null) { - constraints = constraints.tightenMaxSize(giphySize); - } else { - // For backward compatibility, we will fill the available space if the - // attachment size is not available. - fit = BoxFit.cover; - } - - final chatTheme = StreamChatTheme.of(context); - final colorTheme = chatTheme.colorTheme; - final shape = this.shape ?? - RoundedRectangleBorder( - side: BorderSide( - color: colorTheme.borders, - strokeAlign: BorderSide.strokeAlignOutside, - ), - borderRadius: BorderRadius.circular(14), - ); - - return Container( - constraints: constraints, - clipBehavior: Clip.hardEdge, - decoration: ShapeDecoration(shape: shape), - child: AspectRatio( - aspectRatio: giphySize?.aspectRatio ?? 1, - child: Stack( - alignment: Alignment.center, - children: [ - StreamGiphyAttachmentThumbnail( - type: type, - giphy: giphy, - fit: fit, - width: double.infinity, - height: double.infinity, - ), - if (giphy.uploadState.isSuccess) - const Positioned( - bottom: 8, - left: 8, - child: GiphyChip(), - ) - else - Padding( - padding: const EdgeInsets.all(8), - child: StreamAttachmentUploadStateBuilder( - message: message, - attachment: giphy, - ), - ), - ], - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/handler/common.dart b/packages/stream_chat_flutter/lib/src/attachment/handler/common.dart deleted file mode 100644 index 8c58fe2dc3..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/handler/common.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'dart:typed_data'; - -import 'package:dio/dio.dart'; -import 'package:file_selector/file_selector.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// Represents the url and bytes of an attachment. -class AttachmentData { - /// Creates a new [AttachmentData] instance. - const AttachmentData({ - required this.bytes, - required this.downloadUrl, - required this.fileName, - this.mimeType, - }); - - /// The data downloaded from the [downloadUrl]. - final Uint8List bytes; - - /// The url of the attachment that was used to download the [bytes]. - final String downloadUrl; - - /// The name of the file to use when saving the [bytes]. - final String fileName; - - /// The mime type of the attachment. - final String? mimeType; - - /// Creates an [XFile] from the [AttachmentData]. - XFile toXFile({String? path}) { - return XFile.fromData( - bytes, - mimeType: mimeType, - name: fileName, - path: path, - ); - } -} - -/// Downloads the [attachment] and returns the [AttachmentData]. -Future downloadAttachmentData( - Attachment attachment, { - ProgressCallback? onReceiveProgress, - Map? queryParameters, - CancelToken? cancelToken, - Options? options, -}) async { - final type = attachment.type; - - String? downloadUrl; - String? fileName; - /* ---IMAGES/GIFS--- */ - if (type == AttachmentType.image) { - downloadUrl = attachment.imageUrl ?? attachment.assetUrl; - fileName = attachment.title; - fileName ??= 'attachment.${attachment.mimeType ?? 'png'}'; - } - /* ---GIPHY's--- */ - else if (type == AttachmentType.giphy) { - downloadUrl = attachment.thumbUrl; - fileName = '${attachment.title}.gif'; - } - /* ---FILES AND VIDEOS--- */ - else if (type == AttachmentType.file || type == AttachmentType.video) { - downloadUrl = attachment.assetUrl; - fileName = attachment.title; - } - - if (downloadUrl == null) { - throw ArgumentError( - 'Attachment must have an assetUrl or imageUrl or thumbUrl', - ); - } - - final response = await Dio().get>( - downloadUrl, - onReceiveProgress: onReceiveProgress, - queryParameters: queryParameters, - cancelToken: cancelToken, - // set responseType to `bytes` - options: options?.copyWith(responseType: ResponseType.bytes) ?? - Options(responseType: ResponseType.bytes), - ); - - final bytes = Uint8List.fromList(response.data!); - - return AttachmentData( - bytes: bytes, - downloadUrl: downloadUrl, - fileName: fileName!, - mimeType: attachment.mimeType, - ); -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler.dart b/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler.dart deleted file mode 100644 index 36d45725f5..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler.dart +++ /dev/null @@ -1,4 +0,0 @@ -export 'stream_attachment_handler_base.dart' - if (dart.library.html) 'stream_attachment_handler_html.dart' - if (dart.library.io) 'stream_attachment_handler_io.dart' - show StreamAttachmentHandler; diff --git a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_base.dart b/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_base.dart deleted file mode 100644 index 7879f63644..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_base.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'package:file_picker/file_picker.dart'; -import 'package:image_picker/image_picker.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// Base class for handling attachment related functionality. -abstract class StreamAttachmentHandlerBase { - /// Pick an image from the device. - Future pickImage({ - required ImageSource source, - double? maxWidth, - double? maxHeight, - int? imageQuality, - CameraDevice preferredCameraDevice = CameraDevice.rear, - }) { - throw UnimplementedError('pickImage is not implemented'); - } - - /// Pick a video from the device. - Future pickVideo({ - required ImageSource source, - CameraDevice preferredCameraDevice = CameraDevice.rear, - Duration? maxDuration, - }) { - throw UnimplementedError('pickVideo is not implemented'); - } - - /// Pick a file from the device. - Future pickFile({ - String? dialogTitle, - String? initialDirectory, - FileType type = FileType.any, - List? allowedExtensions, - Function(FilePickerStatus)? onFileLoading, - bool allowCompression = true, - bool withData = true, - bool withReadStream = false, - bool lockParentWindow = true, - }) { - throw UnimplementedError('pickFile is not implemented'); - } - - /// Pick an audio from the device. - Future pickAudio() { - throw UnimplementedError('pickAudio is not implemented'); - } - - /// Saves the [attachmentFile] to the temporary directory. - Future saveAttachmentFile({ - required AttachmentFile attachmentFile, - }) { - throw UnimplementedError('saveFile is not implemented'); - } - - /// Deletes the [attachmentFile] from the temporary directory. - Future deleteAttachmentFile({ - required AttachmentFile attachmentFile, - }) { - throw UnimplementedError('deleteAttachmentFile is not implemented'); - } - - /// Downloads the [attachment] to the device and returns - /// the path to the file. - Future downloadAttachment( - Attachment attachment, { - ProgressCallback? onReceiveProgress, - Map? queryParameters, - CancelToken? cancelToken, - Options? options, - }) { - throw UnimplementedError('downloadAttachment is not implemented'); - } -} - -/// Stub implementation of [StreamAttachmentHandlerBase]. -class StreamAttachmentHandler extends StreamAttachmentHandlerBase { - /// Returns an instance of [StreamAttachmentHandler]. - static StreamAttachmentHandler get instance { - throw UnimplementedError('instance is not implemented'); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_html.dart b/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_html.dart deleted file mode 100644 index b3d4b12171..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_html.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:file_picker/file_picker.dart'; -import 'package:stream_chat_flutter/src/attachment/handler/common.dart'; -import 'package:stream_chat_flutter/src/attachment/handler/stream_attachment_handler_base.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// StreamAttachmentHandler implementation for html. -class StreamAttachmentHandler extends StreamAttachmentHandlerBase { - StreamAttachmentHandler._(); - - static StreamAttachmentHandler? _instance; - - /// Returns the singleton instance of [StreamAttachmentHandler]. - // ignore: prefer_constructors_over_static_methods - static StreamAttachmentHandler get instance => - _instance ??= StreamAttachmentHandler._(); - - late final _filePicker = FilePicker.platform; - - @override - Future pickFile({ - String? dialogTitle, - String? initialDirectory, - FileType type = FileType.any, - List? allowedExtensions, - Function(FilePickerStatus)? onFileLoading, - bool allowCompression = true, - bool withData = true, - bool withReadStream = false, - bool lockParentWindow = true, - }) async { - final result = await _filePicker.pickFiles( - dialogTitle: dialogTitle, - initialDirectory: initialDirectory, - type: type, - allowedExtensions: allowedExtensions, - onFileLoading: onFileLoading, - allowCompression: allowCompression, - withData: withData, - withReadStream: withReadStream, - lockParentWindow: lockParentWindow, - ); - - return result?.files.first.toAttachment(type: type.toAttachmentType()); - } - - @override - Future downloadAttachment( - Attachment attachment, { - ProgressCallback? onReceiveProgress, - Map? queryParameters, - CancelToken? cancelToken, - Options? options, - }) async { - final data = await downloadAttachmentData( - attachment, - onReceiveProgress: onReceiveProgress, - queryParameters: queryParameters, - cancelToken: cancelToken, - options: options, - ); - - // Create an XFile for proper file saving. - final file = data.toXFile(); - - // Save the file. We are not using the path parameter because it is not - // supported on web. - await file.saveTo(''); - return null; - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart b/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart deleted file mode 100644 index 69d17385cb..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/handler/stream_attachment_handler_io.dart +++ /dev/null @@ -1,216 +0,0 @@ -import 'dart:io'; - -import 'package:file_picker/file_picker.dart'; -import 'package:file_selector/file_selector.dart'; -import 'package:gal/gal.dart'; -import 'package:image_picker/image_picker.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:stream_chat_flutter/src/attachment/handler/common.dart'; -import 'package:stream_chat_flutter/src/attachment/handler/stream_attachment_handler_base.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// StreamAttachmentHandler implementation for desktop. -class StreamAttachmentHandlerDesktop extends StreamAttachmentHandler { - /// Returns the singleton instance of [StreamAttachmentHandler]. - StreamAttachmentHandlerDesktop() : super.__(); - - @override - Future downloadAttachment( - Attachment attachment, { - ProgressCallback? onReceiveProgress, - Map? queryParameters, - CancelToken? cancelToken, - Options? options, - }) async { - final data = await downloadAttachmentData( - attachment, - onReceiveProgress: onReceiveProgress, - queryParameters: queryParameters, - cancelToken: cancelToken, - options: options, - ); - - // Open the native file browser so the user can select the save location. - final saveLocation = await getSaveLocation(suggestedName: data.fileName); - if (saveLocation == null) { - // Operation was canceled by the user. - return null; - } - - // Get the path to the user's selected location. - final path = saveLocation.path; - - // Create an XFile for proper file saving. - final file = data.toXFile(path: path); - - // Save the file to the user's selected path. - await file.saveTo(path); - return path; - } -} - -/// StreamAttachmentHandler implementation for io. -class StreamAttachmentHandler extends StreamAttachmentHandlerBase { - StreamAttachmentHandler.__(); - - factory StreamAttachmentHandler._() { - if (isDesktopDevice) { - return StreamAttachmentHandlerDesktop(); - } - return StreamAttachmentHandler.__(); - } - - static StreamAttachmentHandler? _instance; - - /// Returns the singleton instance of [StreamAttachmentHandler]. - // ignore: prefer_constructors_over_static_methods - static StreamAttachmentHandler get instance => - _instance ??= StreamAttachmentHandler._(); - - late final _imagePicker = ImagePicker(); - late final _filePicker = FilePicker.platform; - - @override - Future pickImage({ - required ImageSource source, - double? maxWidth, - double? maxHeight, - int? imageQuality, - CameraDevice preferredCameraDevice = CameraDevice.rear, - }) async { - final image = await _imagePicker.pickImage( - source: source, - maxWidth: maxWidth, - maxHeight: maxHeight, - imageQuality: imageQuality, - preferredCameraDevice: preferredCameraDevice, - ); - - return image?.toAttachment(type: 'image'); - } - - @override - Future pickVideo({ - required ImageSource source, - CameraDevice preferredCameraDevice = CameraDevice.rear, - Duration? maxDuration, - }) async { - final video = await _imagePicker.pickVideo( - source: source, - preferredCameraDevice: preferredCameraDevice, - maxDuration: maxDuration, - ); - - return video?.toAttachment(type: 'video'); - } - - @override - Future pickFile({ - String? dialogTitle, - String? initialDirectory, - FileType type = FileType.any, - List? allowedExtensions, - Function(FilePickerStatus)? onFileLoading, - bool allowCompression = true, - bool withData = true, - bool withReadStream = false, - bool lockParentWindow = true, - }) async { - final result = await _filePicker.pickFiles( - dialogTitle: dialogTitle, - initialDirectory: initialDirectory, - type: type, - allowedExtensions: allowedExtensions, - onFileLoading: onFileLoading, - allowCompression: allowCompression, - withData: withData, - withReadStream: withReadStream, - lockParentWindow: lockParentWindow, - ); - - return result?.files.first.toAttachment(type: type.toAttachmentType()); - } - - @override - Future saveAttachmentFile({ - required AttachmentFile attachmentFile, - }) async { - final fileName = attachmentFile.name; - assert(fileName != null, 'Attachment file name is required'); - - final tempDir = await getTemporaryDirectory(); - final tempPath = Uri.file(tempDir.path, windows: CurrentPlatform.isWindows); - final tempFilePath = tempPath - .resolve(fileName!) - .toFilePath(windows: CurrentPlatform.isWindows); - - final attachmentFileBytes = attachmentFile.bytes; - if (attachmentFileBytes == null) { - final attachmentFilePath = attachmentFile.path!; - final file = File(attachmentFilePath); - return file.copy(tempFilePath).then((it) => it.path); - } else { - final file = File(tempFilePath); - return file.writeAsBytes(attachmentFileBytes).then((it) => it.path); - } - } - - @override - Future deleteAttachmentFile({ - required AttachmentFile attachmentFile, - }) async { - final attachmentFilePath = attachmentFile.path; - if (attachmentFilePath != null) { - final file = File(attachmentFilePath); - if (file.existsSync()) { - await file.delete(); - } - } - } - - @override - Future downloadAttachment( - Attachment attachment, { - ProgressCallback? onReceiveProgress, - Map? queryParameters, - CancelToken? cancelToken, - Options? options, - }) async { - final data = await downloadAttachmentData( - attachment, - onReceiveProgress: onReceiveProgress, - queryParameters: queryParameters, - cancelToken: cancelToken, - options: options, - ); - - final appDir = await getTemporaryDirectory(); - final ext = Uri.parse(data.downloadUrl).pathSegments.last; - final path = '${appDir.path}/${attachment.id}.$ext'; - - // Create an XFile for proper file saving. - final file = data.toXFile(path: path); - - // Save the file to a temporary location. - await file.saveTo(path); - - // Now that the file is saved, we need to copy it to the user's gallery - // because the gallery only shows files that are in the gallery folder. - await Gal.putImage(path); - - // Once the file is copied to the gallery, we can delete the temporary file. - await file.delete(); - - return path; - } -} - -extension on XFile { - /// Deletes this xfile from the file system. - Future delete() async { - final file = File(path); - if (file.existsSync()) { - await file.delete(); - } - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/image_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/image_attachment.dart deleted file mode 100644 index 8824bdec7a..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/image_attachment.dart +++ /dev/null @@ -1,104 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamImageAttachment} -/// Shows an image attachment in a [StreamMessageWidget]. -/// {@endtemplate} -class StreamImageAttachment extends StatelessWidget { - /// {@macro streamImageAttachment} - const StreamImageAttachment({ - super.key, - required this.message, - required this.image, - this.shape, - this.constraints = const BoxConstraints(), - this.imageThumbnailSize = const Size(400, 400), - this.imageThumbnailResizeType = 'clip', - this.imageThumbnailCropType = 'center', - }); - - /// The [Message] that the image is attached to. - final Message message; - - /// The [Attachment] object containing the image information. - final Attachment image; - - /// The shape of the attachment. - /// - /// Defaults to [RoundedRectangleBorder] with a radius of 14. - final ShapeBorder? shape; - - /// The constraints to use when displaying the image. - final BoxConstraints constraints; - - /// Size of the attachment image thumbnail. - final Size imageThumbnailSize; - - /// Resize type of the image attachment thumbnail. - /// - /// Defaults to [crop] - final String /*clip|crop|scale|fill*/ imageThumbnailResizeType; - - /// Crop type of the image attachment thumbnail. - /// - /// Defaults to [center] - final String /*center|top|bottom|left|right*/ imageThumbnailCropType; - - @override - Widget build(BuildContext context) { - BoxFit? fit; - final imageSize = image.originalSize; - - // If attachment size is available, we will tighten the constraints max - // size to the attachment size. - var constraints = this.constraints; - if (imageSize != null) { - constraints = constraints.tightenMaxSize(imageSize); - } else { - // For backward compatibility, we will fill the available space if the - // attachment size is not available. - fit = BoxFit.cover; - } - - final chatTheme = StreamChatTheme.of(context); - final colorTheme = chatTheme.colorTheme; - final shape = this.shape ?? - RoundedRectangleBorder( - side: BorderSide( - color: colorTheme.borders, - strokeAlign: BorderSide.strokeAlignOutside, - ), - borderRadius: BorderRadius.circular(14), - ); - - return Container( - constraints: constraints, - clipBehavior: Clip.hardEdge, - decoration: ShapeDecoration(shape: shape), - child: AspectRatio( - aspectRatio: imageSize?.aspectRatio ?? 1, - child: Stack( - alignment: Alignment.center, - children: [ - StreamImageAttachmentThumbnail( - image: image, - fit: fit, - width: double.infinity, - height: double.infinity, - thumbnailSize: imageThumbnailSize, - thumbnailResizeType: imageThumbnailResizeType, - thumbnailCropType: imageThumbnailCropType, - ), - Padding( - padding: const EdgeInsets.all(8), - child: StreamAttachmentUploadStateBuilder( - message: message, - attachment: image, - ), - ), - ], - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/stream_attachment_package.dart b/packages/stream_chat_flutter/lib/src/attachment/stream_attachment_package.dart deleted file mode 100644 index e1e0df0e83..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/stream_attachment_package.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// The [StreamAttachmentPackage] class is basically meant to wrap -/// individual attachments with their corresponding message -class StreamAttachmentPackage { - /// Default constructor to prepare an [StreamAttachmentPackage] object - StreamAttachmentPackage({ - required this.attachment, - required this.message, - }); - - /// This is the individual attachment - final Attachment attachment; - - /// This is the message that the attachment belongs to - /// The message object may have attachment(s) other than the one packaged - final Message message; -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/thumbnail/file_attachment_thumbnail.dart b/packages/stream_chat_flutter/lib/src/attachment/thumbnail/file_attachment_thumbnail.dart deleted file mode 100644 index b6f14b291c..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/thumbnail/file_attachment_thumbnail.dart +++ /dev/null @@ -1,72 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/attachment/thumbnail/image_attachment_thumbnail.dart'; -import 'package:stream_chat_flutter/src/attachment/thumbnail/thumbnail_error.dart'; -import 'package:stream_chat_flutter/src/attachment/thumbnail/video_attachment_thumbnail.dart'; -import 'package:stream_chat_flutter/src/utils/helpers.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// {@template streamFileAttachmentThumbnail} -/// Widget for building file attachment thumbnail. -/// -/// This widget first tries to build an image thumbnail for the file attachment. -/// If the image thumbnail fails to load, it tries to build a video thumbnail. -/// If the video thumbnail fails to load, it returns a generic file type icon. -/// {@endtemplate} -class StreamFileAttachmentThumbnail extends StatelessWidget { - /// {@macro streamFileAttachmentThumbnail} - const StreamFileAttachmentThumbnail({ - super.key, - required this.file, - this.width, - this.height, - this.fit, - this.errorBuilder = _defaultErrorBuilder, - }); - - /// The file attachment to build the thumbnail for. - final Attachment file; - - /// The width of the thumbnail. - final double? width; - - /// The height of the thumbnail. - final double? height; - - /// How to inscribe the thumbnail into the space allocated during layout. - final BoxFit? fit; - - /// Builder used when the thumbnail fails to load. - final ThumbnailErrorBuilder errorBuilder; - - // Default error builder for file attachment thumbnail. - static Widget _defaultErrorBuilder( - BuildContext context, - Object error, - StackTrace? stackTrace, - ) { - // Return a generic file type icon. - return getFileTypeImage(); - } - - @override - Widget build(BuildContext context) { - final mediaType = file.title?.mediaType; - - return switch (mediaType?.type) { - AttachmentType.image => StreamImageAttachmentThumbnail( - image: file, - width: width, - height: height, - fit: fit, - ), - AttachmentType.video => StreamVideoAttachmentThumbnail( - video: file, - width: width, - height: height, - fit: fit, - ), - // Return a generic file type icon. - _ => getFileTypeImage(mediaType?.mimeType), - }; - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/thumbnail/giphy_attachment_thumbnail.dart b/packages/stream_chat_flutter/lib/src/attachment/thumbnail/giphy_attachment_thumbnail.dart deleted file mode 100644 index c38e2800e8..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/thumbnail/giphy_attachment_thumbnail.dart +++ /dev/null @@ -1,103 +0,0 @@ -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:flutter/material.dart'; -import 'package:shimmer/shimmer.dart'; -import 'package:stream_chat_flutter/src/attachment/thumbnail/image_attachment_thumbnail.dart'; -import 'package:stream_chat_flutter/src/attachment/thumbnail/thumbnail_error.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// {@template giphyAttachmentThumbnail} -/// Widget for building giphy attachment thumbnail. -/// -/// This widget is used when the [Attachment.type] is [AttachmentType.giphy]. -/// {@endtemplate} -class StreamGiphyAttachmentThumbnail extends StatelessWidget { - /// {@macro giphyAttachmentThumbnail} - const StreamGiphyAttachmentThumbnail({ - super.key, - required this.giphy, - this.type = GiphyInfoType.original, - this.width, - this.height, - this.fit, - this.errorBuilder = _defaultErrorBuilder, - }); - - /// The giphy attachment to build the thumbnail for. - final Attachment giphy; - - /// The type of giphy thumbnail to build. - final GiphyInfoType type; - - /// The width of the thumbnail. - final double? width; - - /// The height of the thumbnail. - final double? height; - - /// How to inscribe the thumbnail into the space allocated during layout. - final BoxFit? fit; - - /// Builder used when the thumbnail fails to load. - final ThumbnailErrorBuilder errorBuilder; - - // Default error builder for image attachment thumbnail. - static Widget _defaultErrorBuilder( - BuildContext context, - Object error, - StackTrace? stackTrace, - ) { - return ThumbnailError( - error: error, - stackTrace: stackTrace, - height: double.infinity, - width: double.infinity, - fit: BoxFit.cover, - ); - } - - @override - Widget build(BuildContext context) { - // If the giphy info is not available, use the image attachment thumbnail - // instead. - final info = giphy.giphyInfo(type); - if (info == null) { - return StreamImageAttachmentThumbnail( - image: giphy, - width: width, - height: height, - fit: fit, - ); - } - - return CachedNetworkImage( - imageUrl: info.url, - width: width, - height: height, - fit: fit, - placeholder: (context, __) { - final image = Image.asset( - 'lib/assets/images/placeholder.png', - width: width, - height: height, - fit: BoxFit.cover, - package: 'stream_chat_flutter', - ); - - final colorTheme = StreamChatTheme.of(context).colorTheme; - return Shimmer.fromColors( - baseColor: colorTheme.disabled, - highlightColor: colorTheme.inputBg, - child: image, - ); - }, - errorWidget: (context, url, error) { - return errorBuilder( - context, - error, - StackTrace.current, - ); - }, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/thumbnail/image_attachment_thumbnail.dart b/packages/stream_chat_flutter/lib/src/attachment/thumbnail/image_attachment_thumbnail.dart deleted file mode 100644 index 39f34ce8ac..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/thumbnail/image_attachment_thumbnail.dart +++ /dev/null @@ -1,211 +0,0 @@ -import 'dart:io' show File; - -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:flutter/material.dart'; -import 'package:shimmer/shimmer.dart'; -import 'package:stream_chat_flutter/src/attachment/thumbnail/thumbnail_error.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter/src/utils/utils.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// {@template imageAttachmentThumbnail} -/// Widget for building image attachment thumbnail. -/// -/// This widget is used when the [Attachment.type] is [AttachmentType.image]. -/// {@endtemplate} -class StreamImageAttachmentThumbnail extends StatelessWidget { - /// {@macro imageAttachmentThumbnail} - const StreamImageAttachmentThumbnail({ - super.key, - required this.image, - this.width, - this.height, - this.fit, - this.thumbnailSize, - this.thumbnailResizeType = 'clip', - this.thumbnailCropType = 'center', - this.errorBuilder = _defaultErrorBuilder, - }); - - /// The image attachment to show. - final Attachment image; - - /// Width of the attachment image thumbnail. - final double? width; - - /// Height of the attachment image thumbnail. - final double? height; - - /// Fit of the attachment image thumbnail. - final BoxFit? fit; - - /// Size of the attachment image thumbnail. - final Size? thumbnailSize; - - /// Resize type of the image attachment thumbnail. - /// - /// Defaults to [crop] - final String /*clip|crop|scale|fill*/ thumbnailResizeType; - - /// Crop type of the image attachment thumbnail. - /// - /// Defaults to [center] - final String /*center|top|bottom|left|right*/ thumbnailCropType; - - /// Builder used when the thumbnail fails to load. - final ThumbnailErrorBuilder errorBuilder; - - // Default error builder for image attachment thumbnail. - static Widget _defaultErrorBuilder( - BuildContext context, - Object error, - StackTrace? stackTrace, - ) { - return ThumbnailError( - error: error, - stackTrace: stackTrace, - height: double.infinity, - width: double.infinity, - fit: BoxFit.cover, - ); - } - - @override - Widget build(BuildContext context) { - final file = image.file; - if (file != null) { - return _LocalImageAttachment( - file: file, - width: width, - height: height, - fit: fit, - errorBuilder: errorBuilder, - ); - } - - var imageUrl = image.thumbUrl ?? image.imageUrl ?? image.assetUrl; - if (imageUrl != null) { - final thumbnailSize = this.thumbnailSize; - if (thumbnailSize != null) { - imageUrl = imageUrl.getResizedImageUrl( - width: thumbnailSize.width, - height: thumbnailSize.height, - resize: thumbnailResizeType, - crop: thumbnailCropType, - ); - } - - return _RemoteImageAttachment( - url: imageUrl, - width: width, - height: height, - fit: fit, - errorBuilder: errorBuilder, - ); - } - - // Return error widget if no image is found. - return errorBuilder( - context, - 'Image attachment is not valid', - StackTrace.current, - ); - } -} - -class _LocalImageAttachment extends StatelessWidget { - const _LocalImageAttachment({ - required this.file, - required this.errorBuilder, - this.width, - this.height, - this.fit, - }); - - final AttachmentFile file; - final double? width; - final double? height; - final BoxFit? fit; - final ThumbnailErrorBuilder errorBuilder; - - @override - Widget build(BuildContext context) { - final bytes = file.bytes; - if (bytes != null) { - return Image.memory( - bytes, - width: width, - height: height, - fit: fit, - errorBuilder: errorBuilder, - ); - } - - final path = file.path; - if (path != null) { - return Image.file( - File(path), - width: width, - height: height, - fit: fit, - errorBuilder: errorBuilder, - ); - } - - // Return error widget if no image is found. - return errorBuilder( - context, - 'Image attachment is not valid', - StackTrace.current, - ); - } -} - -class _RemoteImageAttachment extends StatelessWidget { - const _RemoteImageAttachment({ - required this.url, - required this.errorBuilder, - this.width, - this.height, - this.fit, - }); - - final String url; - final double? width; - final double? height; - final BoxFit? fit; - final ThumbnailErrorBuilder errorBuilder; - - @override - Widget build(BuildContext context) { - return CachedNetworkImage( - imageUrl: url, - width: width, - height: height, - fit: fit, - placeholder: (context, __) { - final image = Image.asset( - 'lib/assets/images/placeholder.png', - width: width, - height: height, - fit: BoxFit.cover, - package: 'stream_chat_flutter', - ); - - final colorTheme = StreamChatTheme.of(context).colorTheme; - return Shimmer.fromColors( - baseColor: colorTheme.disabled, - highlightColor: colorTheme.inputBg, - child: image, - ); - }, - errorWidget: (context, url, error) { - return errorBuilder( - context, - error, - StackTrace.current, - ); - }, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/thumbnail/media_attachment_thumbnail.dart b/packages/stream_chat_flutter/lib/src/attachment/thumbnail/media_attachment_thumbnail.dart deleted file mode 100644 index ab8c60c571..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/thumbnail/media_attachment_thumbnail.dart +++ /dev/null @@ -1,131 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/attachment/thumbnail/giphy_attachment_thumbnail.dart'; -import 'package:stream_chat_flutter/src/attachment/thumbnail/image_attachment_thumbnail.dart'; -import 'package:stream_chat_flutter/src/attachment/thumbnail/thumbnail_error.dart'; -import 'package:stream_chat_flutter/src/attachment/thumbnail/video_attachment_thumbnail.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// {@template mediaAttachmentThumbnail} -/// Widget for building media attachment thumbnail. -/// -/// This widget is used when the [Attachment.type] is [AttachmentType.image], -/// [AttachmentType.video] or [AttachmentType.giphy]. -/// -/// see also: -/// * [StreamImageAttachmentThumbnail] -/// * [StreamVideoAttachmentThumbnail] -/// * [StreamGiphyAttachmentThumbnail] -/// {@endtemplate} -class StreamMediaAttachmentThumbnail extends StatelessWidget { - /// {@macro mediaAttachmentThumbnail} - const StreamMediaAttachmentThumbnail({ - super.key, - required this.media, - this.width, - this.height, - this.fit, - this.thumbnailSize, - this.thumbnailResizeType = 'clip', - this.thumbnailCropType = 'center', - this.gifInfoType = GiphyInfoType.original, - this.errorBuilder = _defaultErrorBuilder, - }); - - /// The giphy attachment to build the thumbnail for. - final Attachment media; - - /// The width of the thumbnail. - final double? width; - - /// The height of the thumbnail. - final double? height; - - /// How to inscribe the thumbnail into the space allocated during layout. - final BoxFit? fit; - - /// Builder used when the thumbnail fails to load. - final ThumbnailErrorBuilder errorBuilder; - - /// Size of the attachment image thumbnail. - /// - /// Ignored if the [Attachment.type] is not [AttachmentType.image]. - final Size? thumbnailSize; - - /// Resize type of the image attachment thumbnail. - /// - /// Defaults to [crop] - /// - /// Ignored if the [Attachment.type] is not [AttachmentType.image]. - final String /*clip|crop|scale|fill*/ thumbnailResizeType; - - /// Crop type of the image attachment thumbnail. - /// - /// Defaults to [center] - /// - /// Ignored if the [Attachment.type] is not [AttachmentType.image]. - final String /*center|top|bottom|left|right*/ thumbnailCropType; - - /// The type of giphy thumbnail to build. - /// - /// Ignored if the [Attachment.type] is not [AttachmentType.giphy]. - final GiphyInfoType gifInfoType; - - // Default error builder for image attachment thumbnail. - static Widget _defaultErrorBuilder( - BuildContext context, - Object error, - StackTrace? stackTrace, - ) { - return ThumbnailError( - error: error, - stackTrace: stackTrace, - height: double.infinity, - width: double.infinity, - fit: BoxFit.cover, - ); - } - - @override - Widget build(BuildContext context) { - final type = media.type; - if (type == AttachmentType.image) { - return StreamImageAttachmentThumbnail( - image: media, - width: width, - height: height, - fit: fit, - thumbnailSize: thumbnailSize, - thumbnailResizeType: thumbnailResizeType, - thumbnailCropType: thumbnailCropType, - errorBuilder: errorBuilder, - ); - } - - if (type == AttachmentType.giphy) { - return StreamGiphyAttachmentThumbnail( - giphy: media, - width: width, - height: height, - fit: fit, - type: gifInfoType, - errorBuilder: errorBuilder, - ); - } - - if (type == AttachmentType.video) { - return StreamVideoAttachmentThumbnail( - video: media, - width: width, - height: height, - fit: fit, - errorBuilder: errorBuilder, - ); - } - - return errorBuilder( - context, - 'Unsupported attachment type: $type', - StackTrace.current, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/thumbnail/thumbnail_error.dart b/packages/stream_chat_flutter/lib/src/attachment/thumbnail/thumbnail_error.dart deleted file mode 100644 index d5f5a4ae56..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/thumbnail/thumbnail_error.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:flutter/material.dart'; - -/// {@template thumbnailErrorBuilder} -/// Signature for the builder callback used by [ThumbnailError.builder]. -/// -/// The parameters represent the [BuildContext], [error] and [stackTrace] of the -/// error that triggered this callback. -/// {@endtemplate} -typedef ThumbnailErrorBuilder = Widget Function( - BuildContext context, - Object error, - StackTrace? stackTrace, -); - -/// {@template thumbnailError} -/// A widget that shows an error state when a thumbnail fails to load. -/// {@endtemplate} -class ThumbnailError extends StatelessWidget { - /// {@macro thumbnailError} - const ThumbnailError({ - super.key, - required this.error, - this.stackTrace, - this.width, - this.height, - this.fit, - }); - - /// The width of the thumbnail. - final double? width; - - /// The height of the thumbnail. - final double? height; - - /// How to inscribe the thumbnail into the space allocated during layout. - final BoxFit? fit; - - /// The error that triggered this error widget. - final Object error; - - /// The stack trace of the error that triggered this error widget. - final StackTrace? stackTrace; - - @override - Widget build(BuildContext context) { - return Image.asset( - 'lib/assets/images/placeholder.png', - width: width, - height: height, - fit: fit, - package: 'stream_chat_flutter', - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/thumbnail/video_attachment_thumbnail.dart b/packages/stream_chat_flutter/lib/src/attachment/thumbnail/video_attachment_thumbnail.dart deleted file mode 100644 index 6e3c71aa2f..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/thumbnail/video_attachment_thumbnail.dart +++ /dev/null @@ -1,129 +0,0 @@ -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:flutter/material.dart'; -import 'package:shimmer/shimmer.dart'; -import 'package:stream_chat_flutter/src/attachment/thumbnail/thumbnail_error.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter/src/video/video_thumbnail_image.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// {@template videoAttachmentThumbnail} -/// Widget for building video attachment thumbnail. -/// -/// This widget is used when the [Attachment.type] is [AttachmentType.video]. -/// {@endtemplate} -class StreamVideoAttachmentThumbnail extends StatelessWidget { - /// {@macro videoAttachmentThumbnail} - const StreamVideoAttachmentThumbnail({ - super.key, - required this.video, - this.width, - this.height, - this.fit, - this.errorBuilder = _defaultErrorBuilder, - }); - - /// The video attachment to build the thumbnail for. - final Attachment video; - - /// The width of the thumbnail. - final double? width; - - /// The height of the thumbnail. - final double? height; - - /// How to inscribe the thumbnail into the space allocated during layout. - final BoxFit? fit; - - /// Builder used when the thumbnail fails to load. - final ThumbnailErrorBuilder errorBuilder; - - // Default error builder for image attachment thumbnail. - static Widget _defaultErrorBuilder( - BuildContext context, - Object error, - StackTrace? stackTrace, - ) { - return ThumbnailError( - error: error, - stackTrace: stackTrace, - height: double.infinity, - width: double.infinity, - fit: BoxFit.cover, - ); - } - - @override - Widget build(BuildContext context) { - final thumbUrl = video.thumbUrl; - if (thumbUrl != null) { - return CachedNetworkImage( - imageUrl: thumbUrl, - width: width, - height: height, - fit: fit, - placeholder: (context, __) { - final image = Image.asset( - 'lib/assets/images/placeholder.png', - width: width, - height: height, - fit: BoxFit.cover, - package: 'stream_chat_flutter', - ); - - final colorTheme = StreamChatTheme.of(context).colorTheme; - return Shimmer.fromColors( - baseColor: colorTheme.disabled, - highlightColor: colorTheme.inputBg, - child: image, - ); - }, - errorWidget: (context, url, error) { - return errorBuilder( - context, - error, - StackTrace.current, - ); - }, - ); - } - - final filePath = video.file?.path; - final videoAssetUrl = video.assetUrl; - if (filePath != null || videoAssetUrl != null) { - return Image( - image: StreamVideoThumbnailImage(video: filePath ?? videoAssetUrl!), - width: width, - height: height, - fit: fit, - frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { - if (frame != null || wasSynchronouslyLoaded) { - return child; - } - - final image = Image.asset( - 'lib/assets/images/placeholder.png', - width: width, - height: height, - fit: BoxFit.cover, - package: 'stream_chat_flutter', - ); - - final colorTheme = StreamChatTheme.of(context).colorTheme; - return Shimmer.fromColors( - baseColor: colorTheme.disabled, - highlightColor: colorTheme.inputBg, - child: image, - ); - }, - errorBuilder: errorBuilder, - ); - } - - // Return error widget if no thumbnail is found. - return errorBuilder( - context, - 'Video attachment is not valid', - StackTrace.current, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/url_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/url_attachment.dart deleted file mode 100644 index 6d8629ea4c..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/url_attachment.dart +++ /dev/null @@ -1,146 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamUrlAttachment} -/// Displays a URL attachment in a [StreamMessageWidget]. -/// {@endtemplate} -class StreamUrlAttachment extends StatelessWidget { - /// {@macro streamUrlAttachment} - const StreamUrlAttachment({ - super.key, - required this.message, - required this.urlAttachment, - required this.hostDisplayName, - required this.messageTheme, - this.shape, - this.constraints = const BoxConstraints(), - }); - - /// The [Message] that the image is attached to. - final Message message; - - /// Attachment to be displayed - final Attachment urlAttachment; - - /// The shape of the attachment. - /// - /// Defaults to [RoundedRectangleBorder] with a radius of 14. - final ShapeBorder? shape; - - /// The constraints to use when displaying the file. - final BoxConstraints constraints; - - /// Host name - final String hostDisplayName; - - /// The [StreamMessageThemeData] to use for the image title - final StreamMessageThemeData messageTheme; - - @override - Widget build(BuildContext context) { - final chatTheme = StreamChatTheme.of(context); - final colorTheme = chatTheme.colorTheme; - final shape = this.shape ?? - RoundedRectangleBorder( - side: BorderSide( - color: colorTheme.borders, - strokeAlign: BorderSide.strokeAlignOutside, - ), - borderRadius: BorderRadius.circular(8), - ); - - final backgroundColor = messageTheme.urlAttachmentBackgroundColor; - - return Container( - constraints: constraints, - clipBehavior: Clip.hardEdge, - decoration: ShapeDecoration( - shape: shape, - color: backgroundColor, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Stack( - children: [ - AspectRatio( - // Default aspect ratio for Open Graph images. - // https://www.kapwing.com/resources/what-is-an-og-image-make-and-format-og-images-for-your-blog-or-webpage - aspectRatio: 1.91 / 1, - child: StreamImageAttachmentThumbnail( - image: urlAttachment, - fit: BoxFit.cover, - ), - ), - Positioned( - left: 0, - bottom: 0, - child: DecoratedBox( - decoration: BoxDecoration( - borderRadius: const BorderRadius.only( - topRight: Radius.circular(16), - ), - color: backgroundColor, - ), - child: Padding( - padding: const EdgeInsets.only( - top: 8, - left: 8, - right: 12, - bottom: 4, - ), - child: Text( - hostDisplayName, - style: messageTheme.urlAttachmentHostStyle, - ), - ), - ), - ), - ], - ), - Padding( - padding: const EdgeInsets.all(8), - child: Column( - spacing: 4, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - if (urlAttachment.title != null) - Builder(builder: (context) { - final maxLines = messageTheme.urlAttachmentTitleMaxLine; - - TextOverflow? overflow; - if (maxLines != null && maxLines > 0) { - overflow = TextOverflow.ellipsis; - } - - return Text( - urlAttachment.title!.trim(), - maxLines: maxLines, - overflow: overflow, - style: messageTheme.urlAttachmentTitleStyle, - ); - }), - if (urlAttachment.text != null) - Builder(builder: (context) { - final maxLines = messageTheme.urlAttachmentTextMaxLine; - - TextOverflow? overflow; - if (maxLines != null && maxLines > 0) { - overflow = TextOverflow.ellipsis; - } - - return Text( - urlAttachment.text!, - maxLines: maxLines, - overflow: overflow, - style: messageTheme.urlAttachmentTextStyle, - ); - }), - ], - ), - ), - ], - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/video_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/video_attachment.dart deleted file mode 100644 index a79d44c77f..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/video_attachment.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamVideoAttachment} -/// Shows a video attachment in a [StreamMessageWidget]. -/// {@endtemplate} -class StreamVideoAttachment extends StatelessWidget { - /// {@macro streamVideoAttachment} - const StreamVideoAttachment({ - super.key, - required this.message, - required this.video, - this.shape, - this.constraints = const BoxConstraints(), - }); - - /// The [Message] that the video is attached to. - final Message message; - - /// The [Attachment] object containing the video information. - final Attachment video; - - /// The shape of the attachment. - /// - /// Defaults to [RoundedRectangleBorder] with a radius of 14. - final ShapeBorder? shape; - - /// The constraints to use when displaying the video. - final BoxConstraints constraints; - - @override - Widget build(BuildContext context) { - final chatTheme = StreamChatTheme.of(context); - final colorTheme = chatTheme.colorTheme; - final shape = this.shape ?? - RoundedRectangleBorder( - side: BorderSide( - color: colorTheme.borders, - strokeAlign: BorderSide.strokeAlignOutside, - ), - borderRadius: BorderRadius.circular(14), - ); - - return Container( - constraints: constraints, - clipBehavior: Clip.hardEdge, - decoration: ShapeDecoration(shape: shape), - child: Stack( - alignment: Alignment.center, - children: [ - StreamVideoAttachmentThumbnail( - video: video, - width: double.infinity, - height: double.infinity, - fit: BoxFit.cover, - ), - const Material( - shape: CircleBorder(), - child: Padding( - padding: EdgeInsets.all(16), - child: Icon(Icons.play_arrow), - ), - ), - Padding( - padding: const EdgeInsets.all(8), - child: StreamAttachmentUploadStateBuilder( - message: message, - attachment: video, - ), - ), - ], - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/voice_recording_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/voice_recording_attachment.dart deleted file mode 100644 index 3681d30939..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/voice_recording_attachment.dart +++ /dev/null @@ -1,355 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/audio/audio_playlist_state.dart'; -import 'package:stream_chat_flutter/src/audio/audio_sampling.dart' as sampling; -import 'package:stream_chat_flutter/src/misc/audio_waveform.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -const _kDefaultWaveformLimit = 35; -const _kDefaultWaveformHeight = 28.0; - -/// Signature for building trailing widgets in voice recording attachments. -/// -/// Provides a flexible way to customize the trailing section of the -/// voice recording player based on the current track and playback state. -typedef StreamVoiceRecordingAttachmentTrailingWidgetBuilder = Widget Function( - BuildContext context, - PlaylistTrack track, - PlaybackSpeed speed, - ValueChanged? onChangeSpeed, -); - -/// {@template streamVoiceRecordingAttachment} -/// An embedded audio player for voice recordings with comprehensive playback -/// controls. -/// -/// Provides a rich audio message player with features including: -/// - Play/pause controls -/// - Waveform visualization -/// - Playback speed adjustment -/// - Optional title display -/// -/// Supports customizable appearance and interaction through various parameters. -/// {@endtemplate} -class StreamVoiceRecordingAttachment extends StatelessWidget { - /// {@macro streamVoiceRecordingAttachment} - const StreamVoiceRecordingAttachment({ - super.key, - required this.track, - required this.speed, - this.onTrackPause, - this.onTrackPlay, - this.onTrackReplay, - this.onTrackSeekStart, - this.onTrackSeekChanged, - this.onTrackSeekEnd, - this.onChangeSpeed, - this.shape, - this.constraints = const BoxConstraints(), - this.showTitle = false, - this.trailingBuilder = _defaultTrailingBuilder, - }); - - /// The audio track to display. - final PlaylistTrack track; - - /// The current playback speed of the audio track. - final PlaybackSpeed speed; - - /// Callback when the track is paused. - final VoidCallback? onTrackPause; - - /// Callback when the track is played. - final VoidCallback? onTrackPlay; - - /// Callback when the track is replayed. - final VoidCallback? onTrackReplay; - - /// Callback when the track seek is started. - final ValueChanged? onTrackSeekStart; - - /// Callback when the track seek is changed. - final ValueChanged? onTrackSeekChanged; - - /// Callback when the track seek is ended. - final ValueChanged? onTrackSeekEnd; - - /// Callback when the playback speed is changed. - final ValueChanged? onChangeSpeed; - - /// The shape of the attachment. - /// - /// Defaults to [RoundedRectangleBorder] with a radius of 14. - final ShapeBorder? shape; - - /// The constraints to use when displaying the voice recording. - final BoxConstraints constraints; - - /// Whether to show the title of the audio message. - /// - /// Defaults to `false`. - final bool showTitle; - - /// The builder to use for the trailing widget. - final StreamVoiceRecordingAttachmentTrailingWidgetBuilder trailingBuilder; - - static Widget _defaultTrailingBuilder( - BuildContext context, - PlaylistTrack track, - PlaybackSpeed speed, - ValueChanged? onChangeSpeed, - ) { - return AnimatedSwitcher( - duration: const Duration(milliseconds: 200), - child: switch (track.state.isPlaying) { - true => SpeedControlButton( - speed: speed, - onChangeSpeed: onChangeSpeed, - ), - false => getFileTypeImage(track.title?.mediaType?.mimeType), - }, - ); - } - - @override - Widget build(BuildContext context) { - final theme = StreamVoiceRecordingAttachmentTheme.of(context); - final waveformSliderTheme = theme.audioWaveformSliderTheme; - final waveformTheme = waveformSliderTheme?.audioWaveformTheme; - - final shape = this.shape ?? - RoundedRectangleBorder( - side: BorderSide( - color: StreamChatTheme.of(context).colorTheme.borders, - strokeAlign: BorderSide.strokeAlignOutside, - ), - borderRadius: BorderRadius.circular(14), - ); - - return Container( - constraints: constraints, - clipBehavior: Clip.hardEdge, - padding: const EdgeInsets.all(8), - decoration: ShapeDecoration( - shape: shape, - color: theme.backgroundColor, - ), - child: Row( - children: [ - AudioControlButton( - state: track.state, - onPlay: onTrackPlay, - onPause: onTrackPause, - onReplay: onTrackReplay, - ), - const SizedBox(width: 14), - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (track.title case final title? when showTitle) ...[ - AudioTitleText( - title: title, - style: theme.titleTextStyle, - ), - const SizedBox(height: 6), - ], - Row( - children: [ - AudioDurationText( - duration: track.duration, - position: track.position, - style: theme.durationTextStyle, - ), - const SizedBox(width: 8), - Expanded( - child: SizedBox( - height: _kDefaultWaveformHeight, - child: StreamAudioWaveformSlider( - limit: _kDefaultWaveformLimit, - waveform: sampling.resampleWaveformData( - track.waveform, - _kDefaultWaveformLimit, - ), - progress: track.progress, - onChangeStart: onTrackSeekStart, - onChanged: onTrackSeekChanged, - onChangeEnd: onTrackSeekEnd, - color: waveformTheme?.color, - progressColor: waveformTheme?.progressColor, - minBarHeight: waveformTheme?.minBarHeight, - spacingRatio: waveformTheme?.spacingRatio, - heightScale: waveformTheme?.heightScale, - thumbColor: waveformSliderTheme?.thumbColor, - thumbBorderColor: - waveformSliderTheme?.thumbBorderColor, - ), - ), - ), - ], - ), - ], - ), - ), - const SizedBox(width: 14), - trailingBuilder(context, track, speed, onChangeSpeed), - ], - ), - ); - } -} - -/// {@template audioTitleText} -/// A compact text widget for displaying audio file titles. -/// -/// Renders the title with ellipsis truncation and optional styling. -/// {@endtemplate} -class AudioTitleText extends StatelessWidget { - /// {@macro audioTitleText} - const AudioTitleText({ - super.key, - required this.title, - this.style, - }); - - /// The title to display. - final String title; - - /// The style to apply to the title. - final TextStyle? style; - - @override - Widget build(BuildContext context) { - return Text( - title, - style: style, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ); - } -} - -/// {@template audioDurationText} -/// Displays duration for audio playback with dynamic formatting. -/// -/// Shows either current position or total duration based on playback state. -/// {@endtemplate} -class AudioDurationText extends StatelessWidget { - /// {@macro audioDurationText} - const AudioDurationText({ - super.key, - required this.duration, - required this.position, - this.style, - }); - - /// The total duration of the audio track. - final Duration duration; - - /// The current position of the audio track. - final Duration position; - - /// The style to apply to the duration text. - final TextStyle? style; - - @override - Widget build(BuildContext context) { - return Text( - switch (position.inMilliseconds > 0) { - true => position.toMinutesAndSeconds(), - false => duration.toMinutesAndSeconds(), - }, - style: style?.copyWith( - // Use mono space for each num character. - fontFeatures: [const FontFeature.tabularFigures()], - ), - ); - } -} - -/// {@template audioControlButton} -/// A control button for managing audio playback state. -/// -/// Adapts its icon and behavior based on the current track state: -/// - Loading: Shows a progress indicator -/// - Idle: Displays play icon -/// - Playing: Shows pause icon -/// - Paused: Shows play icon -/// {@endtemplate} -class AudioControlButton extends StatelessWidget { - /// {@macro audioControlButton} - const AudioControlButton({ - super.key, - required this.state, - this.onPlay, - this.onPause, - this.onReplay, - }); - - /// The current state of the audio track. - final TrackState state; - - /// Callback when the track is played. - final VoidCallback? onPlay; - - /// Callback when the track is paused. - final VoidCallback? onPause; - - /// Callback when the track is replayed. - final VoidCallback? onReplay; - - @override - Widget build(BuildContext context) { - final theme = StreamVoiceRecordingAttachmentTheme.of(context); - - return ElevatedButton( - style: theme.audioControlButtonStyle, - onPressed: switch (state) { - TrackState.loading => null, - TrackState.idle => onPlay, - TrackState.playing => onPause, - TrackState.paused => onPlay, - }, - child: switch (state) { - TrackState.loading => theme.loadingIndicator, - TrackState.idle => theme.playIcon, - TrackState.playing => theme.pauseIcon, - TrackState.paused => theme.playIcon, - }, - ); - } -} - -/// {@template speedControlButton} -/// A button for controlling audio playback speed. -/// -/// Allows cycling through predefined playback speeds when pressed. -/// {@endtemplate} -class SpeedControlButton extends StatelessWidget { - /// {@macro speedControlButton} - const SpeedControlButton({ - super.key, - required this.speed, - this.onChangeSpeed, - }); - - /// The current playback speed of the audio track. - final PlaybackSpeed speed; - - /// Callback when the playback speed is changed. - final ValueChanged? onChangeSpeed; - - @override - Widget build(BuildContext context) { - final theme = StreamVoiceRecordingAttachmentTheme.of(context); - - return ElevatedButton( - style: theme.speedControlButtonStyle, - onPressed: switch (onChangeSpeed) { - final it? => () => it(speed.next), - _ => null, - }, - child: Text('x${speed.speed}'), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment/voice_recording_attachment_playlist.dart b/packages/stream_chat_flutter/lib/src/attachment/voice_recording_attachment_playlist.dart deleted file mode 100644 index aa062da2f9..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment/voice_recording_attachment_playlist.dart +++ /dev/null @@ -1,156 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/attachment/voice_recording_attachment.dart'; -import 'package:stream_chat_flutter/src/audio/audio_playlist_controller.dart'; - -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamVoiceRecordingAttachmentPlaylist} -/// Shows a voice recording attachment in a [StreamMessageWidget]. -/// {@endtemplate} -class StreamVoiceRecordingAttachmentPlaylist extends StatefulWidget { - /// {@macro streamVoiceRecordingAttachmentPlaylist} - const StreamVoiceRecordingAttachmentPlaylist({ - super.key, - this.shape, - required this.message, - required this.voiceRecordings, - this.padding, - this.itemBuilder, - this.separatorBuilder = _defaultVoiceRecordingPlaylistSeparatorBuilder, - this.constraints = const BoxConstraints(), - }); - - /// The shape of the attachment. - /// - /// Defaults to [RoundedRectangleBorder] with a radius of 14. - final ShapeBorder? shape; - - /// The [Message] that the voice recording is attached to. - final Message message; - - /// The list of [Attachment] object containing the voice recording - /// information. - final List voiceRecordings; - - /// The constraints to use when displaying the voice recording. - final BoxConstraints constraints; - - /// The amount of space by which to inset the children. - final EdgeInsetsGeometry? padding; - - /// The builder to use for each voice recording. - /// - /// If not provided, a default implementation will be used. - final IndexedWidgetBuilder? itemBuilder; - - /// The separator to use between the voice recordings. - final IndexedWidgetBuilder separatorBuilder; - - // Default separator builder for the voice recording playlist. - static Widget _defaultVoiceRecordingPlaylistSeparatorBuilder( - BuildContext context, - int index, - ) { - return const SizedBox.shrink(); - } - - @override - State createState() => - _StreamVoiceRecordingAttachmentPlaylistState(); -} - -class _StreamVoiceRecordingAttachmentPlaylistState - extends State { - late final _controller = StreamAudioPlaylistController( - widget.voiceRecordings.toPlaylist(), - ); - - @override - void initState() { - super.initState(); - _controller.initialize(); - } - - @override - void didUpdateWidget( - covariant StreamVoiceRecordingAttachmentPlaylist oldWidget, - ) { - super.didUpdateWidget(oldWidget); - final equals = const ListEquality().equals; - if (!equals(widget.voiceRecordings, oldWidget.voiceRecordings)) { - // If the playlist have changed, update the playlist. - _controller.updatePlaylist(widget.voiceRecordings.toPlaylist()); - } - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return ValueListenableBuilder( - valueListenable: _controller, - builder: (context, state, _) { - return MediaQuery.removePadding( - context: context, - // Workaround for the bottom padding issue. - // Link: https://github.com/flutter/flutter/issues/156149 - removeTop: true, - removeBottom: true, - child: ListView.separated( - shrinkWrap: true, - padding: widget.padding, - physics: const NeverScrollableScrollPhysics(), - itemCount: state.tracks.length, - separatorBuilder: widget.separatorBuilder, - itemBuilder: (context, index) { - if (widget.itemBuilder case final builder?) { - return builder(context, index); - } - - final track = state.tracks[index]; - return StreamVoiceRecordingAttachment( - track: track, - speed: state.speed, - showTitle: true, - shape: widget.shape, - constraints: widget.constraints, - onTrackPause: _controller.pause, - onChangeSpeed: _controller.setSpeed, - onTrackPlay: () async { - // Play the track directly if it is already loaded. - if (state.currentIndex == index) return _controller.play(); - // Otherwise, load the track first and then play it. - return _controller.skipToItem(index); - }, - // Only allow seeking if the current track is the one being - // interacted with. - onTrackSeekStart: (_) async { - if (state.currentIndex != index) return; - return _controller.pause(); - }, - onTrackSeekEnd: (_) async { - if (state.currentIndex != index) return; - return _controller.play(); - }, - onTrackSeekChanged: (progress) async { - if (state.currentIndex != index) return; - - final duration = track.duration.inMicroseconds; - final seekPosition = (duration * progress).toInt(); - final seekDuration = Duration(microseconds: seekPosition); - - return _controller.seek(seekDuration); - }, - ); - }, - ), - ); - }, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/attachment_actions_modal/attachment_actions_modal.dart b/packages/stream_chat_flutter/lib/src/attachment_actions_modal/attachment_actions_modal.dart deleted file mode 100644 index f11e4e127e..0000000000 --- a/packages/stream_chat_flutter/lib/src/attachment_actions_modal/attachment_actions_modal.dart +++ /dev/null @@ -1,419 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Widget that shows the options in the gallery view -class AttachmentActionsModal extends StatelessWidget { - /// Returns a new [AttachmentActionsModal] - const AttachmentActionsModal({ - super.key, - required this.attachment, - required this.message, - this.onShowMessage, - this.onReply, - this.attachmentDownloader, - this.showReply = true, - this.showShowInChat = true, - this.showSave = true, - this.showDelete = true, - this.customActions = const [], - }); - - /// The attachment object for which the actions are to be performed - final Attachment attachment; - - /// The message containing the attachments - final Message message; - - /// Callback to show the message - final VoidCallback? onShowMessage; - - /// Callback to reply the message - final VoidCallback? onReply; - - /// Callback to download [attachment]. - final AttachmentDownloader? attachmentDownloader; - - /// Show "reply" option - final bool showReply; - - /// Show "show in chat" option - final bool showShowInChat; - - /// Show save option - final bool showSave; - - /// Show delete option - final bool showDelete; - - /// List of custom actions - final List customActions; - - /// Creates a copy of [StreamMessageWidget] with - /// specified attributes overridden. - AttachmentActionsModal copyWith({ - Key? key, - Attachment? attachment, - Message? message, - VoidCallback? onShowMessage, - VoidCallback? onReply, - AttachmentDownloader? attachmentDownloader, - bool? showReply, - bool? showShowInChat, - bool? showSave, - bool? showDelete, - List? customActions, - }) { - return AttachmentActionsModal( - key: key ?? this.key, - attachment: attachment ?? this.attachment, - message: message ?? this.message, - onShowMessage: onShowMessage ?? this.onShowMessage, - onReply: onReply ?? this.onReply, - attachmentDownloader: attachmentDownloader ?? this.attachmentDownloader, - showReply: showReply ?? this.showReply, - showShowInChat: showShowInChat ?? this.showShowInChat, - showSave: showSave ?? this.showSave, - showDelete: showDelete ?? this.showDelete, - customActions: customActions ?? this.customActions, - ); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () => Navigator.of(context).maybePop(), - child: _buildPage(context), - ); - } - - Widget _buildPage(BuildContext context) { - final theme = StreamChatTheme.of(context); - return Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - const SizedBox(height: kToolbarHeight), - Padding( - padding: const EdgeInsets.only(right: 8), - child: Container( - width: MediaQuery.of(context).size.width * 0.5, - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(16), - ), - child: SizedBox( - child: Column( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisSize: MainAxisSize.min, - children: [ - if (showReply) - _buildButton( - context, - context.translations.replyLabel, - StreamSvgIcon( - size: 24, - icon: StreamSvgIcons.reply, - color: theme.colorTheme.textLowEmphasis, - ), - onReply, - ), - if (showShowInChat) - _buildButton( - context, - context.translations.showInChatLabel, - StreamSvgIcon( - size: 24, - icon: StreamSvgIcons.eye, - color: theme.colorTheme.textHighEmphasis, - ), - onShowMessage, - ), - if (showSave) - _buildButton( - context, - attachment.type == AttachmentType.video - ? context.translations.saveVideoLabel - : context.translations.saveImageLabel, - StreamSvgIcon( - size: 24, - icon: StreamSvgIcons.save, - color: theme.colorTheme.textLowEmphasis, - ), - () { - // Closing attachment actions modal before opening - // attachment download dialog - Navigator.of(context).pop(); - - final downloader = attachmentDownloader ?? - StreamAttachmentHandler.instance.downloadAttachment; - - // No need to show progress dialog in case of - // web or desktop. - if (isDesktopDeviceOrWeb) { - downloader(attachment); - return; - } - - final progressNotifier = - ValueNotifier<_DownloadProgress?>( - _DownloadProgress.initial(), - ); - - final downloadedPathNotifier = - ValueNotifier(null); - - downloader( - attachment, - onReceiveProgress: (received, total) { - progressNotifier.value = _DownloadProgress( - total, - received, - ); - }, - ).then((path) { - downloadedPathNotifier.value = path; - }).catchError((e, stk) { - print(e); - print(stk); - progressNotifier.value = null; - }); - - showDialog( - barrierDismissible: false, - context: context, - barrierColor: theme.colorTheme.overlay, - builder: (context) => _buildDownloadProgressDialog( - context, - progressNotifier, - downloadedPathNotifier, - ), - ); - }, - ), - if (StreamChat.of(context).currentUser?.id == - message.user?.id && - showDelete) - _buildButton( - context, - context.translations.deleteLabel.capitalize(), - StreamSvgIcon( - size: 24, - icon: StreamSvgIcons.delete, - color: theme.colorTheme.accentError, - ), - () { - final channel = StreamChannel.of(context).channel; - if (message.attachments.length > 1 || - message.text?.isNotEmpty == true) { - final currentAttachmentIndex = - message.attachments.indexWhere( - (element) => element.id == attachment.id, - ); - final remainingAttachments = [...message.attachments] - ..removeAt(currentAttachmentIndex); - channel.updateMessage(message.copyWith( - attachments: remainingAttachments, - )); - Navigator.of(context) - ..pop() - ..maybePop(); - } else { - channel.deleteMessage(message); - Navigator.of(context) - ..pop() - ..maybePop(); - } - }, - color: theme.colorTheme.accentError, - ), - ...customActions - .map( - (e) => _buildButton( - context, - e.actionTitle, - e.icon, - e.onTap, - ), - ) - .toList(), - ] - .map((e) => Align( - alignment: Alignment.centerRight, - child: e, - )) - .insertBetween( - Container( - height: 1, - color: theme.colorTheme.borders, - ), - ), - ), - ), - ), - ), - ], - ); - } - - Widget _buildButton( - context, - String title, - Widget icon, - VoidCallback? onTap, { - Color? color, - Key? key, - }) { - return Material( - key: key, - color: StreamChatTheme.of(context).colorTheme.barsBg, - child: InkWell( - onTap: onTap, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), - child: Row( - children: [ - icon, - const SizedBox(width: 16), - Text( - title, - style: StreamChatTheme.of(context) - .textTheme - .body - .copyWith(color: color), - ), - ], - ), - ), - ), - ); - } - - Widget _buildDownloadProgressDialog( - BuildContext context, - ValueNotifier<_DownloadProgress?> progressNotifier, - ValueNotifier downloadedFilePathNotifier, - ) { - final theme = StreamChatTheme.of(context); - return ValueListenableBuilder( - valueListenable: downloadedFilePathNotifier, - builder: (_, String? path, __) { - final _downloadComplete = path != null && path.isNotEmpty; - // Pop the dialog in case the download has completed - if (_downloadComplete) { - Future.delayed( - const Duration(milliseconds: 500), - () => Navigator.of(context).maybePop(), - ); - } - return ValueListenableBuilder( - valueListenable: progressNotifier, - builder: (_, _DownloadProgress? progress, __) { - // Pop the dialog in case the progress is null. - if (progress == null) { - Future.delayed( - const Duration(milliseconds: 500), - () => Navigator.of(context).maybePop(), - ); - } - return Material( - type: MaterialType.transparency, - child: Center( - child: Container( - height: 182, - width: 182, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(16), - color: theme.colorTheme.barsBg, - ), - child: Center( - child: progress == null - ? SizedBox( - height: 100, - width: 100, - child: StreamSvgIcon( - icon: StreamSvgIcons.error, - color: theme.colorTheme.disabled, - ), - ) - : _downloadComplete - ? SizedBox( - key: const Key('completedIcon'), - height: 160, - width: 160, - child: StreamSvgIcon( - icon: StreamSvgIcons.check, - color: theme.colorTheme.disabled, - ), - ) - : SizedBox( - height: 100, - width: 100, - child: Stack( - fit: StackFit.expand, - children: [ - CircularProgressIndicator.adaptive( - strokeWidth: 8, - valueColor: AlwaysStoppedAnimation( - theme.colorTheme.accentPrimary, - ), - ), - Center( - child: Text( - '${progress.receivedValueInMB} MB', - style: - theme.textTheme.headline.copyWith( - color: - theme.colorTheme.textLowEmphasis, - ), - ), - ), - ], - ), - ), - ), - ), - ), - ); - }, - ); - }, - ); - } -} - -class _DownloadProgress { - const _DownloadProgress(this.total, this.received); - - factory _DownloadProgress.initial() => - _DownloadProgress(double.maxFinite.toInt(), 0); - - final int total; - final int received; - - String get receivedValueInMB => ((received / 1024) / 1024).toStringAsFixed(2); - - double get toProgressIndicatorValue => received / total; - - int get toPercentage => (received * 100) ~/ total; -} - -/// {@template attachmentAction} -/// Defines a custom attachment action. -/// {@endtemplate} -class AttachmentAction { - /// {@macro attachmentAction} - AttachmentAction({ - required this.actionTitle, - required this.icon, - required this.onTap, - }); - - /// Title for the attachment action - String actionTitle; - - /// Icon for the attachment action - Widget icon; - - /// Callback for when the action is tapped - VoidCallback onTap; -} diff --git a/packages/stream_chat_flutter/lib/src/audio/audio_playlist_controller.dart b/packages/stream_chat_flutter/lib/src/audio/audio_playlist_controller.dart deleted file mode 100644 index 2e8f13f5a1..0000000000 --- a/packages/stream_chat_flutter/lib/src/audio/audio_playlist_controller.dart +++ /dev/null @@ -1,199 +0,0 @@ -import 'dart:async'; - -import 'package:collection/collection.dart'; -import 'package:flutter/foundation.dart'; -import 'package:just_audio/just_audio.dart'; -import 'package:stream_chat_flutter/src/audio/audio_playlist_state.dart'; - -/// {@template streamAudioPlaylistController} -/// A controller for managing an audio playlist. -/// {@endtemplate} -class StreamAudioPlaylistController extends ValueNotifier { - /// {@macro streamAudioPlaylistController} - factory StreamAudioPlaylistController(List tracks) { - return StreamAudioPlaylistController.raw( - player: AudioPlayer(), - state: AudioPlaylistState(tracks: tracks), - ); - } - - /// {@macro streamAudioPlaylistController} - @visibleForTesting - StreamAudioPlaylistController.raw({ - required AudioPlayer player, - AudioPlaylistState state = const AudioPlaylistState(tracks: []), - }) : _player = player, - super(state); - - final AudioPlayer _player; - - StreamSubscription? _playerStateSubscription; - StreamSubscription? _positionSubscription; - StreamSubscription? _speedSubscription; - - /// Initializes the controller and starts listening to player state changes. - Future initialize() async { - // Listen to player state changes - _playerStateSubscription = _player.playerStateStream.listen((state) { - // Handle auto-advance when track completes - if (state.playing && state.processingState == ProcessingState.completed) { - return _onTrackComplete(); - } - - final currentIndex = value.currentIndex; - if (currentIndex == null) return; - - final tracks = [ - ...value.tracks.mapIndexed((index, track) { - final trackState = switch (index == currentIndex) { - true => state.playing - ? TrackState.playing - : switch (state.processingState) { - ProcessingState.idle => TrackState.idle, - ProcessingState.loading => TrackState.loading, - _ => TrackState.paused, - }, - false => switch (track.state) { - TrackState.idle => TrackState.idle, - _ => TrackState.paused, - }, - }; - - return track.copyWith(state: trackState); - }) - ]; - - value = value.copyWith(tracks: tracks); - }); - - // Listen to position changes - _positionSubscription = _player.positionStream.listen((position) { - final currentIndex = value.currentIndex; - if (currentIndex == null) return; - - final tracks = [ - ...value.tracks.mapIndexed((index, track) { - if (index != currentIndex) return track; - return track.copyWith(position: position); - }) - ]; - - value = value.copyWith(tracks: tracks); - }); - - // Listen to speed changes - _speedSubscription = _player.speedStream.listen((speed) { - value = value.copyWith(speed: PlaybackSpeed.fromValue(speed)); - }); - } - - void _onTrackComplete() async { - final currentIndex = value.currentIndex; - if (currentIndex == null) return; - - return pause().then((_) => seek(Duration.zero)).then((_) => skipToNext()); - } - - int? get _nextIndex => _getRelativeIndex(1); - int? get _previousIndex => _getRelativeIndex(-1); - int? _getRelativeIndex(int offset) { - final currentIndex = value.currentIndex; - if (currentIndex == null) return null; - - return switch (value.loopMode) { - PlaylistLoopMode.one => currentIndex, - PlaylistLoopMode.off => currentIndex + offset, - PlaylistLoopMode.all => (currentIndex + offset) % value.tracks.length, - }; - } - - /// Plays the current track. - Future play() => _player.play(); - - /// Pauses the current track. - Future pause() => _player.pause(); - - /// Stops the current track. - Future stop() => _player.stop(); - - /// Sets the speed of the current track. - Future setSpeed(PlaybackSpeed speed) => _player.setSpeed(speed.speed); - - /// Seeks to the given position in the current track. - Future seek(Duration position) => _player.seek(position); - - /// Sets the loop mode of the playlist. - Future setLoopMode(PlaylistLoopMode loopMode) async { - value = value.copyWith(loopMode: loopMode); - } - - /// Plays the next track in the playlist. - Future skipToNext({Duration? position}) async { - final index = _nextIndex; - if (index == null) return; - - return skipToItem(index, position: position); - } - - /// Plays the previous track in the playlist. - Future skipToPrevious({Duration? position}) async { - final index = _previousIndex; - if (index == null) return; - - return skipToItem(index, position: position); - } - - /// Seeks to the given position in the current track in the playlist and - /// resumes playing. - Future skipToItem(int index, {Duration? position}) async { - final tracks = value.tracks; - if (tracks.isEmpty) return; - - if (index < 0 || index >= tracks.length) return; - value = value.copyWith(currentIndex: index); - - final track = tracks[index]; - final seekPosition = position ?? track.position; - final audioSource = AudioSource.uri(track.uri); - - final duration = await _player.setAudioSource( - audioSource, - initialPosition: seekPosition, - ); - - value = value.copyWith( - tracks: [ - ...tracks.mapIndexed((i, track) { - if (i != index) return track; - return track.copyWith(duration: duration); - }), - ], - ); - - return play(); - } - - /// Updates the playlist with the given tracks. - /// - /// Note: This will stop the player if it is currently playing. - Future updatePlaylist(List tracks) async { - if (tracks.isEmpty) return; // No tracks to update - - unawaited(_player.stop()); - - value = AudioPlaylistState( - tracks: tracks, - speed: value.speed, - loopMode: value.loopMode, - ); - } - - @override - void dispose() { - _playerStateSubscription?.cancel(); - _positionSubscription?.cancel(); - _speedSubscription?.cancel(); - _player.dispose(); - super.dispose(); - } -} diff --git a/packages/stream_chat_flutter/lib/src/audio/audio_playlist_state.dart b/packages/stream_chat_flutter/lib/src/audio/audio_playlist_state.dart deleted file mode 100644 index 36a3078037..0000000000 --- a/packages/stream_chat_flutter/lib/src/audio/audio_playlist_state.dart +++ /dev/null @@ -1,228 +0,0 @@ -import 'dart:math' as math; - -import 'package:collection/collection.dart'; - -/// {@template playlistLoopMode} -/// Represents the loop mode of a playlist. -/// {@endtemplate} -enum PlaylistLoopMode { - /// The playlist will not loop. - off, - - /// The playlist will loop all tracks. - all, - - /// The playlist will loop the current track. - one, -} - -/// {@template audioPlaylistState} -/// Represents the current state of an audio playlist. -/// {@endtemplate} -class AudioPlaylistState { - /// {@macro audioPlaylistState} - const AudioPlaylistState({ - required this.tracks, - this.currentIndex, - this.speed = PlaybackSpeed.regular, - this.loopMode = PlaylistLoopMode.off, - }); - - /// The list of tracks in the playlist. - final List tracks; - - /// The index of the current track being played in the playlist. - /// - /// Defaults to `null`. - final int? currentIndex; - - /// The current playback speed of the playlist. - final PlaybackSpeed speed; - - /// The current loop mode of the playlist. - final PlaylistLoopMode loopMode; - - /// Creates a copy of this [AudioPlaylistState] but with the given fields - /// replaced by the new values. - AudioPlaylistState copyWith({ - List? tracks, - int? currentIndex, - PlaybackSpeed? speed, - PlaylistLoopMode? loopMode, - }) { - return AudioPlaylistState( - tracks: tracks ?? this.tracks, - currentIndex: currentIndex ?? this.currentIndex, - speed: speed ?? this.speed, - loopMode: loopMode ?? this.loopMode, - ); - } - - @override - int get hashCode => Object.hash(tracks, currentIndex, speed, loopMode); - - @override - bool operator ==(Object other) => - identical(this, other) || - other is AudioPlaylistState && - runtimeType == other.runtimeType && - const ListEquality().equals(tracks, other.tracks) && - currentIndex == other.currentIndex && - speed == other.speed && - loopMode == other.loopMode; -} - -/// {@template trackState} -/// Represents the current state of a track. -/// {@endtemplate} -enum TrackState { - /// The track has not been loaded yet. - idle, - - /// The track is currently being loaded. - loading, - - /// The track is currently playing. - playing, - - /// The track is currently paused. - paused; - - /// Returns `true` if the track is currently idle. - bool get isIdle => this == TrackState.idle; - - /// Returns `true` if the track is currently loading. - bool get isLoading => this == TrackState.loading; - - /// Returns `true` if the track is currently playing. - bool get isPlaying => this == TrackState.playing; - - /// Returns `true` if the track is currently paused. - bool get isPaused => this == TrackState.paused; -} - -/// {@template playbackSpeed} -/// Represents the speed of a track. -/// {@endtemplate} -enum PlaybackSpeed { - /// The regular speed of the playback (1x). - regular._(1), - - /// A faster speed of the playback (1.5x). - faster._(1.5), - - /// The fastest speed of the playback (2x). - fastest._(2); - - const PlaybackSpeed._(this.speed); - - /// Creates a [PlaybackSpeed] from the given value. - factory PlaybackSpeed.fromValue(double speed) { - return PlaybackSpeed.values.firstWhere( - (it) => it.speed == speed, - orElse: () => PlaybackSpeed.regular, - ); - } - - /// The speed of the playback. - final double speed; -} - -/// Helper extension for [PlaybackSpeed]. -extension StreamAudioPlayerExtension on PlaybackSpeed { - /// Returns the next [PlaybackSpeed] value. - PlaybackSpeed get next { - return switch (this) { - PlaybackSpeed.regular => PlaybackSpeed.faster, - PlaybackSpeed.faster => PlaybackSpeed.fastest, - PlaybackSpeed.fastest => PlaybackSpeed.regular, - }; - } -} - -/// {@template playlistTrack} -/// Represents a track in a playlist. -/// {@endtemplate} -class PlaylistTrack { - /// {@macro playlistTrack} - const PlaylistTrack({ - required this.uri, - this.title, - this.waveform = const [], - this.duration = Duration.zero, - this.position = Duration.zero, - this.state = TrackState.idle, - }); - - /// The uri of the track. - final Uri uri; - - /// The title of the track. - final String? title; - - /// The waveform of the track. - /// - /// Defaults to an empty list. - final List waveform; - - /// The total duration of the track. - /// - /// Defaults to `Duration.zero`. - final Duration duration; - - /// The current playback position of the track. - /// - /// Defaults to `Duration.zero`. - final Duration position; - - /// The current state of the track. - final TrackState state; - - /// The current progress of the track. - double get progress { - final position = this.position.inMicroseconds; - if (position == 0) return 0; - - final duration = this.duration.inMicroseconds; - if (duration == 0) return 0; - - return math.min(position / duration, 1); - } - - /// Creates a copy of this [PlaylistTrack] but with the given fields replaced - /// by the new values. - PlaylistTrack copyWith({ - Uri? uri, - String? title, - Duration? duration, - List? waveform, - Duration? position, - TrackState? state, - }) { - return PlaylistTrack( - uri: uri ?? this.uri, - title: title ?? this.title, - duration: duration ?? this.duration, - waveform: waveform ?? this.waveform, - position: position ?? this.position, - state: state ?? this.state, - ); - } - - @override - int get hashCode { - return Object.hash(uri, title, duration, waveform, position, state); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is PlaylistTrack && - runtimeType == other.runtimeType && - uri == other.uri && - title == other.title && - duration == other.duration && - waveform == other.waveform && - position == other.position && - state == other.state; -} diff --git a/packages/stream_chat_flutter/lib/src/audio/audio_sampling.dart b/packages/stream_chat_flutter/lib/src/audio/audio_sampling.dart deleted file mode 100644 index 0381d273eb..0000000000 --- a/packages/stream_chat_flutter/lib/src/audio/audio_sampling.dart +++ /dev/null @@ -1,142 +0,0 @@ -import 'dart:math' as math; - -/// Resamples the waveformData to the target size. -List resampleWaveformData( - List waveformData, - int amplitudesCount, -) { - if (waveformData.length > amplitudesCount) { - return downSample(waveformData, amplitudesCount); - } - - if (waveformData.length < amplitudesCount) { - return upSample(waveformData, amplitudesCount); - } - - return waveformData; -} - -/// Downsamples the [data] to the target output size. -/// -/// The downSample function uses the Largest-Triangle-Three-Buckets (LTTB) -/// algorithm. See the thesis Downsampling Time Series for Visual Representation -/// by Sveinn Steinarsson for more (https://skemman.is/bitstream/1946/15343/3/SS_MSthesis.pdf) -List downSample(List data, int targetOutputSize) { - if (data.length <= targetOutputSize || targetOutputSize == 0) return data; - if (targetOutputSize == 1) return [_mean(data)]; - - final result = []; - // bucket size adjusted due to the fact that the first and the last item in - // the original data array is kept in target output - final bucketSize = (data.length - 2) / (targetOutputSize - 2); - var lastSelectedPointIndex = 0; - result.add(data[lastSelectedPointIndex]); // Always add the first point - - for (var bucketIndex = 1; bucketIndex < targetOutputSize - 1; bucketIndex++) { - final previousBucketRefPoint = data[lastSelectedPointIndex]; - final nextBucketMean = _getNextBucketMean(data, bucketIndex, bucketSize); - - final currentBucketStartIndex = - ((bucketIndex - 1) * bucketSize).floor() + 1; - final nextBucketStartIndex = (bucketIndex * bucketSize).floor() + 1; - final countUnitsBetweenAtoC = - 1 + nextBucketStartIndex - currentBucketStartIndex; - - var maxArea = -1.0; - var triangleArea = -1.0; - double? maxAreaPoint; - - for (var currentPointIndex = currentBucketStartIndex; - currentPointIndex < nextBucketStartIndex; - currentPointIndex++) { - final countUnitsBetweenAtoB = - (currentPointIndex - currentBucketStartIndex).abs() + 1; - final countUnitsBetweenBtoC = - countUnitsBetweenAtoC - countUnitsBetweenAtoB; - final currentPointValue = data[currentPointIndex]; - - triangleArea = _triangleAreaHeron( - _triangleBase( - (previousBucketRefPoint - currentPointValue).abs(), - countUnitsBetweenAtoB.toDouble(), - ), - _triangleBase( - (currentPointValue - nextBucketMean).abs(), - countUnitsBetweenBtoC.toDouble(), - ), - _triangleBase( - (previousBucketRefPoint - nextBucketMean).abs(), - countUnitsBetweenAtoC.toDouble(), - ), - ); - - if (triangleArea > maxArea) { - maxArea = triangleArea; - maxAreaPoint = data[currentPointIndex]; - lastSelectedPointIndex = currentPointIndex; - } - } - - if (maxAreaPoint != null) { - result.add(maxAreaPoint); - } - } - - result.add(data[data.length - 1]); // Always add the last point - return result; -} - -double _triangleAreaHeron(double a, double b, double c) { - final s = (a + b + c) / 2; - return math.sqrt(s * (s - a) * (s - b) * (s - c)); -} - -double _triangleBase(double a, double b) { - return math.sqrt(math.pow(a, 2) + math.pow(b, 2)); -} - -double _mean(List values) { - return values.reduce((acc, value) => acc + value) / values.length; -} - -List _divMod(int num, int divisor) => [num ~/ divisor, num % divisor]; - -double _getNextBucketMean( - List data, - int currentBucketIndex, - double bucketSize, -) { - final nextBucketStartIndex = (currentBucketIndex * bucketSize).floor() + 1; - var nextNextBucketStartIndex = - ((currentBucketIndex + 1) * bucketSize).floor() + 1; - nextNextBucketStartIndex = nextNextBucketStartIndex < data.length - ? nextNextBucketStartIndex - : data.length; - - return _mean(data.sublist(nextBucketStartIndex, nextNextBucketStartIndex)); -} - -/// Upsamples the [data] to the target output size. -/// -/// The upSample function extends the array of amplitudes by repeating the -/// values in the array. -/// -/// If the target size is smaller than the length of the array, the function -/// returns the original array. -List upSample(List data, int targetOutputSize) { - if (data.isEmpty) return List.filled(targetOutputSize, 0); - if (data.length >= targetOutputSize || targetOutputSize == 0) return data; - - final divModResult = _divMod(targetOutputSize, data.length); - final bucketSize = divModResult[0]; - var remainder = divModResult[1]; - - final result = []; - - for (var i = 0; i < data.length; i++) { - final extra = remainder > 0 ? 1 : 0; - if (remainder > 0) remainder--; - result.addAll(List.filled(bucketSize + extra, data[i])); - } - return result; -} diff --git a/packages/stream_chat_flutter/lib/src/autocomplete/stream_autocomplete.dart b/packages/stream_chat_flutter/lib/src/autocomplete/stream_autocomplete.dart deleted file mode 100644 index 23ec549264..0000000000 --- a/packages/stream_chat_flutter/lib/src/autocomplete/stream_autocomplete.dart +++ /dev/null @@ -1,644 +0,0 @@ -// ignore_for_file: no-empty-block - -import 'package:flutter/material.dart'; -import 'package:flutter_portal/flutter_portal.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -export 'stream_command_autocomplete_options.dart'; -export 'stream_mention_autocomplete_options.dart'; - -/// {@macro stream_chat_flutter.StreamMessageInputController} -typedef StreamMessageEditingController = StreamMessageInputController; - -/// Positions the [AutocompleteTrigger] options around the [TextField] or -/// [TextFormField] that triggered the autocomplete. -enum OptionsAlignment { - /// The options are displayed below the field. - below, - - /// The options are displayed above the field. - /// - /// This is the default. - above; - - Anchor _toAnchor() { - switch (this) { - case OptionsAlignment.below: - return const Aligned( - widthFactor: 1, - follower: Alignment.topCenter, - target: Alignment.bottomCenter, - ); - case OptionsAlignment.above: - return const Aligned( - widthFactor: 1, - follower: Alignment.bottomCenter, - target: Alignment.topCenter, - ); - } - } -} - -/// The type of the Autocomplete callback which returns the widget that -/// contains the input [TextField] or [TextFormField]. -/// -/// See also: -/// -/// * [StreamAutocomplete.fieldViewBuilder], which is of this type. -typedef StreamAutocompleteFieldViewBuilder = Widget Function( - BuildContext context, - StreamMessageEditingController messageEditingController, - FocusNode focusNode, -); - -/// The type of the [StreamAutocompleteTrigger] callback which returns a -/// [Widget] that displays the specified [options]. -/// -/// See also: -/// -/// * [StreamAutocompleteTrigger.optionsViewBuilder], which is of this type. -typedef StreamAutocompleteOptionsViewBuilder = Widget Function( - BuildContext context, - StreamAutocompleteQuery autocompleteQuery, - StreamMessageEditingController messageEditingController, -); - -/// The query to determine the autocomplete options. -class StreamAutocompleteQuery { - /// Creates a [StreamAutocompleteQuery] with the specified [query] and - /// [selection]. - const StreamAutocompleteQuery({ - required this.query, - required this.selection, - }); - - /// The query string. - final String query; - - /// The selection in the text field. - final TextSelection selection; -} - -class _StreamAutocompleteInvokedTriggerWithQuery { - const _StreamAutocompleteInvokedTriggerWithQuery(this.trigger, this.query); - - final StreamAutocompleteTrigger trigger; - final StreamAutocompleteQuery query; -} - -/// The class responsible for triggering autocomplete suggestions and -/// displaying the options. -class StreamAutocompleteTrigger { - /// Creates a [StreamAutocompleteTrigger] which can be used to trigger - /// autocomplete suggestions. - const StreamAutocompleteTrigger({ - required this.trigger, - required this.optionsViewBuilder, - this.triggerOnlyAfterSpace = false, - this.triggerOnlyAtStart = false, - this.minimumRequiredCharacters = 0, - }); - - /// The trigger character. - /// - /// eg. '@', '#', ':' - final String trigger; - - /// Whether the [trigger] should only be recognised at the start of the input. - final bool triggerOnlyAtStart; - - /// Whether the [trigger] should only be recognised after a space. - final bool triggerOnlyAfterSpace; - - /// The minimum required characters for the [trigger] to start recognising - /// a autocomplete options. - final int minimumRequiredCharacters; - - /// Builds the widget responsible for querying and displaying the - /// autocomplete options. - /// - /// See also: - /// * [StreamAutocompleteOptions], which helps in displaying the options. - final StreamAutocompleteOptionsViewBuilder optionsViewBuilder; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StreamAutocompleteTrigger && - runtimeType == other.runtimeType && - trigger == other.trigger && - triggerOnlyAtStart == other.triggerOnlyAtStart && - triggerOnlyAfterSpace == other.triggerOnlyAfterSpace && - minimumRequiredCharacters == other.minimumRequiredCharacters; - - @override - int get hashCode => - trigger.hashCode ^ - triggerOnlyAtStart.hashCode ^ - triggerOnlyAfterSpace.hashCode ^ - minimumRequiredCharacters.hashCode; - - /// Checks if the user is invoking the recognising [trigger] and returns - /// the autocomplete query if so. - StreamAutocompleteQuery? invokingTrigger( - Message message, - TextEditingValue textEditingValue, - ) { - final text = textEditingValue.text; - final cursorPosition = textEditingValue.selection.baseOffset; - - // Find the first [trigger] location before the input cursor. - final firstTriggerIndexBeforeCursor = - text.substring(0, cursorPosition).lastIndexOf(trigger); - - // If the [trigger] is not found before the cursor, then it's not a trigger. - if (firstTriggerIndexBeforeCursor == -1) return null; - - // If the [trigger] is found before the cursor, but the [trigger] is only - // recognised at the start of the input, then it's not a trigger. - if (triggerOnlyAtStart && firstTriggerIndexBeforeCursor != 0) { - return null; - } - - // Only show typing suggestions after a space, or at the start of the input - // valid examples: "@user", "Hello @user" - // invalid examples: "Hello@user" - final textBeforeTrigger = text.substring(0, firstTriggerIndexBeforeCursor); - if (triggerOnlyAfterSpace && - textBeforeTrigger.isNotEmpty && - !textBeforeTrigger.endsWith(' ')) { - return null; - } - - // The suggestion range. Protect against invalid ranges. - final suggestionStart = firstTriggerIndexBeforeCursor + trigger.length; - final suggestionEnd = cursorPosition; - if (suggestionStart > suggestionEnd) return null; - - // Fetch the suggestion text. The suggestions can't have spaces. - // valid example: "@luke_skywa..." - // invalid example: "@luke skywa..." - final suggestionText = text.substring(suggestionStart, suggestionEnd); - if (suggestionText.contains(' ')) return null; - - // A minimum number of characters can be provided to only show - // suggestions after the customer has input enough characters. - if (suggestionText.length < minimumRequiredCharacters) return null; - - return StreamAutocompleteQuery( - query: suggestionText, - selection: TextSelection( - baseOffset: suggestionStart, - extentOffset: suggestionEnd, - ), - ); - } -} - -/// A widget that provides a text field with autocomplete functionality. -class StreamAutocomplete extends StatefulWidget { - /// Create an instance of StreamAutocomplete. - /// - /// [displayStringForOption], [optionsBuilder] and [optionsViewBuilder] must - /// not be null. - const StreamAutocomplete({ - super.key, - this.focusNode, - this.messageEditingController, - required this.autocompleteTriggers, - this.fieldViewBuilder = _defaultFieldViewBuilder, - this.optionsAlignment = OptionsAlignment.above, - this.debounceDuration = const Duration(milliseconds: 300), - }) : assert((focusNode == null) == (messageEditingController == null), ''); - - /// The triggers that trigger autocomplete. - final Iterable autocompleteTriggers; - - /// Builds the field whose input is used to get the options. - /// - /// Pass the provided [StreamMessageEditingController] to the field built - /// here so that StreamAutocomplete can listen for changes. - final StreamAutocompleteFieldViewBuilder fieldViewBuilder; - - /// The [FocusNode] that is used for the text field. - /// - /// The main purpose of this parameter is to allow the use of a separate text - /// field located in another part of the widget tree instead of the text - /// field built by [fieldViewBuilder]. For example, it may be desirable to - /// place the text field in the AppBar and the options below in the main body. - /// - /// When following this pattern, [fieldViewBuilder] can return - /// `SizedBox.shrink()` so that nothing is drawn where the text field would - /// normally be. A separate text field can be created elsewhere, and a - /// FocusNode and StreamMessageEditingController can be passed both to that - /// text field and to StreamAutocomplete. - /// - /// If this parameter is not null, then [messageEditingController] must also - /// be not null. - final FocusNode? focusNode; - - /// The [StreamMessageEditingController] that is used for the text field. - /// - /// If this parameter is not null, then [focusNode] must also be not null. - final StreamMessageEditingController? messageEditingController; - - /// The alignment of the options. - /// - /// The default value is [OptionsAlignment.above]. - final OptionsAlignment optionsAlignment; - - /// The duration of the debounce period for the - /// [StreamMessageEditingController]. - /// - /// The default value is [300ms]. - final Duration debounceDuration; - - static Widget _defaultFieldViewBuilder( - BuildContext context, - StreamMessageEditingController messageEditingController, - FocusNode focusNode, - ) { - return _StreamAutocompleteField( - focusNode: focusNode, - messageEditingController: messageEditingController, - ); - } - - /// Returns the nearest [StreamAutocomplete] ancestor of the given context. - static _StreamAutocompleteState of(BuildContext context) { - final state = context.findAncestorStateOfType<_StreamAutocompleteState>(); - assert(state != null, 'StreamAutocomplete not found in the widget tree'); - return state!; - } - - @override - _StreamAutocompleteState createState() => _StreamAutocompleteState(); -} - -class _StreamAutocompleteState extends State { - late StreamMessageEditingController _messageEditingController; - late FocusNode _focusNode; - - StreamAutocompleteQuery? _currentQuery; - StreamAutocompleteTrigger? _currentTrigger; - - bool _hideOptions = false; - String _lastFieldText = ''; - - // True if the state indicates that the options should be visible. - bool get _shouldShowOptions { - return !_hideOptions && - _focusNode.hasFocus && - _currentQuery != null && - _currentTrigger != null; - } - - /// Accepts and replaces the current query with the given [option] and closes - /// the suggested options. - /// - /// Optionally, pass [keepTrigger] false to remove the trigger from the text. - void acceptAutocompleteOption( - String option, { - bool keepTrigger = true, - }) { - if (option.isEmpty) return; - - final query = _currentQuery; - final trigger = _currentTrigger; - if (query == null || trigger == null) return; - - final querySelection = query.selection; - final text = _messageEditingController.text; - - var start = querySelection.baseOffset; - if (!keepTrigger) start -= 1; - - final end = querySelection.extentOffset; - - final alreadyContainsSpace = text.substring(end).startsWith(' '); - // Having extra space helps dismissing the auto-completion view. - // ignore: parameter_assignments - if (!alreadyContainsSpace) option += ' '; - - var selectionOffset = start + option.length; - // In case the extra space is already there, we need to move the cursor - // after the space. - if (alreadyContainsSpace) selectionOffset += 1; - - final newText = text.replaceRange(start, end, option); - final newSelection = TextSelection.collapsed(offset: selectionOffset); - - _messageEditingController.textEditingValue = TextEditingValue( - text: newText, - selection: newSelection, - ); - - return closeSuggestions(); - } - - /// Closes the suggestions and resets the current query. - void closeSuggestions() { - final prev = _currentQuery; - if (prev == null) return; - - _currentQuery = null; - if (mounted) setState(() {}); - } - - /// Starts showing the suggestions for the given [query]. - void showSuggestions( - StreamAutocompleteQuery query, - StreamAutocompleteTrigger trigger, - ) { - final prevQuery = _currentQuery; - final prevTrigger = _currentTrigger; - if (prevQuery == query && prevTrigger == trigger) return; - - _currentQuery = query; - _currentTrigger = trigger; - if (mounted) setState(() {}); - } - - // Checks if there is any invoked autocomplete trigger and returns the first - // one along with the query that matches the current input. - _StreamAutocompleteInvokedTriggerWithQuery? _getInvokedTriggerWithQuery( - Message messageValue, - TextEditingValue textEditingValue, - ) { - final autocompleteTriggers = widget.autocompleteTriggers.toSet(); - for (final trigger in autocompleteTriggers) { - final query = trigger.invokingTrigger(messageValue, textEditingValue); - if (query != null) { - return _StreamAutocompleteInvokedTriggerWithQuery(trigger, query); - } - } - return null; - } - - // Called when _textEditingController changes. - late final _onChangedField = debounce( - () { - final messageValue = _messageEditingController.message; - final textEditingValue = _messageEditingController.textEditingValue; - - // If the content has not changed, then there is nothing to do. - if (textEditingValue.text == _lastFieldText) return; - - // Make sure the options are no longer hidden if the content of the - // field changes. - _hideOptions = false; - _lastFieldText = textEditingValue.text; - - // If the text field is empty, then there is no need to do anything. - if (textEditingValue.text.isEmpty) return closeSuggestions(); - - // If the text field is not empty, then we need to check if the - // text field contains a trigger. - final _triggerWithQuery = _getInvokedTriggerWithQuery( - messageValue, - textEditingValue, - ); - - // If the text field does not contain a trigger, then there is no need - // to do anything. - if (_triggerWithQuery == null) return closeSuggestions(); - - // If the text field contains a trigger, then we need to open the - // portal. - final trigger = _triggerWithQuery.trigger; - final query = _triggerWithQuery.query; - return showSuggestions(query, trigger); - }, - widget.debounceDuration, - ); - - // Called when the field's FocusNode changes. - void _onChangedFocus() { - // Options should no longer be hidden when the field is re-focused. - _hideOptions = !_focusNode.hasFocus; - if (mounted) setState(() {}); - } - - // Handle a potential change in textEditingController by properly disposing of - // the old one and setting up the new one, if needed. - void _updateTextEditingController( - StreamMessageEditingController? old, - StreamMessageEditingController? current, - ) { - if ((old == null && current == null) || old == current) { - return; - } - if (old == null) { - _messageEditingController - ..removeListener(_onChangedField) - ..dispose(); - _messageEditingController = current!; - } else if (current == null) { - _messageEditingController.removeListener(_onChangedField); - _messageEditingController = StreamMessageEditingController(); - } else { - _messageEditingController.removeListener(_onChangedField); - _messageEditingController = current; - } - _messageEditingController.addListener(_onChangedField); - } - - // Handle a potential change in focusNode by properly disposing of the old one - // and setting up the new one, if needed. - void _updateFocusNode(FocusNode? old, FocusNode? current) { - if ((old == null && current == null) || old == current) { - return; - } - if (old == null) { - _focusNode - ..removeListener(_onChangedFocus) - ..dispose(); - _focusNode = current!; - } else if (current == null) { - _focusNode.removeListener(_onChangedFocus); - _focusNode = FocusNode(); - } else { - _focusNode.removeListener(_onChangedFocus); - _focusNode = current; - } - _focusNode.addListener(_onChangedFocus); - } - - @override - void initState() { - super.initState(); - _messageEditingController = - widget.messageEditingController ?? StreamMessageEditingController(); - _messageEditingController.addListener(_onChangedField); - _focusNode = widget.focusNode ?? FocusNode(); - _focusNode.addListener(_onChangedFocus); - } - - @override - void didUpdateWidget(StreamAutocomplete oldWidget) { - super.didUpdateWidget(oldWidget); - _updateTextEditingController( - oldWidget.messageEditingController, - widget.messageEditingController, - ); - _updateFocusNode(oldWidget.focusNode, widget.focusNode); - } - - @override - void dispose() { - _messageEditingController.removeListener(_onChangedField); - if (widget.messageEditingController == null) { - _messageEditingController.dispose(); - } - _focusNode.removeListener(_onChangedFocus); - if (widget.focusNode == null) { - _focusNode.dispose(); - } - _onChangedField.cancel(); - closeSuggestions(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - // Adding additional builder so that [.of] works. - return Builder( - builder: (context) { - final anchor = widget.optionsAlignment._toAnchor(); - final shouldShowOptions = _shouldShowOptions; - final optionViewBuilder = shouldShowOptions - ? TextFieldTapRegion( - child: _currentTrigger!.optionsViewBuilder( - context, - _currentQuery!, - _messageEditingController, - ), - ) - : null; - - return PortalTarget( - anchor: anchor, - visible: shouldShowOptions, - portalFollower: optionViewBuilder, - child: widget.fieldViewBuilder( - context, - _messageEditingController, - _focusNode, - ), - ); - }, - ); - } -} - -// The default Material-style Autocomplete text field. -class _StreamAutocompleteField extends StatelessWidget { - const _StreamAutocompleteField({ - required this.focusNode, - required this.messageEditingController, - }); - - final FocusNode focusNode; - - final StreamMessageEditingController messageEditingController; - - @override - Widget build(BuildContext context) { - return StreamMessageTextField( - controller: messageEditingController, - focusNode: focusNode, - ); - } -} - -const _kDefaultStreamAutocompleteOptionsShape = RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(8)), -); - -/// A helper widget used to show the options of a [StreamAutocomplete]. -class StreamAutocompleteOptions extends StatelessWidget { - /// Creates a [StreamAutocompleteOptions] widget. - const StreamAutocompleteOptions({ - super.key, - this.color, - this.elevation = 2, - this.margin = const EdgeInsets.all(8), - this.clipBehavior = Clip.hardEdge, - required this.options, - this.maxHeight, - required this.optionBuilder, - this.headerBuilder, - this.shape = _kDefaultStreamAutocompleteOptionsShape, - }); - - /// The background color of the options card. - /// - /// Defaults to [StreamColorTheme.barsBg]. - final Color? color; - - /// The elevation of the options card. - /// - /// The default value is 2. - final double elevation; - - /// The margin of the options card. - /// - /// The default value is [EdgeInsets.all(8)]. - final EdgeInsetsGeometry margin; - - /// The clip behavior of the options card. - /// - /// The default value is [Clip.hardEdge]. - final Clip clipBehavior; - - /// The shape of the options card. - final ShapeBorder shape; - - /// The options to display. - final Iterable options; - - /// The maximum height of the options card. - /// - /// Defaults to half the height of the screen. - final double? maxHeight; - - /// The builder for the options. - final Widget Function(BuildContext context, T option) optionBuilder; - - /// The builder for the header of the options. - final WidgetBuilder? headerBuilder; - - @override - Widget build(BuildContext context) { - final height = MediaQuery.of(context).size.height; - final colorTheme = StreamChatTheme.of(context).colorTheme; - return Card( - margin: margin, - elevation: elevation, - color: color ?? colorTheme.barsBg, - shape: shape, - clipBehavior: clipBehavior, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (headerBuilder != null) ...[ - headerBuilder!(context), - const Divider(height: 0), - ], - LimitedBox( - maxHeight: maxHeight ?? height * 0.5, - child: ListView.builder( - shrinkWrap: true, - padding: EdgeInsets.zero, - itemCount: options.length, - itemBuilder: (context, index) { - final option = options.elementAt(index); - return optionBuilder(context, option); - }, - ), - ), - ], - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/autocomplete/stream_command_autocomplete_options.dart b/packages/stream_chat_flutter/lib/src/autocomplete/stream_command_autocomplete_options.dart deleted file mode 100644 index 4151acda63..0000000000 --- a/packages/stream_chat_flutter/lib/src/autocomplete/stream_command_autocomplete_options.dart +++ /dev/null @@ -1,181 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template commands_overlay} -/// Overlay for displaying commands that can be used -/// to interact with the channel. -/// {@endtemplate} -class StreamCommandAutocompleteOptions extends StatelessWidget { - /// Constructor for creating a [StreamCommandAutocompleteOptions] - const StreamCommandAutocompleteOptions({ - required this.query, - required this.channel, - this.onCommandSelected, - super.key, - }); - - /// Query for searching commands. - final String query; - - /// The channel to search for users. - final Channel channel; - - /// Callback called when a command is selected. - final ValueSetter? onCommandSelected; - - @override - Widget build(BuildContext context) { - final commands = channel.config?.commands.where((it) { - final normalizedQuery = query.toUpperCase(); - final normalizedName = it.name.toUpperCase(); - return normalizedName.contains(normalizedQuery); - }); - - if (commands == null || commands.isEmpty) return const SizedBox.shrink(); - - final streamChatTheme = StreamChatTheme.of(context); - final colorTheme = streamChatTheme.colorTheme; - final textTheme = streamChatTheme.textTheme; - - return StreamAutocompleteOptions( - options: commands, - headerBuilder: (context) { - return ListTile( - dense: true, - horizontalTitleGap: 0, - leading: StreamSvgIcon( - icon: StreamSvgIcons.lightning, - color: colorTheme.accentPrimary, - size: 28, - ), - title: Text( - context.translations.instantCommandsLabel, - style: TextStyle( - // ignore: deprecated_member_use - color: colorTheme.textHighEmphasis.withOpacity(0.5), - ), - ), - ); - }, - optionBuilder: (context, command) { - return ListTile( - dense: true, - horizontalTitleGap: 8, - leading: _CommandIcon(command: command), - title: Row( - children: [ - Text( - command.name.capitalize(), - style: const TextStyle( - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(width: 8), - Text( - '/${command.name} ${command.args}', - style: textTheme.body.copyWith( - color: colorTheme.textLowEmphasis, - ), - ), - ], - ), - onTap: onCommandSelected == null - ? null - : () => onCommandSelected!(command), - ); - }, - ); - } -} - -class _CommandIcon extends StatelessWidget { - const _CommandIcon({required this.command}); - - final Command command; - - @override - Widget build(BuildContext context) { - final _streamChatTheme = StreamChatTheme.of(context); - switch (command.name) { - case 'giphy': - return const CircleAvatar( - radius: 12, - child: StreamSvgIcon( - size: 24, - icon: StreamSvgIcons.giphy, - ), - ); - case 'ban': - return CircleAvatar( - backgroundColor: _streamChatTheme.colorTheme.accentPrimary, - radius: 12, - child: const StreamSvgIcon( - size: 16, - color: Colors.white, - icon: StreamSvgIcons.userRemove, - ), - ); - case 'flag': - return CircleAvatar( - backgroundColor: _streamChatTheme.colorTheme.accentPrimary, - radius: 12, - child: const StreamSvgIcon( - size: 14, - color: Colors.white, - icon: StreamSvgIcons.flag, - ), - ); - case 'imgur': - return CircleAvatar( - backgroundColor: _streamChatTheme.colorTheme.accentPrimary, - radius: 12, - child: const ClipOval( - child: StreamSvgIcon( - size: 24, - icon: StreamSvgIcons.imgur, - ), - ), - ); - case 'mute': - return CircleAvatar( - backgroundColor: _streamChatTheme.colorTheme.accentPrimary, - radius: 12, - child: const StreamSvgIcon( - size: 16, - color: Colors.white, - icon: StreamSvgIcons.mute, - ), - ); - case 'unban': - return CircleAvatar( - backgroundColor: _streamChatTheme.colorTheme.accentPrimary, - radius: 12, - child: const StreamSvgIcon( - size: 16, - color: Colors.white, - icon: StreamSvgIcons.userAdd, - ), - ); - case 'unmute': - return CircleAvatar( - backgroundColor: _streamChatTheme.colorTheme.accentPrimary, - radius: 12, - child: const StreamSvgIcon( - size: 16, - color: Colors.white, - icon: StreamSvgIcons.volumeUp, - ), - ); - default: - return CircleAvatar( - backgroundColor: _streamChatTheme.colorTheme.accentPrimary, - radius: 12, - child: const StreamSvgIcon( - size: 16, - color: Colors.white, - icon: StreamSvgIcons.lightning, - ), - ); - } - } -} diff --git a/packages/stream_chat_flutter/lib/src/autocomplete/stream_mention_autocomplete_options.dart b/packages/stream_chat_flutter/lib/src/autocomplete/stream_mention_autocomplete_options.dart deleted file mode 100644 index 4bc6391b8a..0000000000 --- a/packages/stream_chat_flutter/lib/src/autocomplete/stream_mention_autocomplete_options.dart +++ /dev/null @@ -1,166 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template user_mentions_overlay} -/// Overlay for displaying users that can be mentioned. -/// {@endtemplate} -class StreamMentionAutocompleteOptions extends StatefulWidget { - /// Constructor for creating a [StreamMentionAutocompleteOptions]. - StreamMentionAutocompleteOptions({ - super.key, - required this.query, - required this.channel, - this.client, - this.limit = 10, - this.mentionAllAppUsers = false, - this.mentionsTileBuilder, - this.onMentionUserTap, - }) : assert( - channel.state != null, - 'Channel ${channel.cid} is not yet initialized', - ), - assert( - !mentionAllAppUsers || (mentionAllAppUsers && client != null), - 'StreamChatClient is required in order to use mentionAllAppUsers', - ); - - /// Query for searching users. - final String query; - - /// Limit applied on user search results. - final int limit; - - /// The channel to search for users. - final Channel channel; - - /// The client to search for users in case [mentionAllAppUsers] is True. - final StreamChatClient? client; - - /// When enabled mentions search users across the entire app. - /// - /// Defaults to false. - final bool mentionAllAppUsers; - - /// Customize the tile for the mentions overlay. - final UserMentionTileBuilder? mentionsTileBuilder; - - /// Callback called when a user is selected. - final ValueSetter? onMentionUserTap; - - @override - _StreamMentionAutocompleteOptionsState createState() => - _StreamMentionAutocompleteOptionsState(); -} - -class _StreamMentionAutocompleteOptionsState - extends State { - late Future> userMentionsFuture; - - @override - void initState() { - super.initState(); - userMentionsFuture = queryMentions(widget.query); - } - - @override - void didUpdateWidget(covariant StreamMentionAutocompleteOptions oldWidget) { - super.didUpdateWidget(oldWidget); - if (widget.channel != oldWidget.channel || - widget.query != oldWidget.query || - widget.mentionAllAppUsers != oldWidget.mentionAllAppUsers || - widget.limit != oldWidget.limit) { - userMentionsFuture = queryMentions(widget.query); - } - } - - @override - Widget build(BuildContext context) { - return FutureBuilder>( - future: userMentionsFuture, - builder: (context, snapshot) { - if (snapshot.hasError) return const SizedBox.shrink(); - if (!snapshot.hasData) return const SizedBox.shrink(); - final users = snapshot.data!; - - return StreamAutocompleteOptions( - options: users, - optionBuilder: (context, user) { - final colorTheme = StreamChatTheme.of(context).colorTheme; - return Material( - color: colorTheme.barsBg, - child: InkWell( - onTap: widget.onMentionUserTap == null - ? null - : () => widget.onMentionUserTap!(user), - child: widget.mentionsTileBuilder?.call(context, user) ?? - StreamUserMentionTile(user), - ), - ); - }, - ); - }, - ); - } - - List get membersAndWatchers { - final state = widget.channel.state!; - return { - ...state.watchers, - ...state.members.map((it) => it.user), - }.whereType().toList(growable: false); - } - - Future> queryMentions(String query) async { - if (widget.mentionAllAppUsers) { - return _queryUsers(query); - } - - var channelState = widget.channel.state; - - channelState = channelState!; - final members = channelState.members; - - // By default, we return maximum 100 members via queryChannels api call. - // Thus it is safe to assume, that if number of members in channel.state - // is < 100, then all the members are already available on client side - // and we don't need to make any api call to queryMembers endpoint. - if (members.length < 100) { - final matchingUsers = membersAndWatchers.search(query); - return matchingUsers.toList(growable: false); - } - - final result = await _queryMembers(query); - return result - .map((it) => it.user) - .whereType() - .toList(growable: false); - } - - Future> _queryMembers(String query) async { - final response = await widget.channel.queryMembers( - pagination: PaginationParams(limit: widget.limit), - filter: query.isEmpty - ? const Filter.empty() - : Filter.autoComplete('name', query), - ); - return response.members; - } - - Future> _queryUsers(String query) async { - assert( - widget.client != null, - 'StreamChatClient is required in order to query all app users', - ); - final response = await widget.client!.queryUsers( - pagination: PaginationParams(limit: widget.limit), - filter: query.isEmpty - ? const Filter.empty() - : Filter.or([ - Filter.autoComplete('id', query), - Filter.autoComplete('name', query), - ]), - sort: [const SortOption('id', direction: SortOption.ASC)], - ); - return response.users; - } -} diff --git a/packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart b/packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart deleted file mode 100644 index 93483619fb..0000000000 --- a/packages/stream_chat_flutter/lib/src/avatars/gradient_avatar.dart +++ /dev/null @@ -1,272 +0,0 @@ -import 'dart:math'; -import 'dart:ui' as ui; - -import 'package:flutter/material.dart'; - -/// {@template streamGradientAvatar} -/// Fallback user avatar with a polygon gradient overlaid with text -/// {@endtemplate} -class StreamGradientAvatar extends StatefulWidget { - /// {@macro streamGradientAvatar} - const StreamGradientAvatar({ - super.key, - required this.name, - required this.userId, - }); - - /// Name of user to shorten and display - final String name; - - /// ID of user to be used for key - final String userId; - - @override - _StreamGradientAvatarState createState() => _StreamGradientAvatarState(); -} - -class _StreamGradientAvatarState extends State { - @override - Widget build(BuildContext context) { - return Center( - child: RepaintBoundary( - child: CustomPaint( - painter: PolygonGradientPainter( - widget.userId, - getShortenedName(widget.name), - DefaultTextStyle.of(context).style.fontFamily ?? 'Roboto', - ), - child: const SizedBox.expand(), - ), - ), - ); - } - - String getShortenedName(String name) { - var parts = name.split(' ')..removeWhere((e) => e == ''); - - if (parts.length > 2) { - parts = parts.take(2).toList(); - } - - var result = ''; - - for (var i = 0; i < parts.length; i++) { - result = result + parts[i][0].toUpperCase(); - } - - return result; - } -} - -/// {@template polygonGradientPainter} -/// Painter for bg polygon gradient -/// {@endtemplate} -class PolygonGradientPainter extends CustomPainter { - /// {@macro polygonGradientPainter} - PolygonGradientPainter( - this.userId, - this.username, - this.fontFamily, - ); - - /// Initial grid row count - static const int rowCount = 5; - - /// Initial grid column count - static const int columnCount = 5; - - /// User ID used for key - String userId; - - /// User name to display - String username; - - /// Font family to use - String fontFamily; - - @override - void paint(Canvas canvas, Size size) { - final rowUnit = size.width / columnCount; - final columnUnit = size.height / rowCount; - final rand = Random(userId.length); - - final squares = []; - final points = {}; - final gradient = colorGradients[rand.nextInt(colorGradients.length)]; - - for (var i = 0; i < rowCount; i++) { - for (var j = 0; j < columnCount; j++) { - final off1 = Offset(rowUnit * j, columnUnit * i); - final off2 = Offset(rowUnit * (j + 1), columnUnit * i); - final off3 = Offset(rowUnit * (j + 1), columnUnit * (i + 1)); - final off4 = Offset(rowUnit * j, columnUnit * (i + 1)); - - points.addAll([off1, off2, off3, off4]); - - final pointsList = points.toList(); - - final p1 = pointsList.indexOf(off1); - final p2 = pointsList.indexOf(off2); - final p3 = pointsList.indexOf(off3); - final p4 = pointsList.indexOf(off4); - - squares.add( - Offset4(p1, p2, p3, p4, i, j, rowCount, columnCount, gradient), - ); - } - } - - final list = transformPoints(points, size); - squares.forEach((e) => e.draw(canvas, list)); - - final smallerSide = size.width > size.height ? size.width : size.height; - - final textSize = smallerSide / 3; - - final dxShift = (username.length == 2 ? 1.45 : 0.9) * textSize / 2; - final dyShift = (username.length == 2 ? 1.0 : 1.65) * textSize / 2; - - final fontSize = username.length == 2 ? textSize : textSize * 1.5; - - TextPainter( - text: TextSpan( - text: username, - style: TextStyle( - fontFamily: fontFamily, - fontSize: fontSize, - fontWeight: FontWeight.w500, - // ignore: deprecated_member_use - color: Colors.white.withOpacity(0.7), - ), - ), - textAlign: TextAlign.center, - textDirection: TextDirection.ltr, - ) - ..layout(maxWidth: size.width) - ..paint( - canvas, - Offset( - (size.width / 2) - dxShift, - (size.height / 2) - dyShift, - ), - ); - } - - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) => false; - - /// Transforms initial grid into a polygon grid. - List transformPoints(Set points, Size size) { - final transformedList = []; - final orgList = points.toList(); - final rand = Random(userId.length); - - for (var i = 0; i < points.length; i++) { - final orgDx = orgList[i].dx; - final orgDy = orgList[i].dy; - - if (orgDx == 0 || - orgDy == 0 || - orgDx == size.width || - orgDy == size.height) { - transformedList.add(Offset(orgDx, orgDy)); - continue; - } - - final sign1 = rand.nextInt(2) == 1 ? 1 : -1; - final sign2 = rand.nextInt(2) == 1 ? 1 : -1; - - final dx = sign1 * 0.6 * rand.nextInt(size.width ~/ columnCount); - final dy = sign2 * 0.6 * rand.nextInt(size.height ~/ rowCount); - - transformedList.add(Offset(orgDx + dx, orgDy + dy)); - } - - return transformedList; - } -} - -/// {@template offset4} -/// Class for storing and drawing four points of a polygon. -/// {@endtemplate} -class Offset4 { - /// {@macro offset4} - Offset4( - this.p1, - this.p2, - this.p3, - this.p4, - this.row, - this.column, - this.rowSize, - this.colSize, - this.gradient, - ); - - /// Point 1 - int p1; - - /// Point 2 - int p2; - - /// Point 3 - int p3; - - /// Point 4 - int p4; - - /// Position of polygon on grid - int row; - - /// Position of polygon on grid - int column; - - /// Max row size - int rowSize; - - /// Max col size - int colSize; - - /// Gradient to be applied to polygon - List gradient; - - /// Draw the polygon on canvas - void draw(Canvas canvas, List points) { - final paint = Paint() - ..color = Color.fromARGB( - 255, - Random().nextInt(255), - Random().nextInt(255), - Random().nextInt(255), - ) - ..shader = ui.Gradient.linear( - points[p1], - points[p3], - gradient, - ); - - final backgroundPath = Path() - ..moveTo(points[p1].dx, points[p1].dy) - ..lineTo(points[p2].dx, points[p2].dy) - ..lineTo(points[p3].dx, points[p3].dy) - ..lineTo(points[p4].dx, points[p4].dy) - ..lineTo(points[p1].dx, points[p1].dy) - ..close(); - - canvas.drawPath(backgroundPath, paint); - } -} - -/// Gradient list for polygons -const colorGradients = [ - [Color(0xffffafbd), Color(0xffffc3a0)], - [Color(0xff2193b0), Color(0xff6dd5ed)], - [Color(0xffcc2b5e), Color(0xff753a88)], - [Color(0xffee9ca7), Color(0xffffdde1)], - [Color(0xff42275a), Color(0xff734b6d)], - [Color(0xffde6262), Color(0xffffb88c)], - [Color(0xff56ab2f), Color(0xffa8e063)], - [Color(0xff614385), Color(0xff516395)], - [Color(0xffeacda3), Color(0xffd6ae7b)], - [Color(0xff02aab0), Color(0xff00cdac)], -]; diff --git a/packages/stream_chat_flutter/lib/src/avatars/group_avatar.dart b/packages/stream_chat_flutter/lib/src/avatars/group_avatar.dart deleted file mode 100644 index 146a664318..0000000000 --- a/packages/stream_chat_flutter/lib/src/avatars/group_avatar.dart +++ /dev/null @@ -1,176 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// WidgetBuilder for [StreamGroupAvatar]. -typedef StreamGroupAvatarBuilder = Widget Function( - BuildContext context, - List members, - // ignore: avoid_positional_boolean_parameters - bool isSelected, -); - -/// {@template streamGroupAvatar} -/// Widget for constructing a group of images -/// {@endtemplate} -class StreamGroupAvatar extends StatelessWidget { - /// {@macro streamGroupAvatar} - const StreamGroupAvatar({ - super.key, - this.channel, - required this.members, - this.constraints, - this.onTap, - this.borderRadius, - this.selected = false, - this.selectionColor, - this.selectionThickness = 4, - }); - - /// The channel of the avatar - final Channel? channel; - - /// The list of members in the group whose avatars should be displayed. - final List members; - - /// Constraints on the widget - final BoxConstraints? constraints; - - /// The action to perform when the widget is tapped - final VoidCallback? onTap; - - /// If `true`, this widget should be highlighted. - /// - /// Defaults to `false`. - final bool selected; - - /// [BorderRadius] to pass to the widget - final BorderRadius? borderRadius; - - /// The color to highlight the widget with if [selected] is `true` - final Color? selectionColor; - - /// The value to use for the border thickness and padding of the - /// selected image - final double selectionThickness; - - @override - Widget build(BuildContext context) { - final channel = this.channel ?? StreamChannel.of(context).channel; - - assert(channel.state != null, 'Channel ${channel.id} is not initialized'); - - final streamChatTheme = StreamChatTheme.of(context); - final colorTheme = streamChatTheme.colorTheme; - final previewTheme = streamChatTheme.channelPreviewTheme.avatarTheme; - - Widget avatar = GestureDetector( - onTap: onTap, - child: ClipRRect( - borderRadius: - borderRadius ?? previewTheme?.borderRadius ?? BorderRadius.zero, - child: Container( - constraints: constraints ?? previewTheme?.constraints, - decoration: BoxDecoration(color: colorTheme.accentPrimary), - child: Flex( - direction: Axis.vertical, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Flexible( - fit: FlexFit.tight, - child: Flex( - direction: Axis.horizontal, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: members - .take(2) - .map( - (member) => Flexible( - fit: FlexFit.tight, - child: FittedBox( - fit: BoxFit.cover, - clipBehavior: Clip.antiAlias, - child: Transform.scale( - scale: 1.2, - child: BetterStreamBuilder( - stream: channel.state!.membersStream.map( - (members) => members.firstWhere( - (it) => it.userId == member.userId, - orElse: () => member, - ), - ), - initialData: member, - builder: (context, member) => StreamUserAvatar( - showOnlineStatus: false, - user: member.user!, - borderRadius: BorderRadius.zero, - ), - ), - ), - ), - ), - ) - .toList(), - ), - ), - if (members.length > 2) - Flexible( - fit: FlexFit.tight, - child: Flex( - direction: Axis.horizontal, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: members - .skip(2) - .take(2) - .map( - (member) => Flexible( - fit: FlexFit.tight, - child: FittedBox( - fit: BoxFit.cover, - clipBehavior: Clip.antiAlias, - child: Transform.scale( - scale: 1.2, - child: BetterStreamBuilder( - stream: channel.state!.membersStream.map( - (members) => members.firstWhere( - (it) => it.userId == member.userId, - orElse: () => member, - ), - ), - initialData: member, - builder: (context, member) => - StreamUserAvatar( - showOnlineStatus: false, - user: member.user!, - borderRadius: BorderRadius.zero, - ), - ), - ), - ), - ), - ) - .toList(), - ), - ), - ], - ), - ), - ), - ); - - if (selected) { - avatar = ClipRRect( - borderRadius: BorderRadius.circular(selectionThickness) + - (borderRadius ?? previewTheme?.borderRadius ?? BorderRadius.zero), - child: Container( - constraints: constraints ?? previewTheme?.constraints, - color: selectionColor ?? colorTheme.accentPrimary, - child: Padding( - padding: EdgeInsets.all(selectionThickness), - child: avatar, - ), - ), - ); - } - - return avatar; - } -} diff --git a/packages/stream_chat_flutter/lib/src/avatars/user_avatar.dart b/packages/stream_chat_flutter/lib/src/avatars/user_avatar.dart deleted file mode 100644 index aae50f481e..0000000000 --- a/packages/stream_chat_flutter/lib/src/avatars/user_avatar.dart +++ /dev/null @@ -1,173 +0,0 @@ -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// WidgetBuilder for [StreamUserAvatar]. -typedef StreamUserAvatarBuilder = Widget Function( - BuildContext context, - User user, - // ignore: avoid_positional_boolean_parameters - bool isSelected, -); - -/// {@template streamUserAvatar} -/// Displays a user's avatar. -/// {@endtemplate} -class StreamUserAvatar extends StatelessWidget { - /// {@macro streamUserAvatar} - const StreamUserAvatar({ - super.key, - required this.user, - this.constraints, - this.onlineIndicatorConstraints, - this.onTap, - this.onLongPress, - this.showOnlineStatus = true, - this.borderRadius, - this.onlineIndicatorAlignment = Alignment.topRight, - this.selected = false, - this.selectionColor, - this.selectionThickness = 4, - this.placeholder, - }); - - /// User whose avatar is to be displayed - final User user; - - /// Alignment of the online indicator - /// - /// Defaults to `Alignment.topRight` - final Alignment onlineIndicatorAlignment; - - /// Sizing constraints of the avatar - final BoxConstraints? constraints; - - /// [BorderRadius] of the image - final BorderRadius? borderRadius; - - /// Sizing constraints of the online indicator - final BoxConstraints? onlineIndicatorConstraints; - - /// {@macro onUserAvatarTap} - final OnUserAvatarPress? onTap; - - /// {@macro onUserAvatarTap} - final OnUserAvatarPress? onLongPress; - - /// Flag for showing online status - /// - /// Defaults to `true` - final bool showOnlineStatus; - - /// Flag for if avatar is selected - /// - /// Defaults to `false` - final bool selected; - - /// Color of selection - final Color? selectionColor; - - /// Selection thickness around the avatar - /// - /// Defaults to `4` - final double selectionThickness; - - /// {@macro placeholderUserImage} - final PlaceholderUserImage? placeholder; - - @override - Widget build(BuildContext context) { - final hasImage = user.image != null && user.image!.isNotEmpty; - final streamChatTheme = StreamChatTheme.of(context); - final streamChatConfig = StreamChatConfiguration.of(context); - - final placeholder = - this.placeholder ?? streamChatConfig.placeholderUserImage; - - final backupGradientAvatar = ClipRRect( - borderRadius: borderRadius ?? - streamChatTheme.ownMessageTheme.avatarTheme?.borderRadius ?? - BorderRadius.zero, - child: streamChatConfig.defaultUserImage(context, user), - ); - - Widget avatar = FittedBox( - fit: BoxFit.cover, - child: Container( - constraints: constraints ?? - streamChatTheme.ownMessageTheme.avatarTheme?.constraints, - child: hasImage - ? CachedNetworkImage( - fit: BoxFit.cover, - filterQuality: FilterQuality.high, - imageUrl: user.image!, - errorWidget: (context, __, ___) => backupGradientAvatar, - placeholder: placeholder != null - ? (context, __) => placeholder(context, user) - : null, - imageBuilder: (context, imageProvider) => DecoratedBox( - decoration: BoxDecoration( - borderRadius: borderRadius ?? - streamChatTheme - .ownMessageTheme.avatarTheme?.borderRadius, - image: DecorationImage( - image: imageProvider, - fit: BoxFit.cover, - ), - ), - ), - ) - : backupGradientAvatar, - ), - ); - - if (selected) { - avatar = ClipRRect( - borderRadius: (borderRadius ?? - streamChatTheme.ownMessageTheme.avatarTheme?.borderRadius ?? - BorderRadius.zero) + - BorderRadius.circular(selectionThickness), - child: Container( - constraints: constraints ?? - streamChatTheme.ownMessageTheme.avatarTheme?.constraints, - color: selectionColor ?? streamChatTheme.colorTheme.accentPrimary, - child: Padding( - padding: EdgeInsets.all(selectionThickness), - child: avatar, - ), - ), - ); - } - return GestureDetector( - onTap: onTap != null ? () => onTap!(user) : null, - onLongPress: onLongPress != null ? () => onLongPress!(user) : null, - child: Stack( - children: [ - avatar, - if (showOnlineStatus && user.online) - Positioned.fill( - child: Align( - alignment: onlineIndicatorAlignment, - child: Material( - type: MaterialType.circle, - color: streamChatTheme.colorTheme.barsBg, - child: Container( - margin: const EdgeInsets.all(2), - constraints: onlineIndicatorConstraints ?? - const BoxConstraints.tightFor( - width: 8, - height: 8, - ), - child: Material( - shape: const CircleBorder(), - color: streamChatTheme.colorTheme.accentInfo, - ), - ), - ), - ), - ), - ], - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/bottom_sheets/attachment_modal_sheet.dart b/packages/stream_chat_flutter/lib/src/bottom_sheets/attachment_modal_sheet.dart deleted file mode 100644 index 7367e0201c..0000000000 --- a/packages/stream_chat_flutter/lib/src/bottom_sheets/attachment_modal_sheet.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template attachmentModalSheet} -/// The modalBottomSheet that appears when a mobile user attempts to add -/// attachments to a chat. -/// -/// Should not be used on desktop or web. -/// {@endtemplate} -class AttachmentModalSheet extends StatelessWidget { - /// {@macro attachmentModalSheet} - const AttachmentModalSheet({ - super.key, - required this.onFileTap, - required this.onPhotoTap, - required this.onVideoTap, - }); - - /// The action to perform when the "file" button is tapped. - final VoidCallback onFileTap; - - /// The action to perform when the "photo" button is tapped. - final VoidCallback onPhotoTap; - - /// The action to perform when the "video" button is tapped. - final VoidCallback onVideoTap; - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - ListTile( - title: Text( - context.translations.addAFileLabel, - style: const TextStyle( - fontWeight: FontWeight.bold, - ), - ), - ), - ListTile( - leading: const Icon(Icons.image), - title: Text(context.translations.uploadAPhotoLabel), - onTap: () { - onPhotoTap.call(); - Navigator.of(context).pop(); - }, - ), - ListTile( - leading: const Icon(Icons.video_library), - title: Text(context.translations.uploadAVideoLabel), - onTap: () { - onVideoTap.call(); - Navigator.of(context).pop(); - }, - ), - ListTile( - leading: const Icon(Icons.insert_drive_file), - title: Text(context.translations.uploadAFileLabel), - onTap: () { - onFileTap.call(); - Navigator.of(context).pop(); - }, - ), - ], - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/bottom_sheets/edit_message_sheet.dart b/packages/stream_chat_flutter/lib/src/bottom_sheets/edit_message_sheet.dart deleted file mode 100644 index 7be707fb8f..0000000000 --- a/packages/stream_chat_flutter/lib/src/bottom_sheets/edit_message_sheet.dart +++ /dev/null @@ -1,102 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template editMessageSheet} -/// Allows a user to edit the selected message. -/// {@endtemplate} -class EditMessageSheet extends StatefulWidget { - /// {@macro editMessageSheet} - const EditMessageSheet({ - super.key, - required this.message, - required this.channel, - this.editMessageInputBuilder, - }); - - /// {@macro editMessageInputBuilder} - final EditMessageInputBuilder? editMessageInputBuilder; - - /// The message to edit. - final Message message; - - /// The [StreamChannel] above this widget. - final Channel channel; - - @override - State createState() => _EditMessageSheetState(); -} - -class _EditMessageSheetState extends State { - late final controller = StreamMessageInputController( - message: widget.message, - ); - - @override - void dispose() { - controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final streamChatThemeData = StreamChatTheme.of(context); - return KeyboardShortcutRunner( - onEscapeKeypress: () => Navigator.of(context).pop(), - child: Padding( - padding: MediaQuery.of(context).viewInsets, - child: StreamChannel( - channel: widget.channel, - child: Flex( - direction: Axis.vertical, - mainAxisAlignment: MainAxisAlignment.end, - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(8, 8, 8, 0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Padding( - padding: const EdgeInsets.all(8), - child: StreamSvgIcon( - icon: StreamSvgIcons.edit, - color: streamChatThemeData.colorTheme.disabled, - ), - ), - Text( - context.translations.editMessageLabel, - style: const TextStyle(fontWeight: FontWeight.bold), - ), - IconButton( - visualDensity: VisualDensity.compact, - icon: StreamSvgIcon( - icon: StreamSvgIcons.closeSmall, - color: streamChatThemeData.colorTheme.textLowEmphasis, - ), - onPressed: Navigator.of(context).pop, - ), - ], - ), - ), - if (widget.editMessageInputBuilder != null) - widget.editMessageInputBuilder!(context, widget.message) - else - StreamMessageInput( - messageInputController: controller, - // Disallow editing poll for now as it's not supported. - allowedAttachmentPickerTypes: [ - ...AttachmentPickerType.values, - ]..remove(AttachmentPickerType.poll), - preMessageSending: (m) { - FocusScope.of(context).unfocus(); - Navigator.of(context).pop(); - return m; - }, - ), - ], - ), - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/bottom_sheets/error_alert_sheet.dart b/packages/stream_chat_flutter/lib/src/bottom_sheets/error_alert_sheet.dart deleted file mode 100644 index 744664dd8f..0000000000 --- a/packages/stream_chat_flutter/lib/src/bottom_sheets/error_alert_sheet.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template errorAlertSheet} -/// A bottom sheet that displays when an error occurs. -/// -/// Should only be used on mobile platforms. -/// {@endtemplate} -class ErrorAlertSheet extends StatelessWidget { - /// {@macro errorAlertSheet} - const ErrorAlertSheet({ - super.key, - required this.errorDescription, - }); - - /// The description of the error. - final String errorDescription; - - @override - Widget build(BuildContext context) { - final _streamChatTheme = StreamChatTheme.of(context); - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox( - height: 26, - ), - StreamSvgIcon( - icon: StreamSvgIcons.error, - color: _streamChatTheme.colorTheme.accentError, - size: 24, - ), - const SizedBox( - height: 26, - ), - Text( - context.translations.somethingWentWrongError, - style: _streamChatTheme.textTheme.headlineBold, - ), - const SizedBox( - height: 7, - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Text( - errorDescription, - textAlign: TextAlign.center, - ), - ), - const SizedBox( - height: 36, - ), - Container( - // ignore: deprecated_member_use - color: _streamChatTheme.colorTheme.textHighEmphasis.withOpacity(0.08), - height: 1, - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: Text( - context.translations.okLabel, - style: _streamChatTheme.textTheme.bodyBold.copyWith( - color: _streamChatTheme.colorTheme.accentPrimary, - ), - ), - ), - ], - ), - ], - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/bottom_sheets/stream_channel_info_bottom_sheet.dart b/packages/stream_chat_flutter/lib/src/bottom_sheets/stream_channel_info_bottom_sheet.dart deleted file mode 100644 index a93b9b8fb6..0000000000 --- a/packages/stream_chat_flutter/lib/src/bottom_sheets/stream_channel_info_bottom_sheet.dart +++ /dev/null @@ -1,359 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// A [BottomSheet] that shows information about a [Channel]. -class StreamChannelInfoBottomSheet extends StatelessWidget { - /// Creates a new instance [StreamChannelInfoBottomSheet] widget. - StreamChannelInfoBottomSheet({ - super.key, - required this.channel, - this.onMemberTap, - this.onViewInfoTap, - this.onLeaveChannelTap, - this.onDeleteConversationTap, - this.onCancelTap, - }) : assert( - channel.state != null, - 'Channel ${channel.id} is not initialized', - ); - - /// The [Channel] to show information about. - final Channel channel; - - /// A callback that is called when a member is tapped. - final void Function(Member)? onMemberTap; - - /// A callback that is called when the "View Info" button is tapped. - final VoidCallback? onViewInfoTap; - - /// A callback that is called when the "Leave Channel" button is tapped. - /// - /// Only shown when the channel is a group channel. - final VoidCallback? onLeaveChannelTap; - - /// A callback that is called when the "Delete Conversation" button is tapped. - /// - /// Only shown when you are the `owner` of the channel. - final VoidCallback? onDeleteConversationTap; - - /// A callback that is called when the "Cancel" button is tapped. - final VoidCallback? onCancelTap; - - @override - Widget build(BuildContext context) { - final themeData = StreamChatTheme.of(context); - final colorTheme = themeData.colorTheme; - final channelPreviewTheme = StreamChannelPreviewTheme.of(context); - - final currentUser = channel.client.state.currentUser; - final isOneToOneChannel = channel.isDistinct && channel.memberCount == 2; - - final members = channel.state?.members ?? []; - - // remove current user in case it's 1-1 conversation - if (isOneToOneChannel) { - members.removeWhere((it) => it.user?.id == currentUser?.id); - } - - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(height: 24), - Center( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: StreamChannelName( - channel: channel, - textStyle: themeData.textTheme.headlineBold, - ), - ), - ), - const SizedBox(height: 5), - Center( - // TODO: Refactor ChannelInfo - child: StreamChannelInfo( - showTypingIndicator: false, - channel: channel, - textStyle: channelPreviewTheme.subtitleStyle, - ), - ), - const SizedBox(height: 17), - Container( - height: 94, - alignment: Alignment.center, - child: ListView.separated( - shrinkWrap: true, - itemCount: members.length, - scrollDirection: Axis.horizontal, - padding: const EdgeInsets.symmetric(horizontal: 8), - separatorBuilder: (context, index) => const SizedBox(width: 16), - itemBuilder: (context, index) { - final member = members[index]; - final user = member.user!; - return Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - StreamUserAvatar( - user: user, - constraints: const BoxConstraints.tightFor( - height: 64, - width: 64, - ), - borderRadius: BorderRadius.circular(32), - onlineIndicatorConstraints: BoxConstraints.tight( - const Size(12, 12), - ), - onTap: onMemberTap != null - ? (_) => onMemberTap!(member) - : null, - ), - const SizedBox(height: 6), - Text( - user.name, - style: themeData.textTheme.footnoteBold, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ], - ); - }, - ), - ), - const SizedBox(height: 24), - StreamOptionListTile( - leading: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: StreamSvgIcon( - icon: StreamSvgIcons.user, - color: colorTheme.textLowEmphasis, - ), - ), - title: context.translations.viewInfoLabel, - onTap: onViewInfoTap, - ), - if (!isOneToOneChannel) - StreamOptionListTile( - title: context.translations.leaveGroupLabel, - leading: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: StreamSvgIcon( - icon: StreamSvgIcons.userRemove, - color: colorTheme.textLowEmphasis, - ), - ), - onTap: onLeaveChannelTap, - ), - if (channel.ownCapabilities.contains(PermissionType.deleteChannel)) - StreamOptionListTile( - leading: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: StreamSvgIcon( - icon: StreamSvgIcons.delete, - color: colorTheme.accentError, - ), - ), - title: context.translations.deleteConversationLabel, - titleColor: colorTheme.accentError, - onTap: onDeleteConversationTap, - ), - StreamOptionListTile( - leading: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: StreamSvgIcon( - icon: StreamSvgIcons.closeSmall, - color: colorTheme.textLowEmphasis, - ), - ), - title: context.translations.cancelLabel, - onTap: onCancelTap ?? Navigator.of(context).pop, - ), - ], - ); - } -} - -const _kDefaultChannelInfoBottomSheetShape = RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(32), - topRight: Radius.circular(32), - ), -); - -/// Shows a modal material design bottom sheet. -/// -/// A modal bottom sheet is an alternative to a menu or a dialog and prevents -/// the user from interacting with the rest of the app. -/// -/// A closely related widget is a persistent bottom sheet, which shows -/// information that supplements the primary content of the app without -/// preventing the use from interacting with the app. Persistent bottom sheets -/// can be created and displayed with the [showBottomSheet] function or the -/// [ScaffoldState.showBottomSheet] method. -/// -/// The `context` argument is used to look up the [Navigator] and [Theme] for -/// the bottom sheet. It is only used when the method is called. Its -/// corresponding widget can be safely removed from the tree before the bottom -/// sheet is closed. -/// -/// The `isScrollControlled` parameter specifies whether this is a route for -/// a bottom sheet that will utilize [DraggableScrollableSheet]. If you wish -/// to have a bottom sheet that has a scrollable child such as a [ListView] or -/// a [GridView] and have the bottom sheet be draggable, you should set this -/// parameter to true. -/// -/// The `useRootNavigator` parameter ensures that the root navigator is used to -/// display the [BottomSheet] when set to `true`. This is useful in the case -/// that a modal [BottomSheet] needs to be displayed above all other content -/// but the caller is inside another [Navigator]. -/// -/// The [isDismissible] parameter specifies whether the bottom sheet will be -/// dismissed when user taps on the scrim. -/// -/// The [enableDrag] parameter specifies whether the bottom sheet can be -/// dragged up and down and dismissed by swiping downwards. -/// -/// The optional [backgroundColor], [elevation], [shape], [clipBehavior], -/// [constraints] and [transitionAnimationController] -/// parameters can be passed in to customize the appearance and behavior of -/// modal bottom sheets (see the documentation for these on [BottomSheet] -/// for more details). -/// -/// The [transitionAnimationController] controls the bottom sheet's entrance and -/// exit animations if provided. -/// -/// The optional `routeSettings` parameter sets the [RouteSettings] -/// of the modal bottom sheet sheet. -/// This is particularly useful in the case that a user wants to observe -/// [PopupRoute]s within a [NavigatorObserver]. -/// -/// Returns a `Future` that resolves to the value (if any) that was passed to -/// [Navigator.pop] when the modal bottom sheet was closed. -/// -/// See also: -/// -/// * [BottomSheet], which becomes the parent of the widget returned by the -/// function passed as the `builder` argument to [showModalBottomSheet]. -/// * [showBottomSheet] and [ScaffoldState.showBottomSheet], for showing -/// non-modal bottom sheets. -/// * [DraggableScrollableSheet], which allows you to create a bottom sheet -/// that grows and then becomes scrollable once it reaches its maximum size. -/// * -Future showChannelInfoModalBottomSheet({ - required BuildContext context, - required Channel channel, - Color? backgroundColor, - double? elevation, - BoxConstraints? constraints, - Color? barrierColor, - bool isScrollControlled = true, - bool useRootNavigator = false, - bool isDismissible = true, - bool enableDrag = true, - RouteSettings? routeSettings, - AnimationController? transitionAnimationController, - Clip? clipBehavior = Clip.hardEdge, - ShapeBorder? shape = _kDefaultChannelInfoBottomSheetShape, - void Function(Member)? onMemberTap, - VoidCallback? onViewInfoTap, - VoidCallback? onLeaveChannelTap, - VoidCallback? onDeleteConversationTap, - VoidCallback? onCancelTap, -}) => - showModalBottomSheet( - context: context, - backgroundColor: backgroundColor, - elevation: elevation, - shape: shape, - clipBehavior: clipBehavior, - constraints: constraints, - barrierColor: barrierColor, - isScrollControlled: isScrollControlled, - useRootNavigator: useRootNavigator, - isDismissible: isDismissible, - enableDrag: enableDrag, - routeSettings: routeSettings, - transitionAnimationController: transitionAnimationController, - builder: (BuildContext context) => StreamChannelInfoBottomSheet( - channel: channel, - onMemberTap: onMemberTap, - onViewInfoTap: onViewInfoTap, - onLeaveChannelTap: onLeaveChannelTap, - onDeleteConversationTap: onDeleteConversationTap, - onCancelTap: onCancelTap, - ), - ); - -/// Shows a material design bottom sheet in the nearest [Scaffold] ancestor. If -/// you wish to show a persistent bottom sheet, use [Scaffold.bottomSheet]. -/// -/// Returns a controller that can be used to close and otherwise manipulate the -/// bottom sheet. -/// -/// The optional [backgroundColor], [elevation], [shape], [clipBehavior], -/// [constraints] and [transitionAnimationController] -/// parameters can be passed in to customize the appearance and behavior of -/// persistent bottom sheets (see the documentation for these on [BottomSheet] -/// for more details). -/// -/// To rebuild the bottom sheet (e.g. if it is stateful), call -/// [PersistentBottomSheetController.setState] on the controller returned by -/// this method. -/// -/// The new bottom sheet becomes a [LocalHistoryEntry] for the enclosing -/// [ModalRoute] and a back button is added to the app bar of the [Scaffold] -/// that closes the bottom sheet. -/// -/// To create a persistent bottom sheet that is not a [LocalHistoryEntry] and -/// does not add a back button to the enclosing Scaffold's app bar, use the -/// [Scaffold.bottomSheet] constructor parameter. -/// -/// A closely related widget is a modal bottom sheet, which is an alternative -/// to a menu or a dialog and prevents the user from interacting with the rest -/// of the app. Modal bottom sheets can be created and displayed with the -/// [showModalBottomSheet] function. -/// -/// The `context` argument is used to look up the [Scaffold] for the bottom -/// sheet. It is only used when the method is called. Its corresponding widget -/// can be safely removed from the tree before the bottom sheet is closed. -/// -/// See also: -/// -/// * [BottomSheet], which becomes the parent of the widget returned by the -/// `builder`. -/// * [showModalBottomSheet], which can be used to display a modal bottom -/// sheet. -/// * [Scaffold.of], for information about how to obtain the [BuildContext]. -/// * -PersistentBottomSheetController showChannelInfoBottomSheet({ - required BuildContext context, - required Channel channel, - Color? backgroundColor, - double? elevation, - BoxConstraints? constraints, - AnimationController? transitionAnimationController, - Clip? clipBehavior = Clip.hardEdge, - ShapeBorder? shape = _kDefaultChannelInfoBottomSheetShape, - void Function(Member)? onMemberTap, - VoidCallback? onViewInfoTap, - VoidCallback? onLeaveChannelTap, - VoidCallback? onDeleteConversationTap, - VoidCallback? onCancelTap, -}) => - showBottomSheet( - context: context, - backgroundColor: backgroundColor, - elevation: elevation, - shape: shape, - clipBehavior: clipBehavior, - constraints: constraints, - transitionAnimationController: transitionAnimationController, - builder: (BuildContext context) => StreamChannelInfoBottomSheet( - channel: channel, - onMemberTap: onMemberTap, - onViewInfoTap: onViewInfoTap, - onLeaveChannelTap: onLeaveChannelTap, - onDeleteConversationTap: onDeleteConversationTap, - onCancelTap: onCancelTap, - ), - ); diff --git a/packages/stream_chat_flutter/lib/src/channel/channel_header.dart b/packages/stream_chat_flutter/lib/src/channel/channel_header.dart deleted file mode 100644 index b97af49222..0000000000 --- a/packages/stream_chat_flutter/lib/src/channel/channel_header.dart +++ /dev/null @@ -1,221 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamChannelHeader} -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_header.png) -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_header_paint.png) -/// -/// Shows information about the current [Channel]. -/// -/// ```dart -/// class MyApp extends StatelessWidget { -/// final StreamChatClient client; -/// final Channel channel; -/// -/// MyApp(this.client, this.channel); -/// -/// @override -/// Widget build(BuildContext context) { -/// return MaterialApp( -/// home: StreamChat( -/// client: client, -/// child: StreamChannel( -/// channel: channel, -/// child: Scaffold( -/// appBar: ChannelHeader(), -/// ), -/// ), -/// ), -/// ); -/// } -/// } -/// ``` -/// -/// Usually you would use this widget as an [AppBar] inside a [Scaffold]. -/// However, you can also use it as a normal widget. -/// -/// Make sure to have a [StreamChannel] ancestor in order to provide the -/// information about the channel. -/// -/// Every part of the widget uses a [StreamBuilder] to render the channel -/// information as soon as it updates. -/// -/// By default the widget shows a backButton that calls [Navigator.pop]. -/// You can disable this button using the [showBackButton] property. -/// Alternatively, you can override this behaviour via the [onBackPressed] -/// callback. -/// -/// The UI is rendered based on the first ancestor of type [StreamChatTheme] -/// and the [StreamChatThemeData.channelHeaderTheme] property. Modify it to -/// change the widget's appearance. -/// {@endtemplate} -class StreamChannelHeader extends StatelessWidget - implements PreferredSizeWidget { - /// {@macro streamChannelHeader} - const StreamChannelHeader({ - super.key, - this.showBackButton = true, - this.onBackPressed, - this.onTitleTap, - this.showTypingIndicator = true, - this.onImageTap, - this.showConnectionStateTile = false, - this.title, - this.subtitle, - this.centerTitle, - this.leading, - this.actions, - this.backgroundColor, - this.elevation = 1, - }) : preferredSize = const Size.fromHeight(kToolbarHeight); - - /// Whether to show the leading back button - /// - /// Defaults to `true` - final bool showBackButton; - - /// The action to perform when the back button is pressed. - /// - /// By default it calls [Navigator.pop] - final VoidCallback? onBackPressed; - - /// The action to perform when the header is tapped. - final VoidCallback? onTitleTap; - - /// The action to perform when the image is tapped. - final VoidCallback? onImageTap; - - /// Whether to show the typing indicator - /// - /// Defaults to `true` - final bool showTypingIndicator; - - /// Whether to show the connection state tile - final bool showConnectionStateTile; - - /// Title widget - final Widget? title; - - /// Subtitle widget - final Widget? subtitle; - - /// Whether the title should be centered - final bool? centerTitle; - - /// Leading widget - final Widget? leading; - - /// {@macro flutter.material.appbar.actions} - /// - /// The [StreamChannelAvatar] is shown by default - final List? actions; - - /// The background color for this [StreamChannelHeader]. - final Color? backgroundColor; - - /// The elevation for this [StreamChannelHeader]. - final double elevation; - - @override - final Size preferredSize; - - @override - Widget build(BuildContext context) { - final effectiveCenterTitle = getEffectiveCenterTitle( - Theme.of(context), - actions: actions, - centerTitle: centerTitle, - ); - final channel = StreamChannel.of(context).channel; - final channelHeaderTheme = StreamChannelHeaderTheme.of(context); - - final leadingWidget = leading ?? - (showBackButton - ? StreamBackButton( - onPressed: onBackPressed, - showUnreadCount: true, - ) - : const SizedBox()); - - return StreamConnectionStatusBuilder( - statusBuilder: (context, status) { - var statusString = ''; - var showStatus = true; - - switch (status) { - case ConnectionStatus.connected: - statusString = context.translations.connectedLabel; - showStatus = false; - break; - case ConnectionStatus.connecting: - statusString = context.translations.reconnectingLabel; - break; - case ConnectionStatus.disconnected: - statusString = context.translations.disconnectedLabel; - break; - } - - final theme = Theme.of(context); - - return StreamInfoTile( - showMessage: showConnectionStateTile && showStatus, - message: statusString, - child: AppBar( - toolbarTextStyle: theme.textTheme.bodyMedium, - titleTextStyle: theme.textTheme.titleLarge, - systemOverlayStyle: theme.brightness == Brightness.dark - ? SystemUiOverlayStyle.light - : SystemUiOverlayStyle.dark, - elevation: elevation, - leading: leadingWidget, - backgroundColor: backgroundColor ?? channelHeaderTheme.color, - actions: actions ?? - [ - Padding( - padding: const EdgeInsets.only(right: 10), - child: Center( - child: StreamChannelAvatar( - channel: channel, - borderRadius: - channelHeaderTheme.avatarTheme?.borderRadius, - constraints: - channelHeaderTheme.avatarTheme?.constraints, - onTap: onImageTap, - ), - ), - ), - ], - centerTitle: centerTitle, - title: InkWell( - onTap: onTitleTap, - child: SizedBox( - height: preferredSize.height, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: effectiveCenterTitle - ? CrossAxisAlignment.center - : CrossAxisAlignment.stretch, - children: [ - title ?? - StreamChannelName( - channel: channel, - textStyle: channelHeaderTheme.titleStyle, - ), - const SizedBox(height: 2), - subtitle ?? - StreamChannelInfo( - showTypingIndicator: showTypingIndicator, - channel: channel, - textStyle: channelHeaderTheme.subtitleStyle, - ), - ], - ), - ), - ), - ), - ); - }, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/channel/channel_info.dart b/packages/stream_chat_flutter/lib/src/channel/channel_info.dart deleted file mode 100644 index cbec3fda26..0000000000 --- a/packages/stream_chat_flutter/lib/src/channel/channel_info.dart +++ /dev/null @@ -1,198 +0,0 @@ -import 'package:collection/collection.dart' show IterableExtension; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamChannelInfo} -/// Displays information about the current [Channel]. -/// {@endtemplate} -class StreamChannelInfo extends StatelessWidget { - /// {@macro streamChannelInfo} - const StreamChannelInfo({ - super.key, - required this.channel, - this.textStyle, - this.showTypingIndicator = true, - this.parentId, - }); - - /// The channel to display information about - final Channel channel; - - /// The style of the text displayed - final TextStyle? textStyle; - - /// Whether to show the typing indicator - /// - /// Defaults to `true` - final bool showTypingIndicator; - - /// The ID of the parent message (in the case of a thread) - final String? parentId; - - @override - Widget build(BuildContext context) { - final client = StreamChat.of(context).client; - return BetterStreamBuilder>( - stream: channel.state!.membersStream, - initialData: channel.state!.members, - builder: (context, data) => StreamConnectionStatusBuilder( - statusBuilder: (context, status) { - switch (status) { - case ConnectionStatus.connected: - return _ConnectedTitleState( - channel: channel, - showTypingIndicator: showTypingIndicator, - textStyle: textStyle, - members: data, - parentId: parentId, - ); - case ConnectionStatus.connecting: - return _ConnectingTitleState(textStyle: textStyle); - case ConnectionStatus.disconnected: - return _DisconnectedTitleState( - client: client, - textStyle: textStyle, - ); - } - }, - ), - ); - } -} - -class _ConnectedTitleState extends StatelessWidget { - const _ConnectedTitleState({ - required this.channel, - required this.showTypingIndicator, - this.members, - this.textStyle, - this.parentId, - }); - - final Channel channel; - final List? members; - final TextStyle? textStyle; - final bool showTypingIndicator; - final String? parentId; - - @override - Widget build(BuildContext context) { - Widget? alternativeWidget; - - final memberCount = channel.memberCount; - if (memberCount != null && memberCount > 2) { - var text = context.translations.membersCountText(memberCount); - final onlineCount = - members?.where((m) => m.user?.online == true).length ?? 0; - if (onlineCount > 0) { - text += ', ${context.translations.watchersCountText(onlineCount)}'; - } - alternativeWidget = Text( - text, - style: StreamChannelHeaderTheme.of(context).subtitleStyle, - ); - } else { - final userId = StreamChat.of(context).currentUser?.id; - final otherMember = members?.firstWhereOrNull( - (element) => element.userId != userId, - ); - - if (otherMember != null) { - if (otherMember.user?.online == true) { - alternativeWidget = Text( - context.translations.userOnlineText, - style: textStyle, - ); - } else { - final lastActive = otherMember.user?.lastActive ?? DateTime.now(); - alternativeWidget = Text( - '${context.translations.userLastOnlineText} ' - '${Jiffy.parseFromDateTime(lastActive).fromNow()}', - style: textStyle, - ); - } - } - } - - if (!showTypingIndicator) { - return alternativeWidget ?? const Offstage(); - } - - return StreamTypingIndicator( - parentId: parentId, - alternativeWidget: alternativeWidget, - style: textStyle, - ); - } -} - -class _ConnectingTitleState extends StatelessWidget { - const _ConnectingTitleState({ - this.textStyle, - }); - - final TextStyle? textStyle; - - @override - Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox( - height: 16, - width: 16, - child: Center( - child: CircularProgressIndicator.adaptive(), - ), - ), - const SizedBox(width: 10), - Text( - context.translations.searchingForNetworkText, - style: textStyle, - ), - ], - ); - } -} - -class _DisconnectedTitleState extends StatelessWidget { - const _DisconnectedTitleState({ - required this.client, - this.textStyle, - }); - - final StreamChatClient client; - final TextStyle? textStyle; - - @override - Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - context.translations.offlineLabel, - style: textStyle, - ), - TextButton( - style: TextButton.styleFrom( - padding: EdgeInsets.zero, - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - visualDensity: const VisualDensity( - horizontal: VisualDensity.minimumDensity, - vertical: VisualDensity.minimumDensity, - ), - ), - onPressed: () => client - ..closeConnection() - ..openConnection(), - child: Text( - context.translations.tryAgainLabel, - style: textStyle?.copyWith( - color: StreamChatTheme.of(context).colorTheme.accentPrimary, - ), - ), - ), - ], - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/channel/channel_list_header.dart b/packages/stream_chat_flutter/lib/src/channel/channel_list_header.dart deleted file mode 100644 index 06560e5611..0000000000 --- a/packages/stream_chat_flutter/lib/src/channel/channel_list_header.dart +++ /dev/null @@ -1,287 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamChannelListHeader} -/// Shows the current [StreamChatClient] status. -/// -/// ```dart -/// class MyApp extends StatelessWidget { -/// final StreamChatClient client; -/// -/// MyApp(this.client); -/// -/// @override -/// Widget build(BuildContext context) { -/// return MaterialApp( -/// home: StreamChat( -/// client: client, -/// child: Scaffold( -/// appBar: ChannelListHeader(), -/// ), -/// ), -/// ); -/// } -/// } -/// ``` -/// -/// Usually you would use this widget as an [AppBar] inside a [Scaffold]. -/// However, you can also use it as a normal widget. -/// -/// Uses the inherited [StreamChatClient], by default, to fetch information -/// about the status of the [client]. You can also pass your own -/// [StreamChatClient] if you don't have it in the widget tree. -/// -/// Renders the UI based on the first ancestor of type [StreamChatTheme] and -/// the [StreamChannelListHeaderThemeData] property. Modify it to change the -/// widget's appearance. -/// {@endtemplate} -class StreamChannelListHeader extends StatelessWidget - implements PreferredSizeWidget { - /// {@macro streamChannelListHeader} - const StreamChannelListHeader({ - super.key, - this.client, - this.titleBuilder, - this.onUserAvatarTap, - this.onNewChatButtonTap, - this.showConnectionStateTile = false, - this.preNavigationCallback, - this.subtitle, - this.centerTitle, - this.leading, - this.actions, - this.backgroundColor, - this.elevation = 1, - }); - - /// Use this if you don't have a [StreamChatClient] in your widget tree. - final StreamChatClient? client; - - /// {@macro channelListHeaderTitleBuilder} - final ChannelListHeaderTitleBuilder? titleBuilder; - - /// The action to perform when pressing the user avatar button. - /// - /// By default it calls `Scaffold.of(context).openDrawer()`. - final Function(User)? onUserAvatarTap; - - /// The action to perform when pressing the "new chat" button. - final VoidCallback? onNewChatButtonTap; - - /// Whether to show the connection state tile - final bool showConnectionStateTile; - - /// The function to execute before navigation is performed - final VoidCallback? preNavigationCallback; - - /// Subtitle widget - final Widget? subtitle; - - /// Whether the title should be centered - final bool? centerTitle; - - /// Leading widget - /// - /// By default it shows the logged in user's avatar - final Widget? leading; - - /// {@macro flutter.material.appbar.actions} - /// - /// The "new chat" button is shown by default. - final List? actions; - - /// The background color for this [StreamChannelListHeader]. - final Color? backgroundColor; - - /// The elevation for this [StreamChannelListHeader]. - final double elevation; - - @override - Size get preferredSize => const Size.fromHeight(kToolbarHeight); - - @override - Widget build(BuildContext context) { - final _client = client ?? StreamChat.of(context).client; - final user = _client.state.currentUser; - return StreamConnectionStatusBuilder( - statusBuilder: (context, status) { - var statusString = ''; - var showStatus = true; - - switch (status) { - case ConnectionStatus.connected: - statusString = context.translations.connectedLabel; - showStatus = false; - break; - case ConnectionStatus.connecting: - statusString = context.translations.reconnectingLabel; - break; - case ConnectionStatus.disconnected: - statusString = context.translations.disconnectedLabel; - break; - } - - final chatThemeData = StreamChatTheme.of(context); - final channelListHeaderThemeData = - StreamChannelListHeaderTheme.of(context); - final theme = Theme.of(context); - return StreamInfoTile( - showMessage: showConnectionStateTile && showStatus, - message: statusString, - child: AppBar( - toolbarTextStyle: theme.textTheme.bodyMedium, - titleTextStyle: theme.textTheme.titleLarge, - systemOverlayStyle: theme.brightness == Brightness.dark - ? SystemUiOverlayStyle.light - : SystemUiOverlayStyle.dark, - elevation: elevation, - backgroundColor: - backgroundColor ?? channelListHeaderThemeData.color, - centerTitle: centerTitle, - leading: leading ?? - Center( - child: user != null - ? StreamUserAvatar( - user: user, - showOnlineStatus: false, - onTap: onUserAvatarTap ?? - (_) { - preNavigationCallback?.call(); - Scaffold.of(context).openDrawer(); - }, - borderRadius: channelListHeaderThemeData - .avatarTheme?.borderRadius, - constraints: channelListHeaderThemeData - .avatarTheme?.constraints, - ) - : const Offstage(), - ), - actions: actions ?? - [ - StreamNeumorphicButton( - child: IconButton( - icon: StreamConnectionStatusBuilder( - statusBuilder: (context, status) { - final color = switch (status) { - ConnectionStatus.connected => - chatThemeData.colorTheme.accentPrimary, - ConnectionStatus.connecting => Colors.grey, - ConnectionStatus.disconnected => Colors.grey, - }; - - return StreamSvgIcon( - size: 24, - color: color, - icon: StreamSvgIcons.penWrite, - ); - }, - ), - onPressed: onNewChatButtonTap, - ), - ), - ], - title: Column( - children: [ - Builder( - builder: (context) { - if (titleBuilder != null) { - return titleBuilder!(context, status, _client); - } - switch (status) { - case ConnectionStatus.connected: - return _ConnectedTitleState(); - case ConnectionStatus.connecting: - return _ConnectingTitleState(); - case ConnectionStatus.disconnected: - return _DisconnectedTitleState(client: _client); - } - }, - ), - subtitle ?? const Offstage(), - ], - ), - ), - ); - }, - ); - } -} - -class _ConnectedTitleState extends StatelessWidget { - @override - Widget build(BuildContext context) { - final chatThemeData = StreamChatTheme.of(context); - return Text( - context.translations.streamChatLabel, - style: chatThemeData.textTheme.headlineBold.copyWith( - color: chatThemeData.colorTheme.textHighEmphasis, - ), - ); - } -} - -class _ConnectingTitleState extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox( - height: 16, - width: 16, - child: Center( - child: CircularProgressIndicator.adaptive(), - ), - ), - const SizedBox(width: 10), - Text( - context.translations.searchingForNetworkText, - style: StreamChannelListHeaderTheme.of(context).titleStyle?.copyWith( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - ], - ); - } -} - -class _DisconnectedTitleState extends StatelessWidget { - const _DisconnectedTitleState({ - required this.client, - }); - - final StreamChatClient client; - - @override - Widget build(BuildContext context) { - final chatThemeData = StreamChatTheme.of(context); - final channelListHeaderTheme = StreamChannelListHeaderTheme.of(context); - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - context.translations.offlineLabel, - style: channelListHeaderTheme.titleStyle?.copyWith( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - TextButton( - onPressed: () => client - ..closeConnection() - ..openConnection(), - child: Text( - context.translations.tryAgainLabel, - style: channelListHeaderTheme.titleStyle?.copyWith( - fontSize: 16, - fontWeight: FontWeight.bold, - color: chatThemeData.colorTheme.accentPrimary, - ), - ), - ), - ], - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/channel/channel_name.dart b/packages/stream_chat_flutter/lib/src/channel/channel_name.dart deleted file mode 100644 index ddccdb7200..0000000000 --- a/packages/stream_chat_flutter/lib/src/channel/channel_name.dart +++ /dev/null @@ -1,106 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template channelName} -/// Displays the current [Channel] name using a [Text] widget. -/// -/// The widget uses a [StreamBuilder] to render the channel information -/// image as soon as it updates. -/// {@endtemplate} -class ChannelName extends StatelessWidget { - /// {@macro channelName} - const ChannelName({ - super.key, - this.textStyle, - this.textOverflow = TextOverflow.ellipsis, - }); - - /// The style of the text displayed - final TextStyle? textStyle; - - /// How visual overflow should be handled. - final TextOverflow textOverflow; - - @override - Widget build(BuildContext context) { - final client = StreamChat.of(context); - final channel = StreamChannel.of(context).channel; - - assert(channel.state != null, 'Channel ${channel.id} is not initialized'); - - return BetterStreamBuilder( - stream: channel.nameStream, - initialData: channel.name, - builder: (context, channelName) => Text( - channelName, - style: textStyle, - overflow: textOverflow, - ), - noDataBuilder: (context) => _NameGenerator( - currentUser: client.currentUser!, - members: channel.state!.members, - textStyle: textStyle, - textOverflow: textOverflow, - ), - ); - } -} - -class _NameGenerator extends StatelessWidget { - const _NameGenerator({ - required this.currentUser, - required this.members, - this.textStyle, - this.textOverflow, - }); - - final User currentUser; - final List members; - final TextStyle? textStyle; - final TextOverflow? textOverflow; - - @override - Widget build(BuildContext context) { - return LayoutBuilder( - builder: (context, constraints) { - var channelName = context.translations.noTitleText; - final otherMembers = members.where( - (member) => member.userId != currentUser.id, - ); - - if (otherMembers.isNotEmpty) { - if (otherMembers.length == 1) { - final user = otherMembers.first.user; - if (user != null) { - channelName = user.name; - } - } else { - final maxWidth = constraints.maxWidth; - final maxChars = maxWidth / (textStyle?.fontSize ?? 1); - var currentChars = 0; - final currentMembers = []; - otherMembers.forEach((element) { - final newLength = currentChars + (element.user?.name.length ?? 0); - if (newLength < maxChars) { - currentChars = newLength; - currentMembers.add(element); - } - }); - - final exceedingMembers = - otherMembers.length - currentMembers.length; - channelName = - '${currentMembers.map((e) => e.user?.name).join(', ')} ' - '${exceedingMembers > 0 ? '+ $exceedingMembers' : ''}'; - } - } - - return Text( - channelName, - style: textStyle, - overflow: textOverflow, - ); - }, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/channel/stream_channel_avatar.dart b/packages/stream_chat_flutter/lib/src/channel/stream_channel_avatar.dart deleted file mode 100644 index 6c71eee4ee..0000000000 --- a/packages/stream_chat_flutter/lib/src/channel/stream_channel_avatar.dart +++ /dev/null @@ -1,240 +0,0 @@ -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_image.png) -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/channel_image_paint.png) -/// -/// It shows the current [Channel] image. -/// -/// ```dart -/// class MyApp extends StatelessWidget { -/// final StreamChatClient client; -/// final Channel channel; -/// -/// MyApp(this.client, this.channel); -/// -/// @override -/// Widget build(BuildContext context) { -/// return MaterialApp( -/// debugShowCheckedModeBanner: false, -/// home: StreamChat( -/// client: client, -/// child: StreamChannel( -/// channel: channel, -/// child: Center( -/// child: StreamChannelAvatar( -/// channel: channel, -/// ), -/// ), -/// ), -/// ), -/// ); -/// } -/// } -/// ``` -/// -/// The widget uses a [StreamBuilder] to render the channel information -/// image as soon as it updates. -/// -/// By default the widget radius size is 40x40 pixels. -/// Set the property [constraints] to set a custom dimension. -/// -/// The widget renders the ui based on the first ancestor of type -/// [StreamChatTheme]. -/// Modify it to change the widget appearance. -class StreamChannelAvatar extends StatelessWidget { - /// Instantiate a new ChannelImage - StreamChannelAvatar({ - super.key, - required this.channel, - this.constraints, - this.onTap, - this.borderRadius, - this.selected = false, - this.selectionColor, - this.selectionThickness = 4, - this.ownSpaceAvatarBuilder, - this.oneToOneAvatarBuilder, - this.groupAvatarBuilder, - }) : assert( - channel.state != null, - 'Channel ${channel.id} is not initialized', - ); - - /// [BorderRadius] to display the widget - final BorderRadius? borderRadius; - - /// The channel to show the image of - final Channel channel; - - /// The diameter of the image - final BoxConstraints? constraints; - - /// The function called when the image is tapped - final VoidCallback? onTap; - - /// If image is selected - final bool selected; - - /// Selection color for image - final Color? selectionColor; - - /// Thickness of selection image - final double selectionThickness; - - /// Builder to create avatar for own space channel. - /// - /// Defaults to [StreamUserAvatar]. - final StreamUserAvatarBuilder? ownSpaceAvatarBuilder; - - /// Builder to create avatar for one to one channel. - /// - /// Defaults to [StreamUserAvatar]. - final StreamUserAvatarBuilder? oneToOneAvatarBuilder; - - /// Builder to create avatar for group channel. - /// - /// Defaults to [StreamGroupAvatar]. - final StreamGroupAvatarBuilder? groupAvatarBuilder; - - @override - Widget build(BuildContext context) { - final client = channel.client.state; - - final chatThemeData = StreamChatTheme.of(context); - final colorTheme = chatThemeData.colorTheme; - final previewTheme = chatThemeData.channelPreviewTheme.avatarTheme; - - final fallbackWidget = Center( - child: Text( - channel.name?[0] ?? '', - style: TextStyle( - color: colorTheme.barsBg, - fontWeight: FontWeight.bold, - ), - ), - ); - - return BetterStreamBuilder( - stream: channel.imageStream, - initialData: channel.image, - builder: (context, channelImage) { - Widget child = ClipRRect( - borderRadius: - borderRadius ?? previewTheme?.borderRadius ?? BorderRadius.zero, - child: Container( - constraints: constraints ?? previewTheme?.constraints, - decoration: BoxDecoration(color: colorTheme.accentPrimary), - child: InkWell( - onTap: onTap, - child: channelImage.isEmpty - ? fallbackWidget - : CachedNetworkImage( - imageUrl: channelImage, - errorWidget: (_, __, ___) => fallbackWidget, - fit: BoxFit.cover, - ), - ), - ), - ); - - if (selected) { - child = ClipRRect( - key: const Key('selectedImage'), - borderRadius: BorderRadius.circular(selectionThickness) + - (borderRadius ?? - previewTheme?.borderRadius ?? - BorderRadius.zero), - child: Container( - constraints: constraints ?? previewTheme?.constraints, - color: selectionColor ?? colorTheme.accentPrimary, - child: Padding( - padding: EdgeInsets.all(selectionThickness), - child: child, - ), - ), - ); - } - return child; - }, - noDataBuilder: (context) { - final currentUser = client.currentUser!; - final otherMembers = channel.state!.members - .where((it) => it.userId != currentUser.id) - .toList(growable: false); - - // our own space, no other members - if (otherMembers.isEmpty) { - return BetterStreamBuilder( - stream: client.currentUserStream.map((it) => it!), - initialData: currentUser, - builder: (context, user) { - final ownSpaceBuilder = ownSpaceAvatarBuilder; - if (ownSpaceBuilder != null) { - return ownSpaceBuilder(context, user, selected); - } - - return StreamUserAvatar( - borderRadius: borderRadius ?? previewTheme?.borderRadius, - user: user, - constraints: constraints ?? previewTheme?.constraints, - onTap: onTap != null ? (_) => onTap!() : null, - selected: selected, - selectionColor: selectionColor ?? colorTheme.accentPrimary, - selectionThickness: selectionThickness, - ); - }, - ); - } - - // 1-1 Conversation - if (otherMembers.length == 1) { - final member = otherMembers.first; - return BetterStreamBuilder( - stream: channel.state!.membersStream.map( - (members) => members.firstWhere( - (it) => it.userId == member.userId, - orElse: () => member, - ), - ), - initialData: member, - builder: (context, member) { - final oneToOneBuilder = oneToOneAvatarBuilder; - if (oneToOneBuilder != null) { - return oneToOneBuilder(context, member.user!, selected); - } - - return StreamUserAvatar( - borderRadius: borderRadius ?? previewTheme?.borderRadius, - user: member.user!, - constraints: constraints ?? previewTheme?.constraints, - onTap: onTap != null ? (_) => onTap!() : null, - selected: selected, - selectionColor: selectionColor ?? colorTheme.accentPrimary, - selectionThickness: selectionThickness, - ); - }, - ); - } - - final groupBuilder = groupAvatarBuilder; - if (groupBuilder != null) { - return groupBuilder(context, otherMembers, selected); - } - - // Group conversation - return StreamGroupAvatar( - channel: channel, - members: otherMembers, - borderRadius: borderRadius ?? previewTheme?.borderRadius, - constraints: constraints ?? previewTheme?.constraints, - onTap: onTap, - selected: selected, - selectionColor: selectionColor ?? colorTheme.accentPrimary, - selectionThickness: selectionThickness, - ); - }, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/channel/stream_channel_name.dart b/packages/stream_chat_flutter/lib/src/channel/stream_channel_name.dart deleted file mode 100644 index 273b5b37e8..0000000000 --- a/packages/stream_chat_flutter/lib/src/channel/stream_channel_name.dart +++ /dev/null @@ -1,90 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// It shows the current [Channel] name using a [Text] widget. -/// -/// The widget uses a [StreamBuilder] to render the channel information -/// image as soon as it updates. -class StreamChannelName extends StatelessWidget { - /// Instantiate a new ChannelName - StreamChannelName({ - super.key, - required this.channel, - this.textStyle, - this.textOverflow = TextOverflow.ellipsis, - }) : assert( - channel.state != null, - 'Channel ${channel.id} is not initialized', - ); - - /// The [Channel] to show the name for. - final Channel channel; - - /// The style of the text displayed - final TextStyle? textStyle; - - /// How visual overflow should be handled. - final TextOverflow textOverflow; - - @override - Widget build(BuildContext context) => BetterStreamBuilder( - stream: channel.nameStream, - initialData: channel.name, - builder: (context, channelName) => Text( - channelName, - style: textStyle, - overflow: textOverflow, - ), - noDataBuilder: (context) => _generateName( - channel.client.state.currentUser!, - channel.state!.members, - ), - ); - - Widget _generateName( - User currentUser, - List members, - ) => - LayoutBuilder( - builder: (context, constraints) { - var channelName = context.translations.noTitleText; - final otherMembers = members.where( - (member) => member.userId != currentUser.id, - ); - - if (otherMembers.isNotEmpty) { - if (otherMembers.length == 1) { - final user = otherMembers.first.user; - if (user != null) { - channelName = user.name; - } - } else { - final maxWidth = constraints.maxWidth; - final maxChars = maxWidth / (textStyle?.fontSize ?? 1); - var currentChars = 0; - final currentMembers = []; - otherMembers.forEach((element) { - final newLength = - currentChars + (element.user?.name.length ?? 0); - if (newLength < maxChars) { - currentChars = newLength; - currentMembers.add(element); - } - }); - - final exceedingMembers = - otherMembers.length - currentMembers.length; - channelName = - '${currentMembers.map((e) => e.user?.name).join(', ')} ' - '${exceedingMembers > 0 ? '+ $exceedingMembers' : ''}'; - } - } - - return Text( - channelName, - style: textStyle, - overflow: textOverflow, - ); - }, - ); -} diff --git a/packages/stream_chat_flutter/lib/src/channel/stream_message_preview_text.dart b/packages/stream_chat_flutter/lib/src/channel/stream_message_preview_text.dart deleted file mode 100644 index 6d90acef10..0000000000 --- a/packages/stream_chat_flutter/lib/src/channel/stream_message_preview_text.dart +++ /dev/null @@ -1,122 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// A widget that renders a preview of the message text. -class StreamMessagePreviewText extends StatelessWidget { - /// Creates a new instance of [StreamMessagePreviewText]. - const StreamMessagePreviewText({ - super.key, - required this.message, - this.language, - this.textStyle, - }); - - /// The message to display. - final Message message; - - /// The language to use for translations. - final String? language; - - /// The style to use for the text. - final TextStyle? textStyle; - - @override - Widget build(BuildContext context) { - final messageText = message - .translate(language ?? 'en') - .replaceMentions(linkify: false) - .text; - final messageAttachments = message.attachments; - final messageMentionedUsers = message.mentionedUsers; - - final mentionedUsersRegex = RegExp( - messageMentionedUsers.map((it) => '@${it.name}').join('|'), - caseSensitive: false, - ); - - final messageTextParts = switch (message.isDeleted) { - // Show the deleted message label if the message is deleted. - true => [context.translations.messageDeletedLabel], - // Otherwise, combine the message text with the attachments and poll. - false => [ - ...messageAttachments.map( - (it) => switch (it.type) { - AttachmentType.image => 'šŸ“·', - AttachmentType.video => 'šŸŽ¬', - AttachmentType.giphy => '[GIF]', - AttachmentType.audio => 'šŸŽ§', - AttachmentType.voiceRecording => 'šŸŽ¤', - _ => it == message.attachments.last - ? (it.title ?? 'File') - : '${it.title ?? 'File'},', - }, - ), - if (message.poll?.name case final pollName?) 'šŸ“Š $pollName', - if (messageText != null && messageText.isNotEmpty) - if (messageMentionedUsers.isNotEmpty) - ...mentionedUsersRegex.allMatchesWithSep(messageText) - else - messageText.trim(), - ] - }; - - final fontStyle = switch (message.isSystem || message.isDeleted) { - true => FontStyle.italic, - false => FontStyle.normal, - }; - - final regularTextStyle = textStyle?.copyWith(fontStyle: fontStyle); - - final mentionsTextStyle = textStyle?.copyWith( - fontStyle: fontStyle, - fontWeight: FontWeight.bold, - ); - - final spans = [ - ...messageTextParts.map((part) { - if (messageMentionedUsers.any((it) => '@${it.name}' == part)) { - return TextSpan( - text: part, - style: mentionsTextStyle, - ); - } - - if (messageAttachments.any((it) => it.title == part)) { - return TextSpan( - text: part, - style: regularTextStyle?.copyWith( - fontStyle: FontStyle.italic, - ), - ); - } - - return TextSpan( - text: part, - style: regularTextStyle, - ); - }) - ].insertBetween(const TextSpan(text: ' ')); - - return Text.rich( - TextSpan(children: spans), - maxLines: 1, - overflow: TextOverflow.ellipsis, - textAlign: TextAlign.start, - ); - } -} - -extension _RegExpX on RegExp { - List allMatchesWithSep(String input, [int start = 0]) { - final result = []; - for (final match in allMatches(input, start)) { - result.add(input.substring(start, match.start)); - // ignore: cascade_invocations - result.add(match[0]!); - // ignore: parameter_assignments - start = match.end; - } - result.add(input.substring(start)); - return result; - } -} diff --git a/packages/stream_chat_flutter/lib/src/context_menu_items/context_menu_reaction_picker.dart b/packages/stream_chat_flutter/lib/src/context_menu_items/context_menu_reaction_picker.dart deleted file mode 100644 index fb22d44d27..0000000000 --- a/packages/stream_chat_flutter/lib/src/context_menu_items/context_menu_reaction_picker.dart +++ /dev/null @@ -1,168 +0,0 @@ -import 'package:ezanimation/ezanimation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template contextMenuReactionPicker} -/// Allows the user to select reactions to a message on desktop & web via -/// context menu. -/// -/// This differs slightly from [StreamReactionPicker] in order to match our -/// design spec. -/// -/// Used by the `_buildContextMenu()` function found in `message_widget.dart`. -/// It is not recommended to use this widget directly. -/// {@endtemplate} -class ContextMenuReactionPicker extends StatefulWidget { - /// {@macro contextMenuReactionPicker} - const ContextMenuReactionPicker({ - super.key, - required this.message, - }); - - /// The message to react to. - final Message message; - - @override - State createState() => - _ContextMenuReactionPickerState(); -} - -class _ContextMenuReactionPickerState extends State - with TickerProviderStateMixin { - List animations = []; - - Future triggerAnimations() async { - for (final a in animations) { - a.start(); - await Future.delayed(const Duration(milliseconds: 100)); - } - } - - Future pop() async { - for (final a in animations) { - a.stop(); - } - Navigator.of(context).pop(); - } - - /// Add a reaction to the message - void sendReaction(BuildContext context, String reactionType) { - StreamChannel.of(context).channel.sendReaction( - widget.message, - reactionType, - enforceUnique: - StreamChatConfiguration.of(context).enforceUniqueReactions, - ); - pop(); - } - - /// Remove a reaction from the message - void removeReaction(BuildContext context, Reaction reaction) { - StreamChannel.of(context).channel.deleteReaction(widget.message, reaction); - pop(); - } - - @override - void dispose() { - for (final a in animations) { - a.dispose(); - } - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final reactionIcons = StreamChatConfiguration.of(context).reactionIcons; - - if (animations.isEmpty && reactionIcons.isNotEmpty) { - reactionIcons.forEach((element) { - animations.add( - EzAnimation.tween( - Tween(begin: 0.0, end: 1.0), - const Duration(milliseconds: 250), - curve: Curves.easeInOutBack, - ), - ); - }); - - triggerAnimations(); - } - - final child = Material( - color: StreamChatTheme.of(context).messageListViewTheme.backgroundColor ?? - Theme.of(context).scaffoldBackgroundColor, - //clipBehavior: Clip.hardEdge, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: Row( - spacing: 16, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceAround, - mainAxisSize: MainAxisSize.min, - children: reactionIcons.map((reactionIcon) { - final ownReactionIndex = widget.message.ownReactions?.indexWhere( - (reaction) => reaction.type == reactionIcon.type, - ) ?? - -1; - final index = reactionIcons.indexOf(reactionIcon); - - final child = reactionIcon.builder( - context, - ownReactionIndex != -1, - 24, - ); - - return ConstrainedBox( - constraints: const BoxConstraints.tightFor( - height: 24, - width: 24, - ), - child: RawMaterialButton( - elevation: 0, - shape: ContinuousRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - constraints: const BoxConstraints.tightFor( - height: 24, - width: 24, - ), - onPressed: () { - if (ownReactionIndex != -1) { - removeReaction( - context, - widget.message.ownReactions![ownReactionIndex], - ); - } else { - sendReaction( - context, - reactionIcon.type, - ); - } - }, - child: AnimatedBuilder( - animation: animations[index], - builder: (context, child) => Transform.scale( - scale: animations[index].value, - child: child, - ), - child: child, - ), - ), - ); - }).toList(), - ), - ), - ); - - return TweenAnimationBuilder( - tween: Tween(begin: 0, end: 1), - curve: Curves.easeInOutBack, - duration: const Duration(milliseconds: 500), - builder: (context, val, widget) => Transform.scale( - scale: val, - child: widget, - ), - child: child, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/context_menu_items/download_menu_item.dart b/packages/stream_chat_flutter/lib/src/context_menu_items/download_menu_item.dart deleted file mode 100644 index a3f4d5a207..0000000000 --- a/packages/stream_chat_flutter/lib/src/context_menu_items/download_menu_item.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/context_menu_items/stream_chat_context_menu_item.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template downloadMenuItem} -/// Defines a "download" context menu item that allows a user to download -/// a given attachment. -/// -/// Used in [DesktopFullscreenMedia]. -/// {@endtemplate} -class DownloadMenuItem extends StatelessWidget { - /// {@macro downloadMenuItem} - const DownloadMenuItem({ - super.key, - required this.attachment, - }); - - /// The attachment to download. - final Attachment attachment; - - @override - Widget build(BuildContext context) { - return StreamChatContextMenuItem( - leading: const StreamSvgIcon(icon: StreamSvgIcons.download), - title: Text(context.translations.downloadLabel), - onClick: () async { - Navigator.of(context).pop(); - StreamAttachmentHandler.instance.downloadAttachment(attachment); - }, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/context_menu_items/stream_chat_context_menu_item.dart b/packages/stream_chat_flutter/lib/src/context_menu_items/stream_chat_context_menu_item.dart deleted file mode 100644 index dfe64684e2..0000000000 --- a/packages/stream_chat_flutter/lib/src/context_menu_items/stream_chat_context_menu_item.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamChatContextMenuItem} -/// Builds a context menu item according to Stream design specification. -/// {@endtemplate} -class StreamChatContextMenuItem extends StatelessWidget { - /// {@macro streamChatContextMenuItem} - const StreamChatContextMenuItem({ - super.key, - this.child, - this.leading, - this.title, - this.onClick, - }); - - /// The child widget for this menu item. Usually a [DesktopReactionPicker]. - /// - /// Leave null in order to use the default menu item widget. - final Widget? child; - - /// The widget to lead the menu item with. Usually an [Icon]. - /// - /// If [child] is specified, this will be ignored. - final Widget? leading; - - /// The title of the menu item. Usually a [Text]. - /// - /// If [child] is specified, this will be ignored. - final Widget? title; - - /// The action to perform when the menu item is clicked. - /// - /// If [child] is specified, this will be ignored. - final VoidCallback? onClick; - - @override - Widget build(BuildContext context) { - return Ink( - color: StreamChatTheme.of(context).messageListViewTheme.backgroundColor ?? - Theme.of(context).scaffoldBackgroundColor, - child: child ?? - ListTile( - dense: true, - leading: leading, - title: title, - onTap: onClick, - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/dialogs/channel_info_dialog.dart b/packages/stream_chat_flutter/lib/src/dialogs/channel_info_dialog.dart deleted file mode 100644 index 9d997c58d5..0000000000 --- a/packages/stream_chat_flutter/lib/src/dialogs/channel_info_dialog.dart +++ /dev/null @@ -1,85 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template channelInfoDialog} -/// A dialog for showing information about a channel on desktop & web platforms. -/// {@endtemplate} -class ChannelInfoDialog extends StatelessWidget { - /// {@macro channelInfoDialog} - const ChannelInfoDialog({ - super.key, - required this.channel, - }); - - /// The channel to display information about. - final Channel channel; - - @override - Widget build(BuildContext context) { - final streamTheme = StreamChatTheme.of(context); - final members = channel.state?.members ?? []; - - final userAsMember = members.firstWhere( - (e) => e.user?.id == StreamChat.of(context).currentUser?.id, - ); - return StreamChannel( - channel: channel, - child: SimpleDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - backgroundColor: streamTheme.colorTheme.appBg, - title: Text( - channel.name ?? channel.id!, - style: StreamChatTheme.of(context).textTheme.headlineBold, - ), - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - StreamChannelInfo( - channel: channel, - textStyle: StreamChatTheme.of(context) - .channelPreviewTheme - .subtitleStyle, - ), - ], - ), - const SizedBox(height: 16), - if (channel.isDistinct && channel.memberCount == 2) - Column( - children: [ - StreamUserAvatar( - user: members - .firstWhere( - (e) => e.user?.id != userAsMember.user?.id, - ) - .user!, - constraints: const BoxConstraints( - maxHeight: 64, - maxWidth: 64, - ), - borderRadius: BorderRadius.circular(32), - onlineIndicatorConstraints: - BoxConstraints.tight(const Size(12, 12)), - ), - const SizedBox(height: 6), - Text( - members - .firstWhere( - (e) => e.user?.id != userAsMember.user?.id, - ) - .user - ?.name ?? - '', - style: StreamChatTheme.of(context).textTheme.footnoteBold, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ], - ), - ], - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/dialogs/confirmation_dialog.dart b/packages/stream_chat_flutter/lib/src/dialogs/confirmation_dialog.dart deleted file mode 100644 index 04fd31b841..0000000000 --- a/packages/stream_chat_flutter/lib/src/dialogs/confirmation_dialog.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template confirmationDialog} -/// A dialog that prompts the user to take an action or cancel. -/// {@endtemplate} -class ConfirmationDialog extends StatelessWidget { - /// {@macro confirmationDialog} - const ConfirmationDialog({ - super.key, - required this.titleText, - required this.promptText, - required this.affirmativeText, - required this.onConfirmation, - }); - - /// The text to use for the dialog title. - final String titleText; - - /// The text to use for the dialog prompt. - final String promptText; - - /// The text to use for the confirmation button. - final String affirmativeText; - - /// The action to perform when the user confirms their choice. - final VoidCallback onConfirmation; - - @override - Widget build(BuildContext context) { - final streamTheme = StreamChatTheme.of(context); - return AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - backgroundColor: streamTheme.colorTheme.appBg, - title: Text(titleText), - content: Text(promptText), - actions: [ - TextButton( - style: TextButton.styleFrom( - foregroundColor: streamTheme.colorTheme.accentPrimary, - ), - onPressed: () => Navigator.of(context).pop(false), - child: Text(context.translations.cancelLabel), - ), - TextButton( - style: TextButton.styleFrom( - foregroundColor: streamTheme.colorTheme.accentPrimary, - ), - onPressed: () { - onConfirmation.call(); - Navigator.of(context).pop(true); - }, - child: Text(affirmativeText), - ), - ], - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/dialogs/delete_message_dialog.dart b/packages/stream_chat_flutter/lib/src/dialogs/delete_message_dialog.dart deleted file mode 100644 index 3532b0930e..0000000000 --- a/packages/stream_chat_flutter/lib/src/dialogs/delete_message_dialog.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template deleteMessageDialog} -/// A dialog that asks the user to confirm that they want to -/// delete the selected message. -/// {@endtemplate} -class DeleteMessageDialog extends StatelessWidget { - /// {@macro deleteMessageDialog} - const DeleteMessageDialog({ - super.key, - }); - - @override - Widget build(BuildContext context) { - final streamTheme = StreamChatTheme.of(context); - return AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - backgroundColor: streamTheme.colorTheme.appBg, - title: Text(context.translations.deleteMessageLabel), - content: Text(context.translations.deleteMessageQuestion), - actions: [ - TextButton( - style: TextButton.styleFrom( - foregroundColor: streamTheme.colorTheme.accentPrimary, - ), - onPressed: () => Navigator.of(context).pop(false), - child: Text(context.translations.cancelLabel), - ), - TextButton( - style: TextButton.styleFrom( - foregroundColor: streamTheme.colorTheme.accentPrimary, - ), - onPressed: () => Navigator.of(context).pop(true), - child: Text(context.translations.deleteLabel), - ), - ], - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/dialogs/dialogs.dart b/packages/stream_chat_flutter/lib/src/dialogs/dialogs.dart deleted file mode 100644 index 2daa1f6fb0..0000000000 --- a/packages/stream_chat_flutter/lib/src/dialogs/dialogs.dart +++ /dev/null @@ -1,4 +0,0 @@ -export 'channel_info_dialog.dart'; -export 'confirmation_dialog.dart'; -export 'delete_message_dialog.dart'; -export 'message_dialog.dart'; diff --git a/packages/stream_chat_flutter/lib/src/dialogs/message_dialog.dart b/packages/stream_chat_flutter/lib/src/dialogs/message_dialog.dart deleted file mode 100644 index f8b9930c43..0000000000 --- a/packages/stream_chat_flutter/lib/src/dialogs/message_dialog.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template messageDialog} -/// A dialog that displays a message to a user. Falls back to a -/// generic error message if no [titleText] and [messageText] are specified. -/// -/// If using this dialog to display the default generic error, be sure NOT to -/// specify a [titleText] and [messageText] so the fallback strings can be used. -/// {@endtemplate} -class MessageDialog extends StatelessWidget { - /// {@macro messageDialog} - const MessageDialog({ - super.key, - this.titleText, - this.messageText, - }); - - /// The optional error message title to use. - final String? titleText; - - /// The optional error message to use. - final String? messageText; - - @override - Widget build(BuildContext context) { - final streamTheme = StreamChatTheme.of(context); - return AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - backgroundColor: streamTheme.colorTheme.appBg, - title: Text(titleText ?? context.translations.somethingWentWrongError), - content: messageText != null - ? Text( - messageText ?? - context.translations.operationCouldNotBeCompletedText, - ) - : null, - actions: [ - TextButton( - style: TextButton.styleFrom( - foregroundColor: streamTheme.colorTheme.accentPrimary, - ), - child: Text(context.translations.okLabel), - onPressed: () => Navigator.of(context).pop(), - ), - ], - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/fullscreen_media/fsm_stub.dart b/packages/stream_chat_flutter/lib/src/fullscreen_media/fsm_stub.dart deleted file mode 100644 index 1bf7c435b3..0000000000 --- a/packages/stream_chat_flutter/lib/src/fullscreen_media/fsm_stub.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:stream_chat_flutter/src/fullscreen_media/full_screen_media_widget.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Stub function for returning an instance of either [FullScreenMedia] or -/// [FullScreenMediaDesktop]. -/// -/// This should ONLY be used in [FullScreenMediaBuilder]. -FullScreenMediaWidget getFsm({ - Key? key, - required List mediaAttachmentPackages, - required int startIndex, - required String userName, - ShowMessageCallback? onShowMessage, - ReplyMessageCallback? onReplyMessage, - AttachmentActionsBuilder? attachmentActionsModalBuilder, - bool? autoplayVideos, -}) => - throw UnsupportedError('Cannot create FullScreenMedia'); diff --git a/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media.dart b/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media.dart deleted file mode 100644 index 0d89346aaa..0000000000 --- a/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media.dart +++ /dev/null @@ -1,401 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:chewie/chewie.dart'; -import 'package:flutter/material.dart'; -import 'package:photo_view/photo_view.dart'; -import 'package:stream_chat_flutter/src/fullscreen_media/full_screen_media_widget.dart'; -import 'package:stream_chat_flutter/src/fullscreen_media/gallery_navigation_item.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:video_player/video_player.dart'; - -/// A full screen image widget -class StreamFullScreenMedia extends FullScreenMediaWidget { - /// Instantiate a new FullScreenImage - const StreamFullScreenMedia({ - super.key, - required this.mediaAttachmentPackages, - this.startIndex = 0, - this.userName = '', - this.onShowMessage, - this.onReplyMessage, - this.attachmentActionsModalBuilder, - this.autoplayVideos = false, - }) : assert(startIndex >= 0, 'startIndex cannot be negative'); - - /// The url of the image - final List mediaAttachmentPackages; - - /// First index of media shown - final int startIndex; - - /// Username of sender - final String userName; - - /// Callback for when show message is tapped - final ShowMessageCallback? onShowMessage; - - /// Callback for when reply message is tapped - final ReplyMessageCallback? onReplyMessage; - - /// Widget builder for attachment actions modal - /// [defaultActionsModal] is the default [AttachmentActionsModal] config - /// Use [defaultActionsModal.copyWith] to easily customize it - final AttachmentActionsBuilder? attachmentActionsModalBuilder; - - /// Auto-play videos when page is opened - final bool autoplayVideos; - - @override - _FullScreenMediaState createState() => _FullScreenMediaState(); -} - -class _FullScreenMediaState extends State { - late final PageController _pageController; - - late final _currentPage = ValueNotifier(widget.startIndex); - late final _isDisplayingDetail = ValueNotifier(true); - - void switchDisplayingDetail() { - _isDisplayingDetail.value = !_isDisplayingDetail.value; - } - - final videoPackages = {}; - - @override - void initState() { - super.initState(); - _pageController = PageController(initialPage: widget.startIndex); - for (var i = 0; i < widget.mediaAttachmentPackages.length; i++) { - final attachment = widget.mediaAttachmentPackages[i].attachment; - if (attachment.type != AttachmentType.video) continue; - final package = VideoPackage(attachment, showControls: true); - videoPackages[attachment.id] = package; - } - initializePlayers(); - } - - Future initializePlayers() async { - if (videoPackages.isEmpty) { - return; - } - - final currentAttachment = - widget.mediaAttachmentPackages[widget.startIndex].attachment; - - await Future.wait(videoPackages.values.map( - (it) => it.initialize(), - )); - - if (widget.autoplayVideos && - currentAttachment.type == AttachmentType.video) { - final package = videoPackages.values - .firstWhere((e) => e._attachment == currentAttachment); - package._chewieController?.play(); - } - setState(() {}); // ignore: no-empty-block - } - - @override - void dispose() { - _currentPage.dispose(); - _pageController.dispose(); - _isDisplayingDetail.dispose(); - for (final package in videoPackages.values) { - package.dispose(); - } - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - resizeToAvoidBottomInset: false, - body: ValueListenableBuilder( - valueListenable: _currentPage, - builder: (context, currentPage, child) { - final _currentAttachmentPackage = - widget.mediaAttachmentPackages[currentPage]; - final _currentMessage = _currentAttachmentPackage.message; - final _currentAttachment = _currentAttachmentPackage.attachment; - return Stack( - children: [ - child!, - ValueListenableBuilder( - valueListenable: _isDisplayingDetail, - builder: (context, isDisplayingDetail, child) { - final mediaQuery = MediaQuery.of(context); - final topPadding = mediaQuery.padding.top; - return AnimatedPositionedDirectional( - duration: kThemeAnimationDuration, - curve: Curves.easeInOut, - top: - isDisplayingDetail ? 0 : -(topPadding + kToolbarHeight), - start: 0, - end: 0, - height: topPadding + kToolbarHeight, - child: StreamGalleryHeader( - userName: widget.userName, - sentAt: context.translations.sentAtText( - date: _currentAttachmentPackage.message.createdAt, - time: _currentAttachmentPackage.message.createdAt, - ), - onBackPressed: Navigator.of(context).pop, - message: _currentMessage, - attachment: _currentAttachment, - onShowMessage: widget.onShowMessage != null - ? () { - Navigator.pop(context); - Navigator.pop(context); - widget.onShowMessage?.call( - _currentMessage, - StreamChannel.of(context).channel, - ); - } - : null, - onReplyMessage: widget.onReplyMessage != null - ? () { - Navigator.pop(context); - Navigator.pop(context); - widget.onReplyMessage?.call( - _currentMessage, - ); - } - : null, - attachmentActionsModalBuilder: - widget.attachmentActionsModalBuilder, - ), - ); - }, - ), - if (!_currentMessage.isEphemeral) - ValueListenableBuilder( - valueListenable: _isDisplayingDetail, - builder: (context, isDisplayingDetail, child) { - final mediaQuery = MediaQuery.of(context); - final bottomPadding = mediaQuery.padding.bottom; - return AnimatedPositionedDirectional( - duration: kThemeAnimationDuration, - curve: Curves.easeInOut, - bottom: isDisplayingDetail - ? 0 - : -(bottomPadding + kToolbarHeight), - start: 0, - end: 0, - height: bottomPadding + kToolbarHeight, - child: StreamGalleryFooter( - currentPage: currentPage, - totalPages: widget.mediaAttachmentPackages.length, - mediaAttachmentPackages: widget.mediaAttachmentPackages, - mediaSelectedCallBack: (val) { - _currentPage.value = val; - _pageController.animateToPage( - val, - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - Navigator.pop(context); - }, - ), - ); - }, - ), - if (widget.mediaAttachmentPackages.length > 1) ...[ - if (currentPage > 0) - GalleryNavigationItem( - left: 8, - opacityAnimation: _isDisplayingDetail, - icon: const Icon(Icons.chevron_left_rounded), - onPressed: () { - _currentPage.value--; - _pageController.previousPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - }, - ), - if (currentPage < widget.mediaAttachmentPackages.length - 1) - GalleryNavigationItem( - right: 8, - opacityAnimation: _isDisplayingDetail, - icon: const Icon(Icons.chevron_right_rounded), - onPressed: () { - _currentPage.value++; - _pageController.nextPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - }, - ), - ], - ], - ); - }, - child: InkWell( - onTap: switchDisplayingDetail, - child: KeyboardShortcutRunner( - onEscapeKeypress: Navigator.of(context).pop, - onLeftArrowKeypress: () { - if (_currentPage.value > 0) { - _currentPage.value--; - _pageController.previousPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - } - }, - onRightArrowKeypress: () { - if (_currentPage.value < - widget.mediaAttachmentPackages.length - 1) { - _currentPage.value++; - _pageController.nextPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - } - }, - child: PageView.builder( - controller: _pageController, - itemCount: widget.mediaAttachmentPackages.length, - onPageChanged: (val) { - _currentPage.value = val; - if (videoPackages.isEmpty) return; - final currentAttachment = - widget.mediaAttachmentPackages[val].attachment; - for (final e in videoPackages.values) { - if (e._attachment != currentAttachment) { - e._chewieController?.pause(); - } - } - if (widget.autoplayVideos && - currentAttachment.type == AttachmentType.video) { - final controller = videoPackages[currentAttachment.id]!; - controller._chewieController?.play(); - } - }, - itemBuilder: (context, index) { - final currentAttachmentPackage = - widget.mediaAttachmentPackages[index]; - final attachment = currentAttachmentPackage.attachment; - return ValueListenableBuilder( - valueListenable: _isDisplayingDetail, - builder: (context, isDisplayingDetail, child) { - return AnimatedContainer( - duration: kThemeChangeDuration, - color: isDisplayingDetail - ? StreamChannelHeaderTheme.of(context).color - : Colors.black, - child: Builder( - builder: (context) { - if (attachment.type == AttachmentType.image || - attachment.type == AttachmentType.giphy) { - return PhotoView.customChild( - maxScale: PhotoViewComputedScale.covered, - minScale: PhotoViewComputedScale.contained, - backgroundDecoration: const BoxDecoration( - color: Colors.transparent, - ), - child: StreamMediaAttachmentThumbnail( - media: attachment, - width: double.infinity, - height: double.infinity, - ), - ); - } else if (attachment.type == AttachmentType.video) { - final controller = videoPackages[attachment.id]!; - if (!controller.initialized) { - return const Center( - child: CircularProgressIndicator.adaptive(), - ); - } - - final mediaQuery = MediaQuery.of(context); - final bottomPadding = mediaQuery.padding.bottom; - - return AnimatedPadding( - duration: kThemeChangeDuration, - padding: EdgeInsets.symmetric( - vertical: isDisplayingDetail - ? kToolbarHeight + bottomPadding - : 0, - ), - child: Chewie( - controller: controller.chewieController!, - ), - ); - } - - return const SizedBox(); - }, - ), - ); - }, - ); - }, - ), - ), - ), - ), - ); - } -} - -/// Class for packaging up things required for videos -class VideoPackage { - /// Constructor for creating [VideoPackage] - VideoPackage( - this._attachment, { - bool showControls = false, - bool autoInitialize = true, - }) : _showControls = showControls, - _autoInitialize = autoInitialize, - _videoPlayerController = _attachment.localUri != null - ? VideoPlayerController.file( - File.fromUri(_attachment.localUri!), - ) - : VideoPlayerController.networkUrl( - Uri.parse(_attachment.assetUrl!), - ); - - final Attachment _attachment; - final bool _showControls; - final bool _autoInitialize; - final VideoPlayerController _videoPlayerController; - ChewieController? _chewieController; - - /// Get video player for video - VideoPlayerController get videoPlayer => _videoPlayerController; - - /// Get [ChewieController] for video - ChewieController? get chewieController => _chewieController; - - /// Check if controller is initialised - bool get initialized => _videoPlayerController.value.isInitialized; - - /// Initialize all things required for [VideoPackage] - Future initialize() { - return _videoPlayerController.initialize().then((_) { - _chewieController = ChewieController( - videoPlayerController: _videoPlayerController, - autoInitialize: _autoInitialize, - showControls: _showControls, - showOptions: false, - aspectRatio: _videoPlayerController.value.aspectRatio, - ); - }); - } - - /// Add a listener to video player controller - void addListener(VoidCallback listener) => - _videoPlayerController.addListener(listener); - - /// Remove a listener to video player controller - void removeListener(VoidCallback listener) => - _videoPlayerController.removeListener(listener); - - /// Dispose controllers - Future dispose() { - _chewieController?.dispose(); - return _videoPlayerController.dispose(); - } -} diff --git a/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_builder.dart b/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_builder.dart deleted file mode 100644 index f1919db192..0000000000 --- a/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_builder.dart +++ /dev/null @@ -1,86 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/fullscreen_media/fsm_stub.dart' - if (dart.library.io) 'full_screen_media_desktop.dart' as desktop_fsm; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template fsmBuilder} -/// A wrapper widget for conditionally providing the proper -/// StreamFullScreenMedia widget when writing an application that targets -/// all available Flutter platforms (Android, iOS, macOS, Windows, Linux, -/// & Web). -/// -/// This is required because: -/// * `package:video_player` and `package:chewie` do not support macOS, Windows, -/// & Linux, but _do_ support Android, iOS, & Web. -/// * `package:dart_vlc` _does_ support macOS, Windows, & Linux via FFI. This -/// has the unfortunate consequence of not supporting Web. -/// -/// This widget makes use of dart's conditional imports to ensure that Stream's -/// desktop implementation of StreamFullScreenMedia is not imported when -/// building applications that target web. Additionally, this widget ensures -/// that applications targeting mobile platforms do not build the version of -/// StreamFullScreenMedia that targets desktop platforms (even though -/// `package:dart_vlc` technically supports iOS). -/// {@endtemplate} -class StreamFullScreenMediaBuilder extends StatelessWidget { - /// {@macro fsmBuilder} - const StreamFullScreenMediaBuilder({ - super.key, - required this.mediaAttachmentPackages, - required this.startIndex, - required this.userName, - this.onShowMessage, - this.onReplyMessage, - this.attachmentActionsModalBuilder, - this.autoplayVideos = false, - }); - - /// The url of the image - final List mediaAttachmentPackages; - - /// First index of media shown - final int startIndex; - - /// Username of sender - final String userName; - - /// Callback for when show message is tapped - final ShowMessageCallback? onShowMessage; - - /// Callback for when reply message is tapped - final ReplyMessageCallback? onReplyMessage; - - /// Widget builder for attachment actions modal - /// [defaultActionsModal] is the default [AttachmentActionsModal] config - /// Use [defaultActionsModal.copyWith] to easily customize it - final AttachmentActionsBuilder? attachmentActionsModalBuilder; - - /// Auto-play videos when page is opened - final bool autoplayVideos; - - @override - Widget build(BuildContext context) { - if (!kIsWeb && isDesktopVideoPlayerSupported) { - return desktop_fsm.getFsm( - mediaAttachmentPackages: mediaAttachmentPackages, - startIndex: startIndex, - userName: userName, - autoplayVideos: autoplayVideos, - onShowMessage: onShowMessage, - onReplyMessage: onReplyMessage, - attachmentActionsModalBuilder: attachmentActionsModalBuilder, - ); - } - - return StreamFullScreenMedia( - mediaAttachmentPackages: mediaAttachmentPackages, - startIndex: startIndex, - userName: userName, - onShowMessage: onShowMessage, - onReplyMessage: onReplyMessage, - attachmentActionsModalBuilder: attachmentActionsModalBuilder, - autoplayVideos: autoplayVideos, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_desktop.dart b/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_desktop.dart deleted file mode 100644 index c9d6773b01..0000000000 --- a/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_desktop.dart +++ /dev/null @@ -1,451 +0,0 @@ -import 'package:contextmenu/contextmenu.dart'; -import 'package:flutter/material.dart'; -import 'package:media_kit/media_kit.dart'; -import 'package:media_kit_video/media_kit_video.dart'; -import 'package:photo_view/photo_view.dart'; -import 'package:stream_chat_flutter/src/context_menu_items/download_menu_item.dart'; -import 'package:stream_chat_flutter/src/fullscreen_media/full_screen_media_widget.dart'; -import 'package:stream_chat_flutter/src/fullscreen_media/gallery_navigation_item.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Returns an instance of [FullScreenMediaDesktop]. -/// -/// This should ONLY be used in [FullScreenMediaBuilder]. -FullScreenMediaWidget getFsm({ - Key? key, - required List mediaAttachmentPackages, - required int startIndex, - required String userName, - ShowMessageCallback? onShowMessage, - ReplyMessageCallback? onReplyMessage, - AttachmentActionsBuilder? attachmentActionsModalBuilder, - bool? autoplayVideos, -}) { - return FullScreenMediaDesktop( - key: key, - mediaAttachmentPackages: mediaAttachmentPackages, - startIndex: startIndex, - userName: userName, - onReplyMessage: onReplyMessage, - onShowMessage: onShowMessage, - attachmentActionsModalBuilder: attachmentActionsModalBuilder, - autoplayVideos: autoplayVideos ?? false, - ); -} - -/// A full screen image widget -class FullScreenMediaDesktop extends FullScreenMediaWidget { - /// Instantiate a new FullScreenImage - const FullScreenMediaDesktop({ - super.key, - required this.mediaAttachmentPackages, - this.startIndex = 0, - String? userName, - this.onShowMessage, - this.onReplyMessage, - this.attachmentActionsModalBuilder, - this.autoplayVideos = false, - }) : userName = userName ?? ''; - - /// The url of the image - final List mediaAttachmentPackages; - - /// First index of media shown - final int startIndex; - - /// Username of sender - final String userName; - - /// Callback for when show message is tapped - final ShowMessageCallback? onShowMessage; - - /// Callback for when reply message is tapped - final ReplyMessageCallback? onReplyMessage; - - /// Widget builder for attachment actions modal - /// [defaultActionsModal] is the default [AttachmentActionsModal] config - /// Use [defaultActionsModal.copyWith] to easily customize it - final AttachmentActionsBuilder? attachmentActionsModalBuilder; - - /// Auto-play videos when page is opened - final bool autoplayVideos; - - @override - _FullScreenMediaDesktopState createState() => _FullScreenMediaDesktopState(); -} - -class _FullScreenMediaDesktopState extends State { - late final PageController _pageController; - - late final _currentPage = ValueNotifier(widget.startIndex); - late final _isDisplayingDetail = ValueNotifier(true); - - void switchDisplayingDetail() { - _isDisplayingDetail.value = !_isDisplayingDetail.value; - } - - final videoPackages = {}; - - @override - void initState() { - super.initState(); - _pageController = PageController(initialPage: widget.startIndex); - for (var i = 0; i < widget.mediaAttachmentPackages.length; i++) { - final attachment = widget.mediaAttachmentPackages[i].attachment; - if (attachment.type != AttachmentType.video) continue; - final package = DesktopVideoPackage(attachment); - videoPackages[attachment.id] = package; - } - } - - @override - void dispose() { - _currentPage.dispose(); - _pageController.dispose(); - _isDisplayingDetail.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final containsOnlyVideos = - widget.mediaAttachmentPackages.length == videoPackages.length; - - return Scaffold( - resizeToAvoidBottomInset: false, - body: containsOnlyVideos ? _buildVideoPageView() : _buildPageView(), - ); - } - - Widget _buildVideoPageView() { - return Stack( - children: [ - ContextMenuArea( - verticalPadding: 0, - builder: (_) => [ - DownloadMenuItem( - attachment: - widget.mediaAttachmentPackages[_currentPage.value].attachment, - ), - ], - child: _PlaylistPlayer( - packages: videoPackages.values.toList(), - autoStart: widget.autoplayVideos, - ), - ), - Positioned( - left: 8, - top: 8, - child: MouseRegion( - cursor: SystemMouseCursors.click, - child: GestureDetector( - onTap: () { - videoPackages.values.first.player.stop(); - Navigator.of(context).pop(); - }, - child: const StreamSvgIcon( - size: 30, - icon: StreamSvgIcons.close, - ), - ), - ), - ), - ], - ); - } - - Widget _buildPageView() { - return ValueListenableBuilder( - valueListenable: _currentPage, - builder: (context, currentPage, child) { - final _currentAttachmentPackage = - widget.mediaAttachmentPackages[currentPage]; - final _currentMessage = _currentAttachmentPackage.message; - final _currentAttachment = _currentAttachmentPackage.attachment; - return Stack( - children: [ - child!, - ValueListenableBuilder( - valueListenable: _isDisplayingDetail, - builder: (context, isDisplayingDetail, child) { - final mediaQuery = MediaQuery.of(context); - final topPadding = mediaQuery.padding.top; - return AnimatedPositionedDirectional( - duration: kThemeAnimationDuration, - curve: Curves.easeInOut, - top: isDisplayingDetail ? 0 : -(topPadding + kToolbarHeight), - start: 0, - end: 0, - height: topPadding + kToolbarHeight, - child: StreamGalleryHeader( - userName: widget.userName, - sentAt: context.translations.sentAtText( - date: _currentAttachmentPackage.message.createdAt, - time: _currentAttachmentPackage.message.createdAt, - ), - onBackPressed: Navigator.of(context).pop, - message: _currentMessage, - attachment: _currentAttachment, - onShowMessage: () { - widget.onShowMessage?.call( - _currentMessage, - StreamChannel.of(context).channel, - ); - }, - attachmentActionsModalBuilder: - widget.attachmentActionsModalBuilder, - ), - ); - }, - ), - if (!_currentMessage.isEphemeral) - ValueListenableBuilder( - valueListenable: _isDisplayingDetail, - builder: (context, isDisplayingDetail, child) { - final mediaQuery = MediaQuery.of(context); - final bottomPadding = mediaQuery.padding.bottom; - return AnimatedPositionedDirectional( - duration: kThemeAnimationDuration, - curve: Curves.easeInOut, - bottom: isDisplayingDetail - ? 0 - : -(bottomPadding + kToolbarHeight), - start: 0, - end: 0, - height: bottomPadding + kToolbarHeight, - child: StreamGalleryFooter( - currentPage: currentPage, - totalPages: widget.mediaAttachmentPackages.length, - mediaAttachmentPackages: widget.mediaAttachmentPackages, - mediaSelectedCallBack: (val) { - _currentPage.value = val; - _pageController.animateToPage( - val, - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - Navigator.pop(context); - }, - ), - ); - }, - ), - if (widget.mediaAttachmentPackages.length > 1) ...[ - if (currentPage > 0) - GalleryNavigationItem( - left: 8, - opacityAnimation: _isDisplayingDetail, - icon: const Icon(Icons.chevron_left_rounded), - onPressed: () { - _currentPage.value--; - _pageController.previousPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - }, - ), - if (currentPage < widget.mediaAttachmentPackages.length - 1) - GalleryNavigationItem( - right: 8, - opacityAnimation: _isDisplayingDetail, - icon: const Icon(Icons.chevron_right_rounded), - onPressed: () { - _currentPage.value++; - _pageController.nextPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - }, - ), - ], - ], - ); - }, - child: InkWell( - onTap: switchDisplayingDetail, - child: KeyboardShortcutRunner( - onEscapeKeypress: Navigator.of(context).pop, - onLeftArrowKeypress: () { - if (_currentPage.value > 0) { - _currentPage.value--; - _pageController.previousPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - } - }, - onRightArrowKeypress: () { - if (_currentPage.value < - widget.mediaAttachmentPackages.length - 1) { - _currentPage.value++; - _pageController.nextPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - } - }, - child: PageView.builder( - controller: _pageController, - itemCount: widget.mediaAttachmentPackages.length, - onPageChanged: (val) { - _currentPage.value = val; - if (videoPackages.isEmpty) return; - final currentAttachment = - widget.mediaAttachmentPackages[val].attachment; - for (final p in videoPackages.values) { - if (p.attachment != currentAttachment) { - p.player.pause(); - } - } - if (widget.autoplayVideos && - currentAttachment.type == AttachmentType.video) { - final package = videoPackages[currentAttachment.id]!; - package.player.play(); - } - }, - itemBuilder: (context, index) { - final currentAttachmentPackage = - widget.mediaAttachmentPackages[index]; - final attachment = currentAttachmentPackage.attachment; - - return ValueListenableBuilder( - valueListenable: _isDisplayingDetail, - builder: (context, isDisplayingDetail, child) { - return AnimatedContainer( - duration: kThemeChangeDuration, - color: isDisplayingDetail - ? StreamChannelHeaderTheme.of(context).color - : Colors.black, - child: Builder( - builder: (context) { - if (attachment.type == AttachmentType.image || - attachment.type == AttachmentType.giphy) { - return PhotoView.customChild( - maxScale: PhotoViewComputedScale.covered, - minScale: PhotoViewComputedScale.contained, - backgroundDecoration: const BoxDecoration( - color: Colors.transparent, - ), - child: StreamMediaAttachmentThumbnail( - media: attachment, - width: double.infinity, - height: double.infinity, - ), - ); - } else if (attachment.type == AttachmentType.video) { - final package = videoPackages[attachment.id]!; - if (package.attachment.assetUrl != null) { - package.player.open( - Playlist( - [ - Media(package.attachment.assetUrl!), - ], - ), - play: widget.autoplayVideos, - ); - } - - final mediaQuery = MediaQuery.of(context); - final bottomPadding = mediaQuery.padding.bottom; - - return AnimatedPadding( - duration: kThemeChangeDuration, - padding: EdgeInsets.symmetric( - vertical: isDisplayingDetail - ? kToolbarHeight + bottomPadding - : 0, - ), - child: ContextMenuArea( - verticalPadding: 0, - builder: (_) => [ - DownloadMenuItem( - attachment: attachment, - ), - ], - child: Video( - controller: package.controller, - ), - ), - ); - } - - return const SizedBox(); - }, - ), - ); - }, - ); - }, - ), - ), - ), - ); - } -} - -/// Class for packaging up things required for videos -class DesktopVideoPackage { - /// Constructor for creating [VideoPackage] - factory DesktopVideoPackage( - Attachment attachment, { - bool showControls = true, - }) { - final player = Player(); - final controller = VideoController(player); - return DesktopVideoPackage._internal( - attachment, - player, - controller, - showControls, - ); - } - - DesktopVideoPackage._internal( - this.attachment, - this.player, - this.controller, - this.showControls, - ); - - /// The video attachment to play. - final Attachment attachment; - - /// The VLC player to use. - final Player player; - - /// The VLC video controller to use. - final VideoController controller; - - /// Whether to show the player controls or not. - final bool showControls; -} - -class _PlaylistPlayer extends StatelessWidget { - const _PlaylistPlayer({ - required this.packages, - required this.autoStart, - }); - - final List packages; - final bool autoStart; - - @override - Widget build(BuildContext context) { - final _media = []; - for (final package in packages) { - if (package.attachment.assetUrl != null) { - _media.add(Media(package.attachment.assetUrl!)); - } - } - packages.first.player.open( - Playlist( - _media, - ), - play: autoStart, - ); - return Video( - controller: packages.first.controller, - fit: BoxFit.cover, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_widget.dart b/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_widget.dart deleted file mode 100644 index adcac9d3ff..0000000000 --- a/packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_widget.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:flutter/material.dart'; - -/// {@template fsmWidget} -/// An ultra-simple abstract class that allows [FullScreenMediaBuilder] -/// to call `getFsm()` and build the correct version of FullScreenMedia. -/// {@endtemplate} -abstract class FullScreenMediaWidget extends StatefulWidget { - /// {@macro fsmWidget} - const FullScreenMediaWidget({super.key}); -} diff --git a/packages/stream_chat_flutter/lib/src/fullscreen_media/gallery_navigation_item.dart b/packages/stream_chat_flutter/lib/src/fullscreen_media/gallery_navigation_item.dart deleted file mode 100644 index 5d1af701b4..0000000000 --- a/packages/stream_chat_flutter/lib/src/fullscreen_media/gallery_navigation_item.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/platform_widget_builder/src/platform_widget_builder.dart'; - -/// A widget for desktop and web users to be able to navigate left and right -/// through a gallery of images. -class GalleryNavigationItem extends StatelessWidget { - /// Builds a [GalleryNavigationItem]. - const GalleryNavigationItem({ - super.key, - required this.icon, - this.iconSize = 48, - required this.onPressed, - required this.opacityAnimation, - this.left, - this.right, - }); - - /// The icon to display. - final Widget icon; - - /// The size of the icon. - /// - /// Defaults to 48. - final double iconSize; - - /// The callback to perform when the button is clicked. - final VoidCallback onPressed; - - /// The animation for showing & hiding this widget. - final ValueListenable opacityAnimation; - - /// The left-hand placement of the button. - final double? left; - - /// The right-hand placement of the button. - final double? right; - - @override - Widget build(BuildContext context) { - return PlatformWidgetBuilder( - desktop: (_, child) => child, - web: (_, child) => child, - child: Positioned( - left: left, - right: right, - top: MediaQuery.of(context).size.height / 2, - child: ValueListenableBuilder( - valueListenable: opacityAnimation, - builder: (context, shouldShow, child) { - return AnimatedOpacity( - opacity: shouldShow ? 1 : 0, - duration: kThemeAnimationDuration, - child: child, - ); - }, - child: Material( - color: Colors.transparent, - type: MaterialType.circle, - clipBehavior: Clip.antiAlias, - child: IconButton( - icon: icon, - iconSize: iconSize, - onPressed: onPressed, - ), - ), - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/gallery/gallery_footer.dart b/packages/stream_chat_flutter/lib/src/gallery/gallery_footer.dart deleted file mode 100644 index 73476c4428..0000000000 --- a/packages/stream_chat_flutter/lib/src/gallery/gallery_footer.dart +++ /dev/null @@ -1,298 +0,0 @@ -import 'dart:io'; - -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:share_plus/share_plus.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamGalleryFooter} -/// Footer widget for media display -/// {@endtemplate} -class StreamGalleryFooter extends StatefulWidget - implements PreferredSizeWidget { - /// {@macro streamGalleryFooter} - const StreamGalleryFooter({ - super.key, - this.onBackPressed, - this.onTitleTap, - this.onImageTap, - this.currentPage = 0, - this.totalPages = 0, - required this.mediaAttachmentPackages, - this.mediaSelectedCallBack, - this.backgroundColor, - }) : preferredSize = const Size.fromHeight(kToolbarHeight); - - /// Callback to call when pressing the back button. - /// By default it calls [Navigator.pop] - final VoidCallback? onBackPressed; - - /// Callback to call when the header is tapped. - final VoidCallback? onTitleTap; - - /// Callback to call when the image is tapped. - final VoidCallback? onImageTap; - - /// Stores the current index of media shown - final int currentPage; - - /// Total number of pages of media - final int totalPages; - - /// All attachments to show - final List mediaAttachmentPackages; - - /// Callback when media is selected - final ValueChanged? mediaSelectedCallBack; - - /// The background color of this [StreamGalleryFooter]. - final Color? backgroundColor; - - @override - _StreamGalleryFooterState createState() => _StreamGalleryFooterState(); - - @override - final Size preferredSize; -} - -class _StreamGalleryFooterState extends State { - final shareButtonKey = GlobalKey(); - - @override - Widget build(BuildContext context) { - const showShareButton = !kIsWeb; - final mediaQueryData = MediaQuery.of(context); - final galleryFooterThemeData = StreamGalleryFooterTheme.of(context); - return SizedBox.fromSize( - size: Size( - mediaQueryData.size.width, - mediaQueryData.padding.bottom + widget.preferredSize.height, - ), - child: MediaQuery.removePadding( - context: context, - removeTop: true, - child: BottomAppBar( - surfaceTintColor: - widget.backgroundColor ?? galleryFooterThemeData.backgroundColor, - color: - widget.backgroundColor ?? galleryFooterThemeData.backgroundColor, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - if (!showShareButton) - const SizedBox(width: 48) - else - IconButton( - key: shareButtonKey, - icon: StreamSvgIcon( - size: 24, - icon: StreamSvgIcons.share, - color: galleryFooterThemeData.shareIconColor, - ), - onPressed: () async { - final attachment = widget - .mediaAttachmentPackages[widget.currentPage].attachment; - final url = attachment.imageUrl ?? - attachment.assetUrl ?? - attachment.thumbUrl!; - final type = attachment.type == AttachmentType.image - ? 'jpg' - : url.split('?').first.split('.').last; - final request = await HttpClient().getUrl(Uri.parse(url)); - final response = await request.close(); - final bytes = - await consolidateHttpClientResponseBytes(response); - final tmpPath = await getTemporaryDirectory(); - final filePath = '${tmpPath.path}/${attachment.id}.$type'; - final file = File(filePath); - await file.writeAsBytes(bytes); - final box = - shareButtonKey.currentContext?.findRenderObject(); - final size = shareButtonKey.currentContext?.size; - - final position = - (box! as RenderBox).localToGlobal(Offset.zero); - - await Share.shareXFiles( - [XFile(filePath)], - sharePositionOrigin: Rect.fromLTWH( - position.dx, - position.dy, - size?.width ?? 50, - (size?.height ?? 2) / 2, - ), - ); - }, - ), - InkWell( - onTap: widget.onTitleTap, - child: SizedBox( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - context.translations.galleryPaginationText( - currentPage: widget.currentPage, - totalPages: widget.totalPages, - ), - style: galleryFooterThemeData.titleTextStyle, - ), - ], - ), - ), - ), - IconButton( - icon: StreamSvgIcon( - icon: StreamSvgIcons.grid, - color: galleryFooterThemeData.gridIconButtonColor, - ), - onPressed: () => _showPhotosModal(context), - ), - ], - ), - ), - ), - ); - } - - void _showPhotosModal(context) { - final chatThemeData = StreamChatTheme.of(context); - final galleryFooterThemeData = StreamGalleryFooterTheme.of(context); - showModalBottomSheet( - context: context, - barrierColor: galleryFooterThemeData.bottomSheetBarrierColor, - backgroundColor: galleryFooterThemeData.bottomSheetBackgroundColor, - isScrollControlled: true, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16), - ), - ), - builder: (context) { - return DraggableScrollableSheet( - expand: false, - initialChildSize: - (CurrentPlatform.isAndroid || CurrentPlatform.isIos) ? 0.3 : 0.5, - minChildSize: 0.3, - maxChildSize: 0.7, - builder: (context, scrollController) => Column( - children: [ - Stack( - children: [ - Center( - child: Padding( - padding: const EdgeInsets.all(16), - child: Text( - context.translations.photosLabel, - style: - galleryFooterThemeData.bottomSheetPhotosTextStyle, - ), - ), - ), - Align( - alignment: Alignment.centerRight, - child: IconButton( - icon: StreamSvgIcon( - icon: StreamSvgIcons.close, - color: galleryFooterThemeData.bottomSheetCloseIconColor, - ), - onPressed: () => Navigator.of(context).maybePop(), - ), - ), - ], - ), - Flexible( - child: GridView.builder( - shrinkWrap: true, - controller: scrollController, - itemCount: widget.mediaAttachmentPackages.length, - padding: const EdgeInsets.all(1), - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, - mainAxisSpacing: 2, - crossAxisSpacing: 2, - ), - itemBuilder: (context, index) { - Widget media; - final attachmentPackage = - widget.mediaAttachmentPackages[index]; - final attachment = attachmentPackage.attachment; - final message = attachmentPackage.message; - if (attachment.type == AttachmentType.video) { - media = MouseRegion( - cursor: SystemMouseCursors.click, - child: GestureDetector( - onTap: () => widget.mediaSelectedCallBack!(index), - child: AspectRatio( - aspectRatio: 1, - child: StreamVideoAttachmentThumbnail( - video: attachment, - ), - ), - ), - ); - } else { - media = MouseRegion( - cursor: SystemMouseCursors.click, - child: GestureDetector( - onTap: () => widget.mediaSelectedCallBack!(index), - child: AspectRatio( - aspectRatio: 1, - child: CachedNetworkImage( - imageUrl: attachment.imageUrl ?? - attachment.assetUrl ?? - attachment.thumbUrl!, - fit: BoxFit.cover, - ), - ), - ), - ); - } - - return Stack( - children: [ - media, - if (message.user != null) - Padding( - padding: const EdgeInsets.all(8), - child: Container( - padding: const EdgeInsets.all(2), - clipBehavior: Clip.antiAlias, - decoration: BoxDecoration( - shape: BoxShape.circle, - // ignore: deprecated_member_use - color: Colors.white.withOpacity(0.6), - boxShadow: [ - BoxShadow( - blurRadius: 8, - color: chatThemeData - .colorTheme.textHighEmphasis - // ignore: deprecated_member_use - .withOpacity(0.3), - ), - ], - ), - child: StreamUserAvatar( - user: message.user!, - constraints: - BoxConstraints.tight(const Size(24, 24)), - showOnlineStatus: false, - ), - ), - ), - ], - ); - }, - ), - ), - ], - ), - ); - }, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/gallery/gallery_header.dart b/packages/stream_chat_flutter/lib/src/gallery/gallery_header.dart deleted file mode 100644 index b5f8771c64..0000000000 --- a/packages/stream_chat_flutter/lib/src/gallery/gallery_header.dart +++ /dev/null @@ -1,176 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:stream_chat_flutter/src/attachment_actions_modal/attachment_actions_modal.dart'; -import 'package:stream_chat_flutter/src/icons/stream_svg_icon.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter/src/theme/themes.dart'; -import 'package:stream_chat_flutter/src/utils/utils.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// {@template streamGalleryHeader} -/// Header/AppBar widget for media display screen -/// {@endtemplate} -class StreamGalleryHeader extends StatelessWidget - implements PreferredSizeWidget { - /// {@macro streamGalleryHeader} - const StreamGalleryHeader({ - super.key, - required this.message, - required this.attachment, - this.showBackButton = true, - this.onBackPressed, - this.onShowMessage, - this.onReplyMessage, - this.onTitleTap, - this.onImageTap, - this.userName = '', - this.sentAt = '', - this.backgroundColor, - this.attachmentActionsModalBuilder, - this.elevation = 1.0, - }) : preferredSize = const Size.fromHeight(kToolbarHeight); - - /// Whether to show the leading back button. - /// - /// Defaults to `true`. - final bool showBackButton; - - /// Callback to call when pressing the back button. - /// By default it calls [Navigator.pop] - final VoidCallback? onBackPressed; - - /// Callback to call when pressing the show message button. - final VoidCallback? onShowMessage; - - /// Callback to call when pressing the reply message button. - final VoidCallback? onReplyMessage; - - /// Callback to call when the header is tapped. - final VoidCallback? onTitleTap; - - /// Callback to call when the image is tapped. - final VoidCallback? onImageTap; - - /// Message which attachments are attached to - final Message message; - - /// The attachment that's currently in focus - final Attachment attachment; - - /// Username of sender - final String userName; - - /// Text which connotes the time the message was sent - final String sentAt; - - /// The background color of this [StreamGalleryHeader]. - final Color? backgroundColor; - - /// {@macro attachmentActionsBuilder} - final AttachmentActionsBuilder? attachmentActionsModalBuilder; - - /// The elevation of this [StreamGalleryHeader]. - /// - /// Defaults to `1.0`. When used for desktop & web platforms, it should - /// be set to `0.0`. - final double elevation; - - @override - Widget build(BuildContext context) { - final galleryHeaderThemeData = StreamGalleryHeaderTheme.of(context); - final theme = Theme.of(context); - return AppBar( - toolbarTextStyle: theme.textTheme.bodyMedium, - titleTextStyle: theme.textTheme.titleLarge, - systemOverlayStyle: theme.brightness == Brightness.dark - ? SystemUiOverlayStyle.light - : SystemUiOverlayStyle.dark, - elevation: elevation, - leading: showBackButton - ? IconButton( - icon: StreamSvgIcon( - icon: StreamSvgIcons.close, - color: galleryHeaderThemeData.closeButtonColor, - size: 24, - ), - onPressed: onBackPressed, - ) - : const SizedBox(), - surfaceTintColor: - backgroundColor ?? galleryHeaderThemeData.backgroundColor, - backgroundColor: - backgroundColor ?? galleryHeaderThemeData.backgroundColor, - actions: [ - if (!message.isEphemeral) - IconButton( - icon: StreamSvgIcon( - icon: StreamSvgIcons.menuPoint, - color: galleryHeaderThemeData.iconMenuPointColor, - ), - onPressed: () => _showMessageActionModalBottomSheet(context), - ), - ], - centerTitle: true, - title: !message.isEphemeral - ? InkWell( - onTap: onTitleTap, - child: SizedBox( - height: preferredSize.height, - width: preferredSize.width, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - userName, - style: galleryHeaderThemeData.titleTextStyle, - ), - Text( - sentAt, - style: galleryHeaderThemeData.subtitleTextStyle, - ), - ], - ), - ), - ) - : const SizedBox(), - ); - } - - @override - final Size preferredSize; - - Future _showMessageActionModalBottomSheet(BuildContext context) async { - final channel = StreamChannel.of(context).channel; - final galleryHeaderThemeData = - StreamChatTheme.of(context).galleryHeaderTheme; - - final defaultModal = AttachmentActionsModal( - attachment: attachment, - message: message, - onShowMessage: onShowMessage, - onReply: onReplyMessage, - ); - - final effectiveModal = attachmentActionsModalBuilder?.call( - context, - attachment, - defaultModal, - ) ?? - defaultModal; - - final result = await showDialog( - useRootNavigator: false, - context: context, - barrierColor: galleryHeaderThemeData.bottomSheetBarrierColor, - builder: (context) => StreamChannel( - channel: channel, - child: effectiveModal, - ), - ); - - if (result != null) { - Navigator.of(context).pop(result); - } - } -} diff --git a/packages/stream_chat_flutter/lib/src/icons/stream_svg_icon.dart b/packages/stream_chat_flutter/lib/src/icons/stream_svg_icon.dart deleted file mode 100644 index 03138b891e..0000000000 --- a/packages/stream_chat_flutter/lib/src/icons/stream_svg_icon.dart +++ /dev/null @@ -1,1338 +0,0 @@ -// ignore_for_file: deprecated_member_use_from_same_package - -import 'package:flutter/material.dart'; -import 'package:svg_icon_widget/svg_icon_widget.dart'; - -part 'stream_svg_icon.g.dart'; - -/// {@template StreamSvgIconData} -/// A class that holds the data for a [StreamSvgIcon]. -/// {@endtemplate} -typedef StreamSvgIconData = SvgIconData; - -/// {@template StreamSvgIcon} -/// Icon set of stream chat -/// {@endtemplate} -class StreamSvgIcon extends StatelessWidget { - /// Creates a [StreamSvgIcon]. - const StreamSvgIcon({ - super.key, - this.icon, - @Deprecated("Use 'icon' instead") this.assetName, - this.color, - double? size, - @Deprecated("Use 'size' instead") this.width, - @Deprecated("Use 'size' instead") this.height, - this.textDirection, - this.semanticLabel, - this.applyTextScaling, - }) : assert( - size == null || (width == null && height == null), - 'Cannot provide both a size and a width or height', - ), - size = size ?? width ?? height; - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.settings({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.settings, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.down({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.down, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.up({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.up, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.attach({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.attach, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.loveReaction({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.loveReaction, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.thumbsUpReaction({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.thumbsUpReaction, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.thumbsDownReaction({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.thumbsDownReaction, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.lolReaction({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.lolReaction, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.wutReaction({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.wutReaction, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.smile({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.smile, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.mentions({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.mentions, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.record({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.record, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.camera({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.camera, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.files({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.files, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.polls({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.polls, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.send({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.send, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.pictures({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.pictures, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.left({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.left, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.user({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.user, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.userAdd({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.userAdd, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.check({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.check, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.checkAll({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.checkAll, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.checkSend({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.checkSend, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.penWrite({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.penWrite, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.contacts({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.contacts, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.close({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.close, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.search({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.search, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.right({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.right, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.mute({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.mute, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.userRemove({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.userRemove, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.lightning({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.lightning, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.emptyCircleLeft({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.emptyCircleRight, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.message({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.message, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.messageUnread({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.messageUnread, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.thread({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.threadReply, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.reply({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.reply, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.edit({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.edit, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.download({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.download, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.cloudDownload({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.cloudDownload, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.copy({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.copy, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.delete({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.delete, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.eye({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.eye, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.arrowRight({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.arrowRight, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.closeSmall({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.closeSmall, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.iconCurveLineLeftUp({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.reply, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.iconMoon({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.moon, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.iconShare({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.share, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.iconGrid({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.grid, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.iconSendMessage({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.sendMessage, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.iconMenuPoint({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.menuPoint, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.iconSave({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.save, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.shareArrow({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.shareArrow, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.filetypeAac({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.filetypeAudioAac, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.filetype7z({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.filetypeCompression7z, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.filetypeCsv({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.filetypeCodeCsv, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.filetypeDoc({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.filetypeTextDoc, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.filetypeDocx({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.filetypeTextDocx, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.filetypeGeneric({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.filetypeOtherStandard, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.filetypeHtml({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.filetypeCodeHtml, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.filetypeMd({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.filetypeCodeMd, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.filetypeOdt({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.filetypeTextOdt, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.filetypePdf({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.filetypeOtherPdf, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.filetypePpt({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.filetypePresentationPpt, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.filetypePptx({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.filetypePresentationPptx, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.filetypeRar({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.filetypeCompressionRar, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.filetypeRtf({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.filetypeTextRtf, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.filetypeTar({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.filetypeCodeTar, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.filetypeTxt({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.filetypeTextTxt, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.filetypeXls({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.filetypeSpreadsheetXls, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.filetypeXlsx({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.filetypeSpreadsheetXlsx, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.filetypeZip({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.filetypeCompressionZip, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.iconGroup({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.group, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.iconNotification({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.notification, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.iconUserDelete({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.userDelete, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.error({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.error, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.circleUp({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.circleUp, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.iconUserSettings({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.userSettings, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.giphyIcon({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.giphy, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.imgur({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.imgur, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.volumeUp({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.volumeUp, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.flag({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.flag, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.iconFlag({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.flag, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.retry({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.retry, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.pin({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.pin, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.videoCall({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.videoCall, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.award({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.award, - color: color, - size: size, - ); - } - - /// [StreamSvgIcon] type - @Deprecated("Use regular 'StreamSvgIcon' with 'icon' instead") - factory StreamSvgIcon.reload({ - double? size, - Color? color, - }) { - return StreamSvgIcon( - icon: StreamSvgIcons.reload, - color: color, - size: size, - ); - } - - /// The icon to display. - /// - /// The icon can be null, in which case the widget will render as an empty - /// space of the specified [size]. - final StreamSvgIconData? icon; - - /// The asset to display. - /// - /// The asset can be null, in which case the widget will render as an empty - /// space of the specified [size]. - @Deprecated("Use 'icon' instead") - final String? assetName; - - /// Width of icon - @Deprecated("Use 'size' instead") - final double? width; - - /// Height of icon - @Deprecated("Use 'size' instead") - final double? height; - - /// The size of the icon in logical pixels. - /// - /// Icons occupy a square with width and height equal to size. - /// - /// Defaults to the nearest [IconTheme]'s [IconThemeData.size]. - /// - /// If this [Icon] is being placed inside an [IconButton], then use - /// [IconButton.iconSize] instead, so that the [IconButton] can make the - /// splash area the appropriate size as well. The [IconButton] uses an - /// [IconTheme] to pass down the size to the [Icon]. - final double? size; - - /// The color to use when drawing the icon. - /// - /// Defaults to the nearest [IconTheme]'s [IconThemeData.color]. - /// - /// The color (whether specified explicitly here or obtained from the - /// [IconTheme]) will be further adjusted by the nearest [IconTheme]'s - /// [IconThemeData.opacity]. - /// - /// {@tool snippet} - /// Typically, a Material Design color will be used, as follows: - /// - /// ```dart - /// StreamSvgIcon( - /// icon: 'reload.svg', - /// color: Colors.blue.shade400, - /// ) - /// ``` - /// {@end-tool} - final Color? color; - - /// The text direction to use for rendering the icon. - /// - /// If this is null, the ambient [Directionality] is used instead. - /// - /// Some icons follow the reading direction. For example, "back" buttons point - /// left in left-to-right environments and right in right-to-left - /// environments. Such icons have their [SvgIconData.matchTextDirection] field - /// set to true, and the [SvgIcon] widget uses the [textDirection] to - /// determine the orientation in which to draw the icon. - /// - /// This property has no effect if the [icon]'s - /// [SvgIconData.matchTextDirection] field is false, but for consistency a - /// text direction value must always be specified, either directly using this - /// property or using [Directionality]. - final TextDirection? textDirection; - - /// Whether to scale the size of this widget using the ambient [MediaQuery]'s - /// [TextScaler]. - /// - /// This is specially useful when you have an icon associated with a text, as - /// scaling the text without scaling the icon would result in a confusing - /// interface. - /// - /// Defaults to the nearest [IconTheme]'s - /// [IconThemeData.applyTextScaling]. - final bool? applyTextScaling; - - /// Semantic label for the icon. - /// - /// Announced by assistive technologies (e.g TalkBack/VoiceOver). - /// This label does not show in the UI. - /// - /// * [SemanticsProperties.label], which is set to [semanticLabel] in the - /// underlying [Semantics] widget. - final String? semanticLabel; - - @override - Widget build(BuildContext context) { - assert( - icon == null || assetName == null, - 'Cannot provide both an icon and an assetName', - ); - - const iconPackage = 'stream_chat_flutter'; - final iconData = switch (icon) { - final icon? => icon, - null => switch (assetName) { - final name? => SvgIconData( - 'lib/svgs/$name', - package: iconPackage, - ), - _ => null, - }, - }; - - return SvgIcon( - iconData, - size: size, - color: color, - textDirection: textDirection, - applyTextScaling: applyTextScaling, - semanticLabel: semanticLabel, - ); - } -} - -/// Alternative of [StreamSvgIcon] which follows the [IconTheme]. -@Deprecated("Use regular 'StreamSvgIcon' instead") -class StreamIconThemeSvgIcon extends StatelessWidget { - /// Creates a [StreamIconThemeSvgIcon]. - @Deprecated("Use regular 'StreamSvgIcon' instead") - const StreamIconThemeSvgIcon({ - super.key, - this.assetName, - this.width, - this.height, - this.color, - }); - - /// Factory constructor to create [StreamIconThemeSvgIcon] - /// from [StreamSvgIcon]. - @Deprecated("Use regular 'StreamSvgIcon' instead") - factory StreamIconThemeSvgIcon.fromSvgIcon( - StreamSvgIcon streamSvgIcon, - ) { - return StreamIconThemeSvgIcon( - assetName: streamSvgIcon.assetName, - width: streamSvgIcon.width, - height: streamSvgIcon.height, - color: streamSvgIcon.color, - ); - } - - /// Name of icon asset - final String? assetName; - - /// Width of icon - final double? width; - - /// Height of icon - final double? height; - - /// Color of icon - final Color? color; - - @override - Widget build(BuildContext context) { - final iconTheme = IconTheme.of(context); - final color = this.color ?? iconTheme.color; - final width = this.width ?? iconTheme.size; - final height = this.height ?? iconTheme.size; - - return StreamSvgIcon( - assetName: assetName, - width: width, - height: height, - color: color, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/icons/stream_svg_icon.g.dart b/packages/stream_chat_flutter/lib/src/icons/stream_svg_icon.g.dart deleted file mode 100644 index c6a7fc2001..0000000000 --- a/packages/stream_chat_flutter/lib/src/icons/stream_svg_icon.g.dart +++ /dev/null @@ -1,900 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND -// To regenerate, run: tools/stream_svg_icons_generator.dart - -part of 'stream_svg_icon.dart'; - -// ************************************************************************** -// StreamSvgIconsGenerator -// ************************************************************************** - -/// Identifiers for the supported Stream SVG icons. -/// -/// Use with the [StreamSvgIcon] class to show specific icons. Icons are -/// identified by their name as listed below, e.g. [StreamSvgIcons.settings]. -/// -/// {@tool snippet} -/// This example shows how to create a [Row] of [StreamSvgIcon]s in different -/// colors and sizes. The first [StreamSvgIcon] uses a -/// [StreamSvgIcon.semanticLabel] to announce in accessibility modes like -/// TalkBack and VoiceOver. -/// -/// The following code snippet would generate a row of icons consisting of a -/// pink heart, a green musical note, and a blue umbrella, each progressively -/// bigger than the last. -/// -/// ```dart -/// const Row( -/// mainAxisAlignment: MainAxisAlignment.spaceAround, -/// children: [ -/// StreamSvgIcon( -/// icon: StreamSvgIcons.settings, -/// color: Colors.pink, -/// size: 24.0, -/// semanticLabel: 'Text to announce in accessibility modes', -/// ), -/// StreamSvgIcon( -/// icon: StreamSvgIcons.lock, -/// color: Colors.green, -/// size: 30.0, -/// ), -/// StreamSvgIcon( -/// icon: StreamSvgIcons.mic, -/// color: Colors.blue, -/// size: 36.0, -/// ), -/// ], -/// ) -/// ``` -/// {@end-tool} -/// -/// See also: -/// -/// * [StreamSvgIcon] -/// * [IconButton] -abstract final class StreamSvgIcons { - /// The package that contains the Stream SVG icons. - static const String package = 'stream_chat_flutter'; - - /// Stream SVG icon named 'eye'. - static const StreamSvgIconData eye = StreamSvgIconData( - 'lib/assets/icons/icon_eye.svg', - package: package, - ); - - /// Stream SVG icon named 'checkAll'. - static const StreamSvgIconData checkAll = StreamSvgIconData( - 'lib/assets/icons/icon_check_all.svg', - package: package, - ); - - /// Stream SVG icon named 'menuPoint'. - static const StreamSvgIconData menuPoint = StreamSvgIconData( - 'lib/assets/icons/icon_menu_point.svg', - package: package, - ); - - /// Stream SVG icon named 'pause'. - static const StreamSvgIconData pause = StreamSvgIconData( - 'lib/assets/icons/icon_pause.svg', - package: package, - ); - - /// Stream SVG icon named 'error'. - static const StreamSvgIconData error = StreamSvgIconData( - 'lib/assets/icons/icon_error.svg', - package: package, - ); - - /// Stream SVG icon named 'userAdd'. - static const StreamSvgIconData userAdd = StreamSvgIconData( - 'lib/assets/icons/icon_user_add.svg', - package: package, - ); - - /// Stream SVG icon named 'penWrite'. - static const StreamSvgIconData penWrite = StreamSvgIconData( - 'lib/assets/icons/icon_pen_write.svg', - package: package, - ); - - /// Stream SVG icon named 'share'. - static const StreamSvgIconData share = StreamSvgIconData( - 'lib/assets/icons/icon_share.svg', - package: package, - ); - - /// Stream SVG icon named 'record'. - static const StreamSvgIconData record = StreamSvgIconData( - 'lib/assets/icons/icon_record.svg', - package: package, - ); - - /// Stream SVG icon named 'message'. - static const StreamSvgIconData message = StreamSvgIconData( - 'lib/assets/icons/icon_message.svg', - package: package, - ); - - /// Stream SVG icon named 'moon'. - static const StreamSvgIconData moon = StreamSvgIconData( - 'lib/assets/icons/icon_moon.svg', - package: package, - ); - - /// Stream SVG icon named 'left'. - static const StreamSvgIconData left = StreamSvgIconData( - 'lib/assets/icons/icon_left.svg', - package: package, - ); - - /// Stream SVG icon named 'save'. - static const StreamSvgIconData save = StreamSvgIconData( - 'lib/assets/icons/icon_save.svg', - package: package, - ); - - /// Stream SVG icon named 'cloudDownload'. - static const StreamSvgIconData cloudDownload = StreamSvgIconData( - 'lib/assets/icons/icon_cloud_download.svg', - package: package, - ); - - /// Stream SVG icon named 'settings'. - static const StreamSvgIconData settings = StreamSvgIconData( - 'lib/assets/icons/icon_settings.svg', - package: package, - ); - - /// Stream SVG icon named 'mentions'. - static const StreamSvgIconData mentions = StreamSvgIconData( - 'lib/assets/icons/icon_mentions.svg', - package: package, - ); - - /// Stream SVG icon named 'thumbsDownReaction'. - static const StreamSvgIconData thumbsDownReaction = StreamSvgIconData( - 'lib/assets/icons/icon_thumbs_down_reaction.svg', - package: package, - ); - - /// Stream SVG icon named 'circleUp'. - static const StreamSvgIconData circleUp = StreamSvgIconData( - 'lib/assets/icons/icon_circle_up.svg', - package: package, - ); - - /// Stream SVG icon named 'copy'. - static const StreamSvgIconData copy = StreamSvgIconData( - 'lib/assets/icons/icon_copy.svg', - package: package, - ); - - /// Stream SVG icon named 'mic'. - static const StreamSvgIconData mic = StreamSvgIconData( - 'lib/assets/icons/icon_mic.svg', - package: package, - ); - - /// Stream SVG icon named 'download'. - static const StreamSvgIconData download = StreamSvgIconData( - 'lib/assets/icons/icon_download.svg', - package: package, - ); - - /// Stream SVG icon named 'send'. - static const StreamSvgIconData send = StreamSvgIconData( - 'lib/assets/icons/icon_send.svg', - package: package, - ); - - /// Stream SVG icon named 'messageUnread'. - static const StreamSvgIconData messageUnread = StreamSvgIconData( - 'lib/assets/icons/icon_message_unread.svg', - package: package, - ); - - /// Stream SVG icon named 'search'. - static const StreamSvgIconData search = StreamSvgIconData( - 'lib/assets/icons/icon_search.svg', - package: package, - ); - - /// Stream SVG icon named 'play'. - static const StreamSvgIconData play = StreamSvgIconData( - 'lib/assets/icons/icon_play.svg', - package: package, - ); - - /// Stream SVG icon named 'loveReaction'. - static const StreamSvgIconData loveReaction = StreamSvgIconData( - 'lib/assets/icons/icon_love_reaction.svg', - package: package, - ); - - /// Stream SVG icon named 'userSettings'. - static const StreamSvgIconData userSettings = StreamSvgIconData( - 'lib/assets/icons/icon_user_settings.svg', - package: package, - ); - - /// Stream SVG icon named 'shareArrow'. - static const StreamSvgIconData shareArrow = StreamSvgIconData( - 'lib/assets/icons/icon_share_arrow.svg', - package: package, - ); - - /// Stream SVG icon named 'polls'. - static const StreamSvgIconData polls = StreamSvgIconData( - 'lib/assets/icons/icon_polls.svg', - package: package, - ); - - /// Stream SVG icon named 'smile'. - static const StreamSvgIconData smile = StreamSvgIconData( - 'lib/assets/icons/icon_smile.svg', - package: package, - ); - - /// Stream SVG icon named 'right'. - static const StreamSvgIconData right = StreamSvgIconData( - 'lib/assets/icons/icon_right.svg', - package: package, - ); - - /// Stream SVG icon named 'videoCall'. - static const StreamSvgIconData videoCall = StreamSvgIconData( - 'lib/assets/icons/icon_video_call.svg', - package: package, - ); - - /// Stream SVG icon named 'close'. - static const StreamSvgIconData close = StreamSvgIconData( - 'lib/assets/icons/icon_close.svg', - package: package, - ); - - /// Stream SVG icon named 'volumeUp'. - static const StreamSvgIconData volumeUp = StreamSvgIconData( - 'lib/assets/icons/icon_volume_up.svg', - package: package, - ); - - /// Stream SVG icon named 'mute'. - static const StreamSvgIconData mute = StreamSvgIconData( - 'lib/assets/icons/icon_mute.svg', - package: package, - ); - - /// Stream SVG icon named 'lightning'. - static const StreamSvgIconData lightning = StreamSvgIconData( - 'lib/assets/icons/icon_lightning.svg', - package: package, - ); - - /// Stream SVG icon named 'threadReply'. - static const StreamSvgIconData threadReply = StreamSvgIconData( - 'lib/assets/icons/icon_thread_reply.svg', - package: package, - ); - - /// Stream SVG icon named 'sendMessage'. - static const StreamSvgIconData sendMessage = StreamSvgIconData( - 'lib/assets/icons/icon_send_message.svg', - package: package, - ); - - /// Stream SVG icon named 'edit'. - static const StreamSvgIconData edit = StreamSvgIconData( - 'lib/assets/icons/icon_edit.svg', - package: package, - ); - - /// Stream SVG icon named 'award'. - static const StreamSvgIconData award = StreamSvgIconData( - 'lib/assets/icons/icon_award.svg', - package: package, - ); - - /// Stream SVG icon named 'closeSmall'. - static const StreamSvgIconData closeSmall = StreamSvgIconData( - 'lib/assets/icons/icon_close_small.svg', - package: package, - ); - - /// Stream SVG icon named 'link'. - static const StreamSvgIconData link = StreamSvgIconData( - 'lib/assets/icons/icon_link.svg', - package: package, - ); - - /// Stream SVG icon named 'userDelete'. - static const StreamSvgIconData userDelete = StreamSvgIconData( - 'lib/assets/icons/icon_user_delete.svg', - package: package, - ); - - /// Stream SVG icon named 'attach'. - static const StreamSvgIconData attach = StreamSvgIconData( - 'lib/assets/icons/icon_attach.svg', - package: package, - ); - - /// Stream SVG icon named 'notification'. - static const StreamSvgIconData notification = StreamSvgIconData( - 'lib/assets/icons/icon_notification.svg', - package: package, - ); - - /// Stream SVG icon named 'lock'. - static const StreamSvgIconData lock = StreamSvgIconData( - 'lib/assets/icons/icon_lock.svg', - package: package, - ); - - /// Stream SVG icon named 'arrowRight'. - static const StreamSvgIconData arrowRight = StreamSvgIconData( - 'lib/assets/icons/icon_arrow_right.svg', - package: package, - ); - - /// Stream SVG icon named 'pin'. - static const StreamSvgIconData pin = StreamSvgIconData( - 'lib/assets/icons/icon_pin.svg', - package: package, - ); - - /// Stream SVG icon named 'time'. - static const StreamSvgIconData time = StreamSvgIconData( - 'lib/assets/icons/icon_time.svg', - package: package, - ); - - /// Stream SVG icon named 'emptyCircleRight'. - static const StreamSvgIconData emptyCircleRight = StreamSvgIconData( - 'lib/assets/icons/icon_empty_circle_right.svg', - package: package, - ); - - /// Stream SVG icon named 'userRemove'. - static const StreamSvgIconData userRemove = StreamSvgIconData( - 'lib/assets/icons/icon_user_remove.svg', - package: package, - ); - - /// Stream SVG icon named 'delete'. - static const StreamSvgIconData delete = StreamSvgIconData( - 'lib/assets/icons/icon_delete.svg', - package: package, - ); - - /// Stream SVG icon named 'check'. - static const StreamSvgIconData check = StreamSvgIconData( - 'lib/assets/icons/icon_check.svg', - package: package, - ); - - /// Stream SVG icon named 'stop'. - static const StreamSvgIconData stop = StreamSvgIconData( - 'lib/assets/icons/icon_stop.svg', - package: package, - ); - - /// Stream SVG icon named 'thumbsUpReaction'. - static const StreamSvgIconData thumbsUpReaction = StreamSvgIconData( - 'lib/assets/icons/icon_thumbs_up_reaction.svg', - package: package, - ); - - /// Stream SVG icon named 'flag'. - static const StreamSvgIconData flag = StreamSvgIconData( - 'lib/assets/icons/icon_flag.svg', - package: package, - ); - - /// Stream SVG icon named 'lolReaction'. - static const StreamSvgIconData lolReaction = StreamSvgIconData( - 'lib/assets/icons/icon_lol_reaction.svg', - package: package, - ); - - /// Stream SVG icon named 'up'. - static const StreamSvgIconData up = StreamSvgIconData( - 'lib/assets/icons/icon_up.svg', - package: package, - ); - - /// Stream SVG icon named 'retry'. - static const StreamSvgIconData retry = StreamSvgIconData( - 'lib/assets/icons/icon_retry.svg', - package: package, - ); - - /// Stream SVG icon named 'group'. - static const StreamSvgIconData group = StreamSvgIconData( - 'lib/assets/icons/icon_group.svg', - package: package, - ); - - /// Stream SVG icon named 'contacts'. - static const StreamSvgIconData contacts = StreamSvgIconData( - 'lib/assets/icons/icon_contacts.svg', - package: package, - ); - - /// Stream SVG icon named 'pictures'. - static const StreamSvgIconData pictures = StreamSvgIconData( - 'lib/assets/icons/icon_pictures.svg', - package: package, - ); - - /// Stream SVG icon named 'checkSend'. - static const StreamSvgIconData checkSend = StreamSvgIconData( - 'lib/assets/icons/icon_check_send.svg', - package: package, - ); - - /// Stream SVG icon named 'camera'. - static const StreamSvgIconData camera = StreamSvgIconData( - 'lib/assets/icons/icon_camera.svg', - package: package, - ); - - /// Stream SVG icon named 'wutReaction'. - static const StreamSvgIconData wutReaction = StreamSvgIconData( - 'lib/assets/icons/icon_wut_reaction.svg', - package: package, - ); - - /// Stream SVG icon named 'grid'. - static const StreamSvgIconData grid = StreamSvgIconData( - 'lib/assets/icons/icon_grid.svg', - package: package, - ); - - /// Stream SVG icon named 'user'. - static const StreamSvgIconData user = StreamSvgIconData( - 'lib/assets/icons/icon_user.svg', - package: package, - ); - - /// Stream SVG icon named 'files'. - static const StreamSvgIconData files = StreamSvgIconData( - 'lib/assets/icons/icon_files.svg', - package: package, - ); - - /// Stream SVG icon named 'reload'. - static const StreamSvgIconData reload = StreamSvgIconData( - 'lib/assets/icons/icon_reload.svg', - package: package, - ); - - /// Stream SVG icon named 'down'. - static const StreamSvgIconData down = StreamSvgIconData( - 'lib/assets/icons/icon_down.svg', - package: package, - ); - - /// Stream SVG icon named 'reply'. - static const StreamSvgIconData reply = StreamSvgIconData( - 'lib/assets/icons/icon_reply.svg', - package: package, - ); - - /// Stream SVG icon named 'filetypePresentationPps'. - static const StreamSvgIconData filetypePresentationPps = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_presentation_pps.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeCompressionRar'. - static const StreamSvgIconData filetypeCompressionRar = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_compression_rar.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeSpreadsheetXlsx'. - static const StreamSvgIconData filetypeSpreadsheetXlsx = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_spreadsheet_xlsx.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypePresentationStandard'. - static const StreamSvgIconData filetypePresentationStandard = - StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_presentation_standard.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypePresentationSpecial'. - static const StreamSvgIconData filetypePresentationSpecial = - StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_presentation_special.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeSpreadsheetSpecial'. - static const StreamSvgIconData filetypeSpreadsheetSpecial = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_spreadsheet_special.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeAudioSpecial'. - static const StreamSvgIconData filetypeAudioSpecial = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_audio_special.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeCodeTar'. - static const StreamSvgIconData filetypeCodeTar = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_code_tar.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypePresentationOdp'. - static const StreamSvgIconData filetypePresentationOdp = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_presentation_odp.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeAudioMp3'. - static const StreamSvgIconData filetypeAudioMp3 = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_audio_mp3.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeCompressionDeb'. - static const StreamSvgIconData filetypeCompressionDeb = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_compression_deb.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeSpreadsheetXlsm'. - static const StreamSvgIconData filetypeSpreadsheetXlsm = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_spreadsheet_xlsm.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'imgur'. - static const StreamSvgIconData imgur = StreamSvgIconData( - 'lib/assets/icons/colored/icon_imgur.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeTextOdt'. - static const StreamSvgIconData filetypeTextOdt = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_text_odt.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypePresentationPpt'. - static const StreamSvgIconData filetypePresentationPpt = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_presentation_ppt.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeCodeStandard'. - static const StreamSvgIconData filetypeCodeStandard = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_code_standard.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeAudioFlac'. - static const StreamSvgIconData filetypeAudioFlac = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_audio_flac.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeTextSpecial'. - static const StreamSvgIconData filetypeTextSpecial = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_text_special.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeSpreadsheetOds'. - static const StreamSvgIconData filetypeSpreadsheetOds = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_spreadsheet_ods.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeCodeXml'. - static const StreamSvgIconData filetypeCodeXml = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_code_xml.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeCompressionPkg'. - static const StreamSvgIconData filetypeCompressionPkg = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_compression_pkg.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeSpreadsheetXls'. - static const StreamSvgIconData filetypeSpreadsheetXls = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_spreadsheet_xls.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeTextWdp'. - static const StreamSvgIconData filetypeTextWdp = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_text_wdp.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeCompressionZip'. - static const StreamSvgIconData filetypeCompressionZip = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_compression_zip.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeSpreadsheetStandard'. - static const StreamSvgIconData filetypeSpreadsheetStandard = - StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_spreadsheet_standard.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeAudioAac'. - static const StreamSvgIconData filetypeAudioAac = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_audio_aac.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeOtherPdf'. - static const StreamSvgIconData filetypeOtherPdf = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_other_pdf.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeCodeSav'. - static const StreamSvgIconData filetypeCodeSav = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_code_sav.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeCompressionRpm'. - static const StreamSvgIconData filetypeCompressionRpm = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_compression_rpm.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'giphy'. - static const StreamSvgIconData giphy = StreamSvgIconData( - 'lib/assets/icons/colored/icon_giphy.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeOtherSpecial'. - static const StreamSvgIconData filetypeOtherSpecial = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_other_special.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeAudioM4a'. - static const StreamSvgIconData filetypeAudioM4a = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_audio_m4a.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeAudioM4b'. - static const StreamSvgIconData filetypeAudioM4b = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_audio_m4b.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeCompressionArj'. - static const StreamSvgIconData filetypeCompressionArj = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_compression_arj.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeTextStandard'. - static const StreamSvgIconData filetypeTextStandard = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_text_standard.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeCodeMd'. - static const StreamSvgIconData filetypeCodeMd = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_code_md.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeTextTxt'. - static const StreamSvgIconData filetypeTextTxt = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_text_txt.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeTextRtf'. - static const StreamSvgIconData filetypeTextRtf = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_text_rtf.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeCompression7z'. - static const StreamSvgIconData filetypeCompression7z = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_compression_7z.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeAudioStandard'. - static const StreamSvgIconData filetypeAudioStandard = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_audio_standard.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeOtherStandard'. - static const StreamSvgIconData filetypeOtherStandard = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_other_standard.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeCodeCsv'. - static const StreamSvgIconData filetypeCodeCsv = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_code_csv.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeCodeHtml'. - static const StreamSvgIconData filetypeCodeHtml = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_code_html.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeCodeDat'. - static const StreamSvgIconData filetypeCodeDat = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_code_dat.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypePresentationPptx'. - static const StreamSvgIconData filetypePresentationPptx = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_presentation_pptx.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeOtherWkq'. - static const StreamSvgIconData filetypeOtherWkq = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_other_wkq.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeCompressionZ'. - static const StreamSvgIconData filetypeCompressionZ = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_compression_z.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeAudioAiff'. - static const StreamSvgIconData filetypeAudioAiff = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_audio_aiff.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeCodeSpecial'. - static const StreamSvgIconData filetypeCodeSpecial = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_code_special.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeCompressionStandard'. - static const StreamSvgIconData filetypeCompressionStandard = - StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_compression_standard.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeTextDoc'. - static const StreamSvgIconData filetypeTextDoc = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_text_doc.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeAudioOgg'. - static const StreamSvgIconData filetypeAudioOgg = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_audio_ogg.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeCompressionSpecial'. - static const StreamSvgIconData filetypeCompressionSpecial = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_compression_special.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypePresentationKey'. - static const StreamSvgIconData filetypePresentationKey = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_presentation_key.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeAudioWav'. - static const StreamSvgIconData filetypeAudioWav = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_audio_wav.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeTextTex'. - static const StreamSvgIconData filetypeTextTex = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_text_tex.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeCodeDb'. - static const StreamSvgIconData filetypeCodeDb = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_code_db.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeTextDocx'. - static const StreamSvgIconData filetypeTextDocx = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_text_docx.svg', - package: package, - preserveColors: true, - ); - - /// Stream SVG icon named 'filetypeAudioAlac'. - static const StreamSvgIconData filetypeAudioAlac = StreamSvgIconData( - 'lib/assets/icons/colored/icon_filetype_audio_alac.svg', - package: package, - preserveColors: true, - ); -} diff --git a/packages/stream_chat_flutter/lib/src/indicators/loading_indicator.dart b/packages/stream_chat_flutter/lib/src/indicators/loading_indicator.dart deleted file mode 100644 index cc42af630d..0000000000 --- a/packages/stream_chat_flutter/lib/src/indicators/loading_indicator.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -/// {@template streamProgressIndicator} -/// A simple progress indicator that can be used in place of the default -/// [CircularProgressIndicator] in the Stream Chat widgets. -/// {@endtemplate} -class StreamLoadingIndicator extends StatelessWidget { - /// {@macro streamProgressIndicator} - const StreamLoadingIndicator({super.key}); - - @override - Widget build(BuildContext context) { - final color = StreamChatTheme.of(context).colorTheme.accentPrimary; - return CircularProgressIndicator.adaptive( - strokeWidth: 2, - backgroundColor: color, - valueColor: AlwaysStoppedAnimation(color), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/indicators/sending_indicator.dart b/packages/stream_chat_flutter/lib/src/indicators/sending_indicator.dart deleted file mode 100644 index ebf57c3afa..0000000000 --- a/packages/stream_chat_flutter/lib/src/indicators/sending_indicator.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamSendingIndicator} -/// Shows the sending status of a message. -/// {@endtemplate} -class StreamSendingIndicator extends StatelessWidget { - /// {@macro streamSendingIndicator} - const StreamSendingIndicator({ - super.key, - required this.message, - this.isMessageRead = false, - this.size = 12, - }); - - /// Message for sending indicator - final Message message; - - /// Flag if message is read - final bool isMessageRead; - - /// Size for message - final double? size; - - @override - Widget build(BuildContext context) { - if (isMessageRead) { - return StreamSvgIcon( - size: size, - icon: StreamSvgIcons.checkAll, - color: StreamChatTheme.of(context).colorTheme.accentPrimary, - ); - } - if (message.state.isCompleted) { - return StreamSvgIcon( - size: size, - icon: StreamSvgIcons.check, - color: StreamChatTheme.of(context).colorTheme.textLowEmphasis, - ); - } - if (message.state.isOutgoing) { - return StreamSvgIcon( - size: size, - icon: StreamSvgIcons.time, - color: StreamChatTheme.of(context).colorTheme.textLowEmphasis, - ); - } - return const SizedBox(); - } -} diff --git a/packages/stream_chat_flutter/lib/src/indicators/typing_indicator.dart b/packages/stream_chat_flutter/lib/src/indicators/typing_indicator.dart deleted file mode 100644 index c0253c2852..0000000000 --- a/packages/stream_chat_flutter/lib/src/indicators/typing_indicator.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lottie/lottie.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamTypingIndicator} -/// Shows the list of user who are actively typing. -/// {@endtemplate} -class StreamTypingIndicator extends StatelessWidget { - /// {@macro streamTypingIndicator} - const StreamTypingIndicator({ - super.key, - this.channel, - this.alternativeWidget, - this.style, - this.padding = EdgeInsets.zero, - this.parentId, - }); - - /// Style of the text widget - final TextStyle? style; - - /// List of typing users - final Channel? channel; - - /// The widget to build when no typing is happening - final Widget? alternativeWidget; - - /// The padding of this widget - final EdgeInsets padding; - - /// Id of the parent message in case of a thread - final String? parentId; - - @override - Widget build(BuildContext context) { - final channelState = - channel?.state ?? StreamChannel.of(context).channel.state!; - - final altWidget = alternativeWidget ?? const Offstage(); - - return BetterStreamBuilder>( - initialData: channelState.typingEvents.keys, - stream: channelState.typingEventsStream.map((typingEvents) => typingEvents - .entries - .where((element) => element.value.parentId == parentId) - .map((e) => e.key)), - builder: (context, users) => AnimatedSwitcher( - layoutBuilder: (currentChild, previousChildren) => Stack( - children: [ - ...previousChildren, - if (currentChild != null) currentChild, - ], - ), - duration: const Duration(milliseconds: 300), - child: users.isNotEmpty - ? Padding( - padding: padding, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Lottie.asset( - 'lib/assets/animations/typing_dots.json', - package: 'stream_chat_flutter', - height: 4, - ), - Flexible( - child: Text( - context.translations.userTypingText(users), - maxLines: 1, - style: style, - ), - ), - ], - ), - ) - : altWidget, - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/indicators/unread_indicator.dart b/packages/stream_chat_flutter/lib/src/indicators/unread_indicator.dart deleted file mode 100644 index 167fa0a810..0000000000 --- a/packages/stream_chat_flutter/lib/src/indicators/unread_indicator.dart +++ /dev/null @@ -1,108 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamUnreadIndicator} -/// Shows different unread counts of the user. -/// {@endtemplate} -class StreamUnreadIndicator extends StatelessWidget { - /// Displays the total unread count. - StreamUnreadIndicator({ - super.key, - @Deprecated('Use StreamUnreadIndicator.channels instead') String? cid, - }) : _unreadType = switch (cid) { - final cid? => _UnreadChannels(cid: cid), - _ => const _TotalUnreadCount(), - }; - - /// Displays the unreadChannel count. - /// - /// Optionally, provide a [cid] to filter count for a specific channel. - StreamUnreadIndicator.channels({ - super.key, - String? cid, - }) : _unreadType = _UnreadChannels(cid: cid); - - /// Displays the unreadThreads count. - /// - /// Optionally, provide a [id] to filter count for a specific thread. - StreamUnreadIndicator.threads({ - super.key, - String? id, - }) : _unreadType = _UnreadThreads(id: id); - - final _UnreadTypes _unreadType; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - final client = StreamChat.of(context).client; - - final stream = switch (_unreadType) { - _TotalUnreadCount() => client.state.totalUnreadCountStream, - _UnreadChannels(cid: final cid) => switch (cid) { - final cid? => client.state.channels[cid]?.state?.unreadCountStream, - _ => client.state.unreadChannelsStream, - }, - _UnreadThreads(id: final id) => switch (id) { - // TODO: Handle id once it's supported - _ => client.state.unreadThreadsStream, - } - }; - - final initialData = switch (_unreadType) { - _TotalUnreadCount() => client.state.totalUnreadCount, - _UnreadChannels(cid: final cid) => switch (cid) { - final cid? => client.state.channels[cid]?.state?.unreadCount, - _ => client.state.unreadChannels, - }, - _UnreadThreads(id: final id) => switch (id) { - // TODO: Handle id once it's supported - _ => client.state.unreadThreads, - } - }; - - return IgnorePointer( - child: BetterStreamBuilder( - stream: stream, - initialData: initialData, - builder: (context, unreadCount) { - if (unreadCount == 0) return const SizedBox.shrink(); - - return Badge( - textColor: Colors.white, - textStyle: theme.textTheme.footnoteBold, - backgroundColor: theme.channelPreviewTheme.unreadCounterColor, - label: Text( - switch (unreadCount) { - > 99 => '99+', - _ => '$unreadCount', - }, - ), - ); - }, - ), - ); - } -} - -sealed class _UnreadTypes { - const _UnreadTypes._(); -} - -final class _TotalUnreadCount extends _UnreadTypes { - const _TotalUnreadCount() : super._(); -} - -final class _UnreadChannels extends _UnreadTypes { - const _UnreadChannels({this.cid}) : super._(); - - /// Optional channel cid to filter unread count. - final String? cid; -} - -final class _UnreadThreads extends _UnreadTypes { - const _UnreadThreads({this.id}) : super._(); - - /// Optional parent message id to filter unread count. - final String? id; -} diff --git a/packages/stream_chat_flutter/lib/src/indicators/upload_progress_indicator.dart b/packages/stream_chat_flutter/lib/src/indicators/upload_progress_indicator.dart deleted file mode 100644 index 7a6b289814..0000000000 --- a/packages/stream_chat_flutter/lib/src/indicators/upload_progress_indicator.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamUploadProgressIndicator} -/// Shows the upload progress of an attachment. -/// {@endtemplate} -class StreamUploadProgressIndicator extends StatelessWidget { - /// {@macro streamUploadProgressIndicator} - const StreamUploadProgressIndicator({ - super.key, - required this.uploaded, - required this.total, - this.progressIndicatorColor = const Color(0xffb2b2b2), - this.padding = const EdgeInsets.only( - top: 5, - bottom: 5, - right: 11, - left: 5, - ), - this.showBackground = true, - this.textStyle, - }); - - /// Bytes uploaded - final int uploaded; - - /// Total bytes - final int total; - - /// Color of progress indicator - final Color progressIndicatorColor; - - /// Padding for widget - final EdgeInsetsGeometry padding; - - /// Flag for showing background - final bool showBackground; - - /// [TextStyle] to be applied to text - final TextStyle? textStyle; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - final _percentage = (uploaded / total) * 100; - Widget child = Padding( - padding: padding, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - height: 16, - width: 16, - child: CircularProgressIndicator.adaptive( - strokeWidth: 3, - valueColor: AlwaysStoppedAnimation(progressIndicatorColor), - ), - ), - const SizedBox(width: 8), - Text( - '${_percentage.toInt()}%', - style: textStyle ?? - theme.textTheme.footnote.copyWith( - color: theme.colorTheme.barsBg, - ), - ), - ], - ), - ); - if (showBackground) { - child = DecoratedBox( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - // ignore: deprecated_member_use - color: theme.colorTheme.overlayDark.withOpacity(0.6), - ), - child: child, - ); - } - return child; - } -} diff --git a/packages/stream_chat_flutter/lib/src/keyboard_shortcuts/intents.dart b/packages/stream_chat_flutter/lib/src/keyboard_shortcuts/intents.dart deleted file mode 100644 index 3a5f9713de..0000000000 --- a/packages/stream_chat_flutter/lib/src/keyboard_shortcuts/intents.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:flutter/widgets.dart'; - -/// The intent for pressing the "enter" key. -class EnterKeyIntent extends Intent {} - -/// The intent for pressing the "escape" key. -class EscapeKeyIntent extends Intent {} - -/// The intent for pressing the "right" arrow key. -class RightArrowKeyIntent extends Intent {} - -/// The intent for pressing the "left" arrow key. -class LeftArrowKeyIntent extends Intent {} diff --git a/packages/stream_chat_flutter/lib/src/keyboard_shortcuts/keyboard_shortcut_runner.dart b/packages/stream_chat_flutter/lib/src/keyboard_shortcuts/keyboard_shortcut_runner.dart deleted file mode 100644 index 706adad454..0000000000 --- a/packages/stream_chat_flutter/lib/src/keyboard_shortcuts/keyboard_shortcut_runner.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/keyboard_shortcuts/intents.dart'; -import 'package:stream_chat_flutter/src/keyboard_shortcuts/keysets.dart'; - -/// A widget that executes functions when specific physical keyboard shortcuts -/// are performed. -class KeyboardShortcutRunner extends StatelessWidget { - /// Builds a [KeyboardShortcutRunner]. - const KeyboardShortcutRunner({ - super.key, - required this.child, - this.onEnterKeypress, - this.onEscapeKeypress, - this.onRightArrowKeypress, - this.onLeftArrowKeypress, - }); - - /// This child of this widget. - final Widget child; - - /// The function to execute when the "enter" key is pressed. - final VoidCallback? onEnterKeypress; - - /// The function to execute when the "escape" key is pressed. - final VoidCallback? onEscapeKeypress; - - /// The function to execute when the "right arrow" key is pressed. - final VoidCallback? onRightArrowKeypress; - - /// The function to execute when the "left arrow" key is pressed. - final VoidCallback? onLeftArrowKeypress; - - @override - Widget build(BuildContext context) { - return FocusableActionDetector( - autofocus: true, - shortcuts: { - if (onEnterKeypress != null) enterKeySet: EnterKeyIntent(), - if (onEscapeKeypress != null) escapeKeySet: EscapeKeyIntent(), - if (onRightArrowKeypress != null) - rightArrowKeySet: RightArrowKeyIntent(), - if (onLeftArrowKeypress != null) leftArrowKeySet: LeftArrowKeyIntent(), - }, - actions: { - EnterKeyIntent: CallbackAction( - onInvoke: (e) => onEnterKeypress?.call(), - ), - EscapeKeyIntent: CallbackAction( - onInvoke: (e) => onEscapeKeypress?.call(), - ), - RightArrowKeyIntent: CallbackAction( - onInvoke: (e) => onRightArrowKeypress?.call(), - ), - LeftArrowKeyIntent: CallbackAction( - onInvoke: (e) => onLeftArrowKeypress?.call(), - ), - }, - child: child, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/keyboard_shortcuts/keysets.dart b/packages/stream_chat_flutter/lib/src/keyboard_shortcuts/keysets.dart deleted file mode 100644 index 495be7d922..0000000000 --- a/packages/stream_chat_flutter/lib/src/keyboard_shortcuts/keysets.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; - -/// The "enter" keyset. -/// -/// Use to quickly send a message in [StreamMessageInput]. -final enterKeySet = LogicalKeySet( - LogicalKeyboardKey.enter, -); - -/// The "escape" keyset. -/// -/// Use for: -/// * Removing a reply from [StreamMessageInput]. -/// * Closing [FullScreenMediaDesktop]. -final escapeKeySet = LogicalKeySet( - LogicalKeyboardKey.escape, -); - -/// The "right arrow" keyset. -/// -/// Use for navigating to the next [FullScreenMediaDesktop] item. -final rightArrowKeySet = LogicalKeySet( - LogicalKeyboardKey.arrowRight, -); - -/// The "left arrow" keyset. -/// -/// Use for navigating to the previous [FullScreenMediaDesktop] item. -final leftArrowKeySet = LogicalKeySet( - LogicalKeyboardKey.arrowLeft, -); diff --git a/packages/stream_chat_flutter/lib/src/localization/stream_chat_localizations.dart b/packages/stream_chat_flutter/lib/src/localization/stream_chat_localizations.dart deleted file mode 100644 index 66c09e8847..0000000000 --- a/packages/stream_chat_flutter/lib/src/localization/stream_chat_localizations.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:stream_chat_flutter/src/localization/translations.dart' - show Translations; - -/// Defines the localized resource values used by the StreamChatFlutter widgets. -/// -/// See also: -/// -/// * [GlobalStreamChatLocalizations], which provides stream chat localizations -/// for many languages. -abstract class StreamChatLocalizations implements Translations { - /// The `StreamChatLocalizations` from the closest [Localizations] instance - /// that encloses the given context. - /// - /// If no [StreamChatLocalizations] are available in the given `context`, this - /// method returns null. - /// - /// This method is just a convenient shorthand for: - /// `Localizations.of( - /// context, - /// StreamChatLocalizations - /// )`. - /// - /// References to the localized resources defined by this class are typically - /// written in terms of this method. For example: - /// - /// ```dart - /// tooltip: StreamChatLocalizations.of(context).streamChatLabel, - /// ``` - static StreamChatLocalizations? of(BuildContext context) { - return Localizations.of( - context, - StreamChatLocalizations, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/localization/translations.dart b/packages/stream_chat_flutter/lib/src/localization/translations.dart deleted file mode 100644 index 9a396dfd87..0000000000 --- a/packages/stream_chat_flutter/lib/src/localization/translations.dart +++ /dev/null @@ -1,1115 +0,0 @@ -import 'package:jiffy/jiffy.dart'; -import 'package:stream_chat_flutter/src/message_list_view/message_list_view.dart'; -import 'package:stream_chat_flutter/src/misc/connection_status_builder.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart' - show PollVotingMode, Range, User; - -/// Translation strings for the stream chat widgets -abstract class Translations { - /// The error shown when [launchURL] fails - String get launchUrlError; - - /// The error shown when loading users fails - String get loadingUsersError; - - /// The label for "retry" button - String get retryLabel; - - /// The label for showing no users - String get noUsersLabel; - - /// The label for showing no photo or video - String get noPhotoOrVideoLabel; - - /// The text for showing user is online - String get userOnlineText; - - /// The text for showing the last online of the user - String get userLastOnlineText; - - /// The text shown when [users] starts typing - String userTypingText(Iterable users); - - /// The label for "thread reply" - String get threadReplyLabel; - - /// The text for showing if the message is only visible to you - String get onlyVisibleToYouText; - - /// The text for showing the thread reply count - String threadReplyCountText(int count); - - /// The text for showing the attachments upload progress - String attachmentsUploadProgressText({ - required int remaining, - required int total, - }); - - /// The text for showing who pinned the message - String pinnedByUserText({ - required User pinnedBy, - required User currentUser, - }); - - /// The text for showing there are empty messages - String get emptyMessagesText; - - /// The text for showing generic error - String get genericErrorText; - - /// The error shown when loading messages fails - String get loadingMessagesError; - - /// The text for showing the result count in [StreamMessageSearchListView] - String resultCountText(int count); - - /// The text for showing the message is deleted - String get messageDeletedText; - - /// The label for message deleted - String get messageDeletedLabel; - - /// The label for showing the message is edited - String get editedMessageLabel; - - /// The label for message reactions - String get messageReactionsLabel; - - /// The text for showing there are no chats - String get emptyChatMessagesText; - - /// The text for showing the thread separator in case [StreamMessageListView] - /// contains a parent message - String threadSeparatorText(int replyCount); - - /// The text for showing the unread messages count - /// in the [StreamMessageListView] - String unreadMessagesSeparatorText(); - - /// The label for "connected" in [StreamConnectionStatusBuilder] - String get connectedLabel; - - /// The label for "disconnected" in [StreamConnectionStatusBuilder] - String get disconnectedLabel; - - /// The label for "reconnecting" in [StreamConnectionStatusBuilder] - String get reconnectingLabel; - - /// The label for also send - /// as direct message "checkbox"" in [StreamMessageInput] - String get alsoSendAsDirectMessageLabel; - - /// The label for search Gif - String get searchGifLabel; - - /// The label for the MessageInput hint when permission denied on sendMessage - String get sendMessagePermissionError; - - /// The label for add a comment or send in case of - /// attachments inside [StreamMessageInput] - String get addACommentOrSendLabel; - - /// The label for write a message in [StreamMessageInput] - String get writeAMessageLabel; - - /// The label for slow mode enabled in [StreamMessageInput] - String get slowModeOnLabel; - - /// The label for instant commands in [StreamMessageInput] - String get instantCommandsLabel; - - /// The error shown in case the file is too large even after compression - /// while uploading via [StreamMessageInput] - String fileTooLargeAfterCompressionError(double limitInMB); - - /// The error shown in case the file is too large - /// while uploading via [StreamMessageInput] - String fileTooLargeError(double limitInMB); - - /// The error shown when the file being read has no bytes - String get couldNotReadBytesFromFileError; - - /// The label for "add a file" - String get addAFileLabel; - - /// The label for "upload a photo" - String get uploadAPhotoLabel; - - /// The label for "upload a video" - String get uploadAVideoLabel; - - /// The label for "photo from camera" - String get photoFromCameraLabel; - - /// The label for "video from camera" - String get videoFromCameraLabel; - - /// The label for "upload a file" - String get uploadAFileLabel; - - /// The error shown when something went wrong - String get somethingWentWrongError; - - /// The label for "OK" - String get okLabel; - - /// The label for a link disabled error - String get linkDisabledError; - - /// The additional info on a link disabled error - String get linkDisabledDetails; - - /// The label for "add more files" - String get addMoreFilesLabel; - - /// The message shown for asking photo and video access permission - String get enablePhotoAndVideoAccessMessage; - - /// The message shown for asking photo and video access permission - String get enableFileAccessMessage; - - /// The message shown for asking gallery access permission - String get allowGalleryAccessMessage; - - /// The message shown for asking file access permission - String get allowFileAccessMessage; - - /// The label for "flag message" - String get flagMessageLabel; - - /// The question asked while showing flag message dialog - String get flagMessageQuestion; - - /// The label for "Flag" - String get flagLabel; - - /// The label for "Cancel" - String get cancelLabel; - - /// The label for successful message flag - String get flagMessageSuccessfulLabel; - - /// The text for showing the message if successfully flagged - String get flagMessageSuccessfulText; - - /// The label for "delete message" - String get deleteMessageLabel; - - /// The question asked while showing delete message dialog - String get deleteMessageQuestion; - - /// The label for "Delete" - String get deleteLabel; - - /// The text for showing the operation could not be completed - String get operationCouldNotBeCompletedText; - - /// The label for "Reply" - String get replyLabel; - - /// The text for showing pin/un-pin functionality in [MessageWidget] - /// based on [pinned] - String togglePinUnpinText({required bool pinned}); - - /// The text for marking message as unread functionality in [MessageWidget] - String get markAsUnreadLabel; - - /// The text for unread count indicator - String unreadCountIndicatorLabel({required int unreadCount}); - - /// The text of an error shown when marking a message as unread fails - String get markUnreadError; - - /// The text for showing delete/retry-delete based on [isDeleteFailed] - String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed}); - - /// The label for "copy message" - String get copyMessageLabel; - - /// The label for "edit message" - String get editMessageLabel; - - /// The text for showing resend/resend-edited message - /// based on [isUpdateFailed] - String toggleResendOrResendEditedMessage({required bool isUpdateFailed}); - - /// The label for "Photos" - String get photosLabel; - - /// The text for showing on which [date] and [time] the message was sent - String sentAtText({required DateTime date, required DateTime time}); - - /// The label for "Today" - String get todayLabel; - - /// The label for "Yesterday" - String get yesterdayLabel; - - /// The text for showing the channel is muted - String get channelIsMutedText; - - /// The text for showing there is no title - String get noTitleText; - - /// The label for "let's start chatting" - String get letsStartChattingLabel; - - /// The label for sending the first message - String get sendingFirstMessageLabel; - - /// The label for "start a chat" - String get startAChatLabel; - - /// The error shown when loading channel fails - String get loadingChannelsError; - - /// The label for "Delete conversation" - String get deleteConversationLabel; - - /// The question asked while showing delete conversation dialog - String get deleteConversationQuestion; - - /// The label for "Stream Chat" - String get streamChatLabel; - - /// The text for showing searching for network - String get searchingForNetworkText; - - /// The label for "Offline" - String get offlineLabel; - - /// The label for "Try again" - String get tryAgainLabel; - - /// The text for showing the members count based on [count] - String membersCountText(int count); - - /// The text for showing the watchers count based on [count] - String watchersCountText(int count); - - /// The label for "View Info" - String get viewInfoLabel; - - /// The label for "Leave Group" - String get leaveGroupLabel; - - /// The label for "Leave" - String get leaveLabel; - - /// The label for "Leave conversation" - String get leaveConversationLabel; - - /// The question asked while showing leave conversation dialog - String get leaveConversationQuestion; - - /// The label for "Show in chat" - String get showInChatLabel; - - /// The label for "Save Image" - String get saveImageLabel; - - /// The label for "Save Video" - String get saveVideoLabel; - - /// The label for "Upload Error" - String get uploadErrorLabel; - - /// The label for "Giphy" - String get giphyLabel; - - /// The label for "Shuffle" - String get shuffleLabel; - - /// The label for "Send" - String get sendLabel; - - /// The label for "With" - String get withText; - - /// The text shown for "In" - String get inText; - - /// The text shown for "You" - String get youText; - - /// Gallery footer pagination text - String galleryPaginationText({ - required int currentPage, - required int totalPages, - }); - - /// The text shown for "File" - String get fileText; - - /// The label for "Reply to message" - String get replyToMessageLabel; - - /// The label for "View library" - String get viewLibrary; - - /// Label for "Attachment limit exceeded: - /// it's not possible to add more than $limit attachments" - String attachmentLimitExceedError(int limit); - - /// The label for "Download" - String get downloadLabel; - - /// The text for "Mute Group"/"Unmute Group" based on the value of [isMuted]. - String toggleMuteUnmuteGroupText({required bool isMuted}); - - /// The text for "Mute User"/"Unmute User" based on the value of [isMuted]. - String toggleMuteUnmuteUserText({required bool isMuted}); - - /// The text for "Are you sure you want to mute this group?"/"Are you sure you want to unmute this group?" - /// based on the value of [isMuted]. - String toggleMuteUnmuteGroupQuestion({required bool isMuted}); - - /// The text for "Are you sure you want to mute this user?"/"Are you sure you want to unmute this user?" - /// based on the value of [isMuted]. - String toggleMuteUnmuteUserQuestion({required bool isMuted}); - - /// The text for "MUTE"/"UNMUTE" based on the value of [isMuted]. - String toggleMuteUnmuteAction({required bool isMuted}); - - /// The label for "Create poll". - /// - /// If [isNew] is true, it returns "Create a new poll". - String createPollLabel({bool isNew = false}); - - /// The label for "Questions". - String get questionsLabel; - - /// The label for "Ask a question". - String get askAQuestionLabel; - - /// The error shown when the poll question [length] is not within the [range]. - /// - /// Returns 'Question must be a least ${range.min} characters long' if the - /// question is too short and 'Question must be at most ${range.max} - /// characters long' if the question is too long. - String? pollQuestionValidationError(int length, Range range); - - /// The label for "Option". - /// - /// If [isPlural] is true, it returns "Options". - String optionLabel({bool isPlural = false}); - - /// The error shown when the poll option text is empty. - String get pollOptionEmptyError; - - /// The error shown when the poll option is a duplicate. - String get pollOptionDuplicateError; - - /// The label for "Add an option". - String get addAnOptionLabel; - - /// The label for "Multiple answers". - String get multipleAnswersLabel; - - /// The label for "Maximum votes per person". - String get maximumVotesPerPersonLabel; - - /// The error shown when the max [votes] is not within the [range]. - /// - /// Returns 'Vote count must be at least ${range.min}' if the vote count is - /// too short and 'Vote count must be at most ${range.max}' if the vote count - /// is too long. - String? maxVotesPerPersonValidationError(int votes, Range range); - - /// The label for "Anonymous poll". - String get anonymousPollLabel; - - /// The label for "Poll Options". - String get pollOptionsLabel; - - /// The label for "Suggest an option". - String get suggestAnOptionLabel; - - /// The label for "Enter a new option". - String get enterANewOptionLabel; - - /// The label for "Add a comment". - String get addACommentLabel; - - /// The label for "Poll comments". - String get pollCommentsLabel; - - /// The label for "Update your comment". - String get updateYourCommentLabel; - - /// The label for "Enter your comment". - String get enterYourCommentLabel; - - /// The label for "Create". - String get createLabel; - - /// The label for Poll voting mode. - /// - /// Returns different labels based on the [votingMode]. - /// - /// eg: 'Vote ended', 'Select one', 'Select up to $count', - /// 'Select one or more'. - String pollVotingModeLabel(PollVotingMode votingMode); - - /// The label for "See all options". - /// - /// If [totalOptions] is provided, it returns "See all $count options". - String seeAllOptionsLabel({int? count}); - - /// The label for "View Comments". - String get viewCommentsLabel; - - /// The label for "View Results". - String get viewResultsLabel; - - /// The label for "End Vote". - String get endVoteLabel; - - /// The label for "Poll Results". - String get pollResultsLabel; - - /// The label for "$count votes". - String voteCountLabel({int? count}); - - /// The label for "Show all votes". - /// - /// If [count] is provided, it returns "Show all $count votes". - String showAllVotesLabel({int? count}); - - /// The label for "There are no poll votes currently". - String get noPollVotesLabel; - - /// The label for "Error loading poll votes". - String get loadingPollVotesError; - - /// The label for "replied to:" - String get repliedToLabel; - - /// The label for "$count new threads" - String newThreadsLabel({required int count}); - - /// The label for "Slide to cancel" - String get slideToCancelLabel; - - /// The label for "Hold to record" - String get holdToRecordLabel; -} - -/// Default implementation of Translation strings for the stream chat widgets -class DefaultTranslations implements Translations { - const DefaultTranslations._(); - - /// Singleton instance of [DefaultTranslations] - static const instance = DefaultTranslations._(); - - @override - String get launchUrlError => 'Cannot launch the url'; - - @override - String get loadingUsersError => 'Error loading users'; - - @override - String get noUsersLabel => 'There are no users currently'; - - @override - String get noPhotoOrVideoLabel => 'There is no photo or video'; - - @override - String get retryLabel => 'Retry'; - - @override - String get userLastOnlineText => 'Last online'; - - @override - String get userOnlineText => 'Online'; - - @override - String userTypingText(Iterable users) { - if (users.isEmpty) return ''; - final first = users.first; - if (users.length == 1) { - return '${first.name} is typing'; - } - return '${first.name} and ${users.length - 1} more are typing'; - } - - @override - String get threadReplyLabel => 'Thread Reply'; - - @override - String get onlyVisibleToYouText => 'Only visible to you'; - - @override - String threadReplyCountText(int count) => '$count Thread Replies'; - - @override - String attachmentsUploadProgressText({ - required int remaining, - required int total, - }) => - 'Uploading $remaining/$total ...'; - - @override - String pinnedByUserText({ - required User pinnedBy, - required User currentUser, - }) { - final pinnedByCurrentUser = currentUser.id == pinnedBy.id; - if (pinnedByCurrentUser) return 'Pinned by You'; - return 'Pinned by ${pinnedBy.name}'; - } - - @override - String get sendMessagePermissionError => - "You don't have permission to send messages"; - - @override - String get emptyMessagesText => 'There are no messages currently'; - - @override - String get genericErrorText => 'Something went wrong'; - - @override - String get loadingMessagesError => 'Error loading messages'; - - @override - String resultCountText(int count) => '$count results'; - - @override - String get messageDeletedText => 'This message is deleted.'; - - @override - String get messageDeletedLabel => 'Message deleted'; - - @override - String get editedMessageLabel => 'Edited'; - - @override - String get messageReactionsLabel => 'Message Reactions'; - - @override - String get emptyChatMessagesText => 'No chats here yet...'; - - @override - String threadSeparatorText(int replyCount) { - if (replyCount == 1) return '1 Reply'; - return '$replyCount Replies'; - } - - @override - String get connectedLabel => 'Connected'; - - @override - String get disconnectedLabel => 'Disconnected'; - - @override - String get reconnectingLabel => 'Reconnecting...'; - - @override - String get alsoSendAsDirectMessageLabel => 'Also send as direct message'; - - @override - String get addACommentOrSendLabel => 'Add a comment or send'; - - @override - String get searchGifLabel => 'Search GIFs'; - - @override - String get writeAMessageLabel => 'Write a message'; - - @override - String get instantCommandsLabel => 'Instant Commands'; - - @override - String fileTooLargeAfterCompressionError(double limitInMB) => - 'The file is too large to upload. ' - 'The file size limit is $limitInMB MB. ' - 'We tried compressing it, but it was not enough.'; - - @override - String fileTooLargeError(double limitInMB) => - 'The file is too large to upload. The file size limit is $limitInMB MB.'; - - @override - String get couldNotReadBytesFromFileError => - 'Could not read bytes from file.'; - - @override - String get addAFileLabel => 'Add a file'; - - @override - String get photoFromCameraLabel => 'Photo from camera'; - - @override - String get uploadAFileLabel => 'Upload a file'; - - @override - String get uploadAPhotoLabel => 'Upload a photo'; - - @override - String get uploadAVideoLabel => 'Upload a video'; - - @override - String get videoFromCameraLabel => 'Video from camera'; - - @override - String get okLabel => 'OK'; - - @override - String get somethingWentWrongError => 'Something went wrong'; - - @override - String get addMoreFilesLabel => 'Add more files'; - - @override - String get enablePhotoAndVideoAccessMessage => - 'Please enable access to your photos' - '\nand videos so you can share them with friends.'; - - @override - String get allowGalleryAccessMessage => 'Allow access to your gallery'; - - @override - String get flagMessageLabel => 'Flag Message'; - - @override - String get flagMessageQuestion => - 'Do you want to send a copy of this message to a' - '\nmoderator for further investigation?'; - - @override - String get flagLabel => 'FLAG'; - - @override - String get cancelLabel => 'CANCEL'; - - @override - String get flagMessageSuccessfulLabel => 'Message flagged'; - - @override - String get flagMessageSuccessfulText => - 'The message has been reported to a moderator.'; - - @override - String get deleteLabel => 'DELETE'; - - @override - String get deleteMessageLabel => 'Delete Message'; - - @override - String get deleteMessageQuestion => - 'Are you sure you want to permanently delete this\nmessage?'; - - @override - String get operationCouldNotBeCompletedText => - "The operation couldn't be completed."; - - @override - String get replyLabel => 'Reply'; - - @override - String togglePinUnpinText({required bool pinned}) { - if (pinned) return 'Unpin from Conversation'; - return 'Pin to Conversation'; - } - - @override - String get markAsUnreadLabel => 'Mark as Unread'; - - @override - String unreadCountIndicatorLabel({required int unreadCount}) { - return '$unreadCount unread'; - } - - @override - String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed}) { - if (isDeleteFailed) return 'Retry Deleting Message'; - return 'Delete Message'; - } - - @override - String get copyMessageLabel => 'Copy Message'; - - @override - String get editMessageLabel => 'Edit Message'; - - @override - String toggleResendOrResendEditedMessage({required bool isUpdateFailed}) { - if (isUpdateFailed) return 'Resend Edited Message'; - return 'Resend'; - } - - @override - String get photosLabel => 'Photos'; - - String _getDay(DateTime dateTime) { - final now = DateTime.now(); - final today = DateTime(now.year, now.month, now.day); - final yesterday = DateTime(now.year, now.month, now.day - 1); - - final date = DateTime(dateTime.year, dateTime.month, dateTime.day); - - if (date == today) { - return 'today'; - } else if (date == yesterday) { - return 'yesterday'; - } else { - return 'on ${Jiffy.parseFromDateTime(date).MMMd}'; - } - } - - @override - String sentAtText({required DateTime date, required DateTime time}) { - final atTime = Jiffy.parseFromDateTime(time.toLocal()); - return 'Sent ${_getDay(date)} at ${atTime.jm}'; - } - - @override - String get todayLabel => 'Today'; - - @override - String get yesterdayLabel => 'Yesterday'; - - @override - String get channelIsMutedText => 'Channel is muted'; - - @override - String get noTitleText => 'No title'; - - @override - String get letsStartChattingLabel => 'Let’s start chatting!'; - - @override - String get sendingFirstMessageLabel => - 'How about sending your first message to a friend?'; - - @override - String get startAChatLabel => 'Start a chat'; - - @override - String get loadingChannelsError => 'Error loading channels'; - - @override - String get deleteConversationLabel => 'Delete Conversation'; - - @override - String get deleteConversationQuestion => - 'Are you sure you want to delete this conversation?'; - - @override - String get streamChatLabel => 'Stream Chat'; - - @override - String get searchingForNetworkText => 'Searching for Network'; - - @override - String get offlineLabel => 'Offline...'; - - @override - String get tryAgainLabel => 'Try Again'; - - @override - String membersCountText(int count) { - if (count == 1) return '1 Member'; - return '$count Members'; - } - - @override - String watchersCountText(int count) { - if (count == 1) return '1 Online'; - return '$count Online'; - } - - @override - String get viewInfoLabel => 'View Info'; - - @override - String get leaveGroupLabel => 'Leave Group'; - - @override - String get leaveLabel => 'LEAVE'; - - @override - String get leaveConversationLabel => 'Leave conversation'; - - @override - String get leaveConversationQuestion => - 'Are you sure you want to leave this conversation?'; - - @override - String get showInChatLabel => 'Show in Chat'; - - @override - String get saveImageLabel => 'Save Image'; - - @override - String get saveVideoLabel => 'Save Video'; - - @override - String get uploadErrorLabel => 'UPLOAD ERROR'; - - @override - String get giphyLabel => 'Giphy'; - - @override - String get shuffleLabel => 'Shuffle'; - - @override - String get sendLabel => 'Send'; - - @override - String get withText => 'with'; - - @override - String get inText => 'in'; - - @override - String get youText => 'You'; - - @override - String galleryPaginationText({ - required int currentPage, - required int totalPages, - }) => - '${currentPage + 1} of $totalPages'; - - @override - String get fileText => 'File'; - - @override - String get replyToMessageLabel => 'Reply to Message'; - - @override - String get slowModeOnLabel => 'Slow mode ON'; - - @override - String get viewLibrary => 'View library'; - - @override - String attachmentLimitExceedError(int limit) => """ -Attachment limit exceeded: it's not possible to add more than $limit attachments"""; - - @override - String get downloadLabel => 'Download'; - - @override - String toggleMuteUnmuteUserText({required bool isMuted}) { - if (isMuted) { - return 'Unmute User'; - } else { - return 'Mute User'; - } - } - - @override - String toggleMuteUnmuteGroupQuestion({required bool isMuted}) { - if (isMuted) { - return 'Are you sure you want to unmute this group?'; - } else { - return 'Are you sure you want to mute this group?'; - } - } - - @override - String toggleMuteUnmuteUserQuestion({required bool isMuted}) { - if (isMuted) { - return 'Are you sure you want to unmute this user?'; - } else { - return 'Are you sure you want to mute this user?'; - } - } - - @override - String toggleMuteUnmuteAction({required bool isMuted}) { - if (isMuted) { - return 'UNMUTE'; - } else { - return 'MUTE'; - } - } - - @override - String toggleMuteUnmuteGroupText({required bool isMuted}) { - if (isMuted) { - return 'Unmute Group'; - } else { - return 'Mute Group'; - } - } - - @override - String get linkDisabledDetails => - 'Sending links is not allowed in this conversation.'; - - @override - String get linkDisabledError => 'Links are disabled'; - - @override - String unreadMessagesSeparatorText() => 'New messages'; - - @override - String get enableFileAccessMessage => 'Please enable access to files' - '\nso you can share them with friends.'; - - @override - String get allowFileAccessMessage => 'Allow access to files'; - - @override - String get markUnreadError => - 'Error marking message unread. Cannot mark unread messages older than the' - ' newest 100 channel messages.'; - - @override - String createPollLabel({bool isNew = false}) { - if (isNew) return 'Create a new poll'; - return 'Create Poll'; - } - - @override - String get questionsLabel => 'Questions'; - - @override - String get askAQuestionLabel => 'Ask a question'; - - @override - String? pollQuestionValidationError(int length, Range range) { - final (:min, :max) = range; - - // Check if the question is too short. - if (min != null && length < min) { - return 'Question must be at least $min characters long'; - } - - // Check if the question is too long. - if (max != null && length > max) { - return 'Question must be at most $max characters long'; - } - - return null; - } - - @override - String optionLabel({bool isPlural = false}) { - if (isPlural) return 'Options'; - return 'Option'; - } - - @override - String get pollOptionEmptyError => 'Option cannot be empty'; - - @override - String get pollOptionDuplicateError => 'This is already an option'; - - @override - String get addAnOptionLabel => 'Add an option'; - - @override - String get multipleAnswersLabel => 'Multiple answers'; - - @override - String get maximumVotesPerPersonLabel => 'Maximum votes per person'; - - @override - String? maxVotesPerPersonValidationError(int votes, Range range) { - final (:min, :max) = range; - - if (min != null && votes < min) { - return 'Vote count must be at least $min'; - } - - if (max != null && votes > max) { - return 'Vote count must be at most $max'; - } - - return null; - } - - @override - String get anonymousPollLabel => 'Anonymous poll'; - - @override - String get pollOptionsLabel => 'Poll Options'; - - @override - String get suggestAnOptionLabel => 'Suggest an option'; - - @override - String get enterANewOptionLabel => 'Enter a new option'; - - @override - String get addACommentLabel => 'Add a comment'; - - @override - String get pollCommentsLabel => 'Poll Comments'; - - @override - String get updateYourCommentLabel => 'Update your comment'; - - @override - String get enterYourCommentLabel => 'Enter your comment'; - - @override - String get createLabel => 'Create'; - - @override - String pollVotingModeLabel(PollVotingMode votingMode) { - return votingMode.when( - disabled: () => 'Vote ended', - unique: () => 'Select one', - limited: (count) => 'Select up to $count', - all: () => 'Select one or more', - ); - } - - @override - String seeAllOptionsLabel({int? count}) { - if (count == null) return 'See all options'; - return 'See all $count options'; - } - - @override - String get viewCommentsLabel => 'View Comments'; - - @override - String get viewResultsLabel => 'View Results'; - - @override - String get endVoteLabel => 'End Vote'; - - @override - String get pollResultsLabel => 'Poll Results'; - - @override - String showAllVotesLabel({int? count}) { - if (count == null) return 'Show all votes'; - return 'Show all $count votes'; - } - - @override - String voteCountLabel({int? count}) => switch (count) { - null || < 1 => '0 votes', - 1 => '1 vote', - _ => '$count votes', - }; - - @override - String get noPollVotesLabel => 'There are no poll votes currently'; - - @override - String get loadingPollVotesError => 'Error loading poll votes'; - - @override - String get repliedToLabel => 'replied to:'; - - @override - String newThreadsLabel({required int count}) { - if (count == 1) return '1 new thread'; - return '$count new threads'; - } - - @override - String get slideToCancelLabel => 'Slide to cancel'; - - @override - String get holdToRecordLabel => 'Hold to record, release to send.'; -} diff --git a/packages/stream_chat_flutter/lib/src/message_actions_modal/copy_message_button.dart b/packages/stream_chat_flutter/lib/src/message_actions_modal/copy_message_button.dart deleted file mode 100644 index cdb7415f21..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_actions_modal/copy_message_button.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template copyMessageButton} -/// Allows a user to copy the text of a message. -/// -/// Used by [MessageActionsModal]. Should not be used by itself. -/// {@endtemplate} -class CopyMessageButton extends StatelessWidget { - /// {@macro copyMessageButton} - const CopyMessageButton({ - super.key, - required this.onTap, - }); - - /// The callback to perform when the button is tapped. - final VoidCallback onTap; - - @override - Widget build(BuildContext context) { - final streamChatThemeData = StreamChatTheme.of(context); - return InkWell( - onTap: onTap, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16), - child: Row( - children: [ - StreamSvgIcon( - size: 24, - icon: StreamSvgIcons.copy, - color: streamChatThemeData.primaryIconTheme.color, - ), - const SizedBox(width: 16), - Text( - context.translations.copyMessageLabel, - style: streamChatThemeData.textTheme.body, - ), - ], - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_actions_modal/delete_message_button.dart b/packages/stream_chat_flutter/lib/src/message_actions_modal/delete_message_button.dart deleted file mode 100644 index 565d260fbd..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_actions_modal/delete_message_button.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template deleteMessageButton} -/// A button that allows a user to delete the selected message. -/// -/// Used by [MessageActionsModal]. Should not be used by itself. -/// {@endtemplate} -class DeleteMessageButton extends StatelessWidget { - /// {@macro deleteMessageButton} - const DeleteMessageButton({ - super.key, - required this.isDeleteFailed, - required this.onTap, - }); - - /// Indicates whether the deletion has failed or not. - final bool isDeleteFailed; - - /// The action (deleting the message) to be performed on tap. - final VoidCallback onTap; - - @override - Widget build(BuildContext context) { - return InkWell( - onTap: onTap, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16), - child: Row( - children: [ - const StreamSvgIcon( - color: Colors.red, - icon: StreamSvgIcons.delete, - ), - const SizedBox(width: 16), - Text( - context.translations.toggleDeleteRetryDeleteMessageText( - isDeleteFailed: isDeleteFailed, - ), - style: StreamChatTheme.of(context) - .textTheme - .body - .copyWith(color: Colors.red), - ), - ], - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_actions_modal/edit_message_button.dart b/packages/stream_chat_flutter/lib/src/message_actions_modal/edit_message_button.dart deleted file mode 100644 index b4f23771d2..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_actions_modal/edit_message_button.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template editMessageButton} -/// Allows a user to edit a message. -/// -/// Used by [MessageActionsModal]. Should not be used by itself. -/// {@endtemplate} -class EditMessageButton extends StatelessWidget { - /// {@macro editMessageButton} - const EditMessageButton({ - super.key, - required this.onTap, - }); - - /// The callback to perform when the button is tapped. - final VoidCallback onTap; - - @override - Widget build(BuildContext context) { - final streamChatThemeData = StreamChatTheme.of(context); - return InkWell( - onTap: onTap, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16), - child: Row( - children: [ - StreamSvgIcon( - icon: StreamSvgIcons.edit, - color: streamChatThemeData.primaryIconTheme.color, - ), - const SizedBox(width: 16), - Text( - context.translations.editMessageLabel, - style: streamChatThemeData.textTheme.body, - ), - ], - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_actions_modal/flag_message_button.dart b/packages/stream_chat_flutter/lib/src/message_actions_modal/flag_message_button.dart deleted file mode 100644 index 5c57547108..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_actions_modal/flag_message_button.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template flagMessageButton} -/// Allows a user to flag a message. -/// -/// Used by [MessageActionsModal]. Should not be used by itself. -/// {@endtemplate} -class FlagMessageButton extends StatelessWidget { - /// {@macro flagMessageButton} - const FlagMessageButton({ - super.key, - required this.onTap, - }); - - /// The callback to perform when the button is tapped. - final VoidCallback onTap; - - @override - Widget build(BuildContext context) { - final streamChatThemeData = StreamChatTheme.of(context); - return InkWell( - onTap: onTap, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16), - child: Row( - children: [ - StreamSvgIcon( - icon: StreamSvgIcons.flag, - color: streamChatThemeData.primaryIconTheme.color, - ), - const SizedBox(width: 16), - Text( - context.translations.flagMessageLabel, - style: streamChatThemeData.textTheme.body, - ), - ], - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_actions_modal/mam_widgets.dart b/packages/stream_chat_flutter/lib/src/message_actions_modal/mam_widgets.dart deleted file mode 100644 index e756d682b2..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_actions_modal/mam_widgets.dart +++ /dev/null @@ -1,8 +0,0 @@ -export 'copy_message_button.dart'; -export 'delete_message_button.dart'; -export 'edit_message_button.dart'; -export 'flag_message_button.dart'; -export 'pin_message_button.dart'; -export 'reply_button.dart'; -export 'resend_message_button.dart'; -export 'thread_reply_button.dart'; diff --git a/packages/stream_chat_flutter/lib/src/message_actions_modal/mark_unread_message_button.dart b/packages/stream_chat_flutter/lib/src/message_actions_modal/mark_unread_message_button.dart deleted file mode 100644 index 12c38d0210..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_actions_modal/mark_unread_message_button.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template markUnreadMessageButton} -/// Allows a user to mark message (and all messages onwards) as unread. -/// -/// Used by [MessageActionsModal]. Should not be used by itself. -/// {@endtemplate} -class MarkUnreadMessageButton extends StatelessWidget { - /// {@macro markUnreadMessageButton} - const MarkUnreadMessageButton({ - super.key, - required this.onTap, - }); - - /// The callback to perform when the button is tapped. - final VoidCallback onTap; - - @override - Widget build(BuildContext context) { - final streamChatThemeData = StreamChatTheme.of(context); - return InkWell( - onTap: onTap, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16), - child: Row( - children: [ - StreamSvgIcon( - icon: StreamSvgIcons.messageUnread, - color: streamChatThemeData.primaryIconTheme.color, - size: 24, - ), - const SizedBox(width: 16), - Text( - context.translations.markAsUnreadLabel, - style: streamChatThemeData.textTheme.body, - ), - ], - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_actions_modal/message_action.dart b/packages/stream_chat_flutter/lib/src/message_actions_modal/message_action.dart deleted file mode 100644 index f3acaac964..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_actions_modal/message_action.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:stream_chat_flutter/src/utils/typedefs.dart'; - -/// {@template streamMessageAction} -/// Class describing a message action -/// {@endtemplate} -class StreamMessageAction { - /// {@macro streamMessageAction} - StreamMessageAction({ - this.leading, - this.title, - this.onTap, - }); - - /// leading widget - final Widget? leading; - - /// title widget - final Widget? title; - - /// {@macro onMessageTap} - final OnMessageTap? onTap; -} diff --git a/packages/stream_chat_flutter/lib/src/message_actions_modal/message_actions_modal.dart b/packages/stream_chat_flutter/lib/src/message_actions_modal/message_actions_modal.dart deleted file mode 100644 index 7c8e27435e..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_actions_modal/message_actions_modal.dart +++ /dev/null @@ -1,445 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/material.dart' hide ButtonStyle; -import 'package:stream_chat_flutter/src/message_actions_modal/mam_widgets.dart'; -import 'package:stream_chat_flutter/src/message_actions_modal/mark_unread_message_button.dart'; -import 'package:stream_chat_flutter/src/message_widget/reactions/reactions_align.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template messageActionsModal} -/// Constructs a modal with actions for a message -/// {@endtemplate} -class MessageActionsModal extends StatefulWidget { - /// {@macro messageActionsModal} - const MessageActionsModal({ - super.key, - required this.message, - required this.messageWidget, - required this.messageTheme, - this.showReactionPicker = true, - this.showDeleteMessage = true, - this.showEditMessage = true, - this.onReplyTap, - this.onConfirmDeleteTap, - this.onThreadReplyTap, - this.showCopyMessage = true, - this.showReplyMessage = true, - this.showResendMessage = true, - this.showThreadReplyMessage = true, - this.showMarkUnreadMessage = true, - this.showFlagButton = true, - this.showPinButton = true, - this.editMessageInputBuilder, - this.reverse = false, - this.customActions = const [], - this.onCopyTap, - }); - - /// Widget that shows the message - final Widget messageWidget; - - /// Builder for edit message - final EditMessageInputBuilder? editMessageInputBuilder; - - /// The action to perform when "thread reply" is tapped - final OnMessageTap? onThreadReplyTap; - - /// The action to perform when "reply" is tapped - final OnMessageTap? onReplyTap; - - /// The action to perform when delete confirmation button is tapped. - final Future Function(Message)? onConfirmDeleteTap; - - /// Message in focus for actions - final Message message; - - /// [StreamMessageThemeData] for message - final StreamMessageThemeData messageTheme; - - /// Flag for showing reaction picker. - final bool showReactionPicker; - - /// Callback when copy is tapped - final OnMessageTap? onCopyTap; - - /// Callback when delete is tapped - final bool showDeleteMessage; - - /// Flag for showing copy action - final bool showCopyMessage; - - /// Flag for showing edit action - final bool showEditMessage; - - /// Flag for showing resend action - final bool showResendMessage; - - /// Flag for showing mark unread action - final bool showMarkUnreadMessage; - - /// Flag for showing reply action - final bool showReplyMessage; - - /// Flag for showing thread reply action - final bool showThreadReplyMessage; - - /// Flag for showing flag action - final bool showFlagButton; - - /// Flag for showing pin action - final bool showPinButton; - - /// Flag for reversing message - final bool reverse; - - /// List of custom actions - final List customActions; - - @override - _MessageActionsModalState createState() => _MessageActionsModalState(); -} - -class _MessageActionsModalState extends State { - bool _showActions = true; - - @override - Widget build(BuildContext context) { - final mediaQueryData = MediaQuery.of(context); - final user = StreamChat.of(context).currentUser; - final orientation = mediaQueryData.orientation; - - final _userPermissions = StreamChannel.of(context).channel.ownCapabilities; - final hasReactionPermission = - _userPermissions.contains(PermissionType.sendReaction); - - final fontSize = widget.messageTheme.messageTextStyle?.fontSize; - final streamChatThemeData = StreamChatTheme.of(context); - - final channel = StreamChannel.of(context).channel; - - final child = Center( - child: SingleChildScrollView( - child: SafeArea( - child: Padding( - padding: const EdgeInsets.all(8), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - if (widget.showReactionPicker && hasReactionPermission) - LayoutBuilder( - builder: (context, constraints) { - return Align( - alignment: Alignment( - calculateReactionsHorizontalAlignment( - user, - widget.message, - constraints, - fontSize, - orientation, - ), - 0, - ), - child: StreamReactionPicker( - message: widget.message, - ), - ); - }, - ), - const SizedBox(height: 10), - IgnorePointer( - child: widget.messageWidget, - ), - const SizedBox(height: 8), - Padding( - padding: EdgeInsets.only( - left: widget.reverse ? 0 : 40, - ), - child: SizedBox( - width: mediaQueryData.size.width * 0.75, - child: Material( - color: streamChatThemeData.colorTheme.appBg, - clipBehavior: Clip.hardEdge, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - if (widget.showReplyMessage && - widget.message.state.isCompleted) - ReplyButton( - onTap: () { - Navigator.of(context).pop(); - if (widget.onReplyTap != null) { - widget.onReplyTap?.call(widget.message); - } - }, - ), - if (widget.showThreadReplyMessage && - (widget.message.state.isCompleted) && - widget.message.parentId == null) - ThreadReplyButton( - message: widget.message, - onThreadReplyTap: widget.onThreadReplyTap, - ), - if (widget.showMarkUnreadMessage) - MarkUnreadMessageButton(onTap: () async { - try { - await channel.markUnread(widget.message.id); - } catch (ex) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.translations.markUnreadError, - ), - ), - ); - } - - Navigator.of(context).pop(); - }), - if (widget.showResendMessage) - ResendMessageButton( - message: widget.message, - channel: channel, - ), - if (widget.showEditMessage) - EditMessageButton( - onTap: () { - Navigator.of(context).pop(); - _showEditBottomSheet(context); - }, - ), - if (widget.showCopyMessage) - CopyMessageButton( - onTap: () { - widget.onCopyTap?.call(widget.message); - Navigator.of(context).pop(); - }, - ), - if (widget.showFlagButton) - FlagMessageButton( - onTap: _showFlagDialog, - ), - if (widget.showPinButton) - PinMessageButton( - onTap: _togglePin, - pinned: widget.message.pinned, - ), - if (widget.showDeleteMessage) - DeleteMessageButton( - isDeleteFailed: - widget.message.state.isDeletingFailed, - onTap: _showDeleteBottomSheet, - ), - ...widget.customActions - .map((action) => _buildCustomAction( - context, - action, - )), - ].insertBetween( - Container( - height: 1, - color: streamChatThemeData.colorTheme.borders, - ), - ), - ), - ), - ), - ), - ], - ), - ), - ), - ), - ); - - return GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () => Navigator.of(context).maybePop(), - child: Stack( - children: [ - Positioned.fill( - child: BackdropFilter( - filter: ImageFilter.blur( - sigmaX: 10, - sigmaY: 10, - ), - child: ColoredBox( - color: streamChatThemeData.colorTheme.overlay, - ), - ), - ), - if (_showActions) - TweenAnimationBuilder( - tween: Tween(begin: 0, end: 1), - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOutBack, - builder: (context, val, child) => Transform.scale( - scale: val, - child: child, - ), - child: child, - ), - ], - ), - ); - } - - InkWell _buildCustomAction( - BuildContext context, - StreamMessageAction messageAction, - ) { - return InkWell( - onTap: () => messageAction.onTap?.call(widget.message), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16), - child: Row( - children: [ - messageAction.leading ?? const Offstage(), - const SizedBox(width: 16), - messageAction.title ?? const Offstage(), - ], - ), - ), - ); - } - - Future _showFlagDialog() async { - final client = StreamChat.of(context).client; - - final streamChatThemeData = StreamChatTheme.of(context); - final answer = await showConfirmationBottomSheet( - context, - title: context.translations.flagMessageLabel, - icon: StreamSvgIcon( - icon: StreamSvgIcons.flag, - color: streamChatThemeData.colorTheme.accentError, - size: 24, - ), - question: context.translations.flagMessageQuestion, - okText: context.translations.flagLabel, - cancelText: context.translations.cancelLabel, - ); - - final theme = streamChatThemeData; - if (answer == true) { - try { - await client.flagMessage(widget.message.id); - await showInfoBottomSheet( - context, - icon: StreamSvgIcon( - icon: StreamSvgIcons.flag, - color: theme.colorTheme.accentError, - size: 24, - ), - details: context.translations.flagMessageSuccessfulText, - title: context.translations.flagMessageSuccessfulLabel, - okText: context.translations.okLabel, - ); - } catch (err) { - if (err is StreamChatNetworkError && - err.errorCode == ChatErrorCode.inputError) { - await showInfoBottomSheet( - context, - icon: StreamSvgIcon( - icon: StreamSvgIcons.flag, - color: theme.colorTheme.accentError, - size: 24, - ), - details: context.translations.flagMessageSuccessfulText, - title: context.translations.flagMessageSuccessfulLabel, - okText: context.translations.okLabel, - ); - } else { - _showErrorAlertBottomSheet(); - } - } - } - } - - Future _togglePin() async { - final channel = StreamChannel.of(context).channel; - - Navigator.of(context).pop(); - try { - if (!widget.message.pinned) { - await channel.pinMessage(widget.message); - } else { - await channel.unpinMessage(widget.message); - } - } catch (e) { - _showErrorAlertBottomSheet(); - } - } - - /// Shows a "delete message" bottom sheet on mobile platforms. - Future _showDeleteBottomSheet() async { - setState(() => _showActions = false); - final answer = await showConfirmationBottomSheet( - context, - title: context.translations.deleteMessageLabel, - icon: StreamSvgIcon( - icon: StreamSvgIcons.flag, - color: StreamChatTheme.of(context).colorTheme.accentError, - size: 24, - ), - question: context.translations.deleteMessageQuestion, - okText: context.translations.deleteLabel, - cancelText: context.translations.cancelLabel, - ); - - if (answer == true) { - try { - Navigator.of(context).pop(); - final onConfirmDeleteTap = widget.onConfirmDeleteTap; - if (onConfirmDeleteTap != null) { - await onConfirmDeleteTap(widget.message); - } else { - await StreamChannel.of(context).channel.deleteMessage(widget.message); - } - } catch (err) { - _showErrorAlertBottomSheet(); - } - } else { - setState(() => _showActions = true); - } - } - - void _showErrorAlertBottomSheet() { - showInfoBottomSheet( - context, - icon: StreamSvgIcon( - icon: StreamSvgIcons.error, - color: StreamChatTheme.of(context).colorTheme.accentError, - size: 24, - ), - details: context.translations.operationCouldNotBeCompletedText, - title: context.translations.somethingWentWrongError, - okText: context.translations.okLabel, - ); - } - - void _showEditBottomSheet(BuildContext context) { - final channel = StreamChannel.of(context).channel; - showModalBottomSheet( - context: context, - elevation: 2, - clipBehavior: Clip.hardEdge, - isScrollControlled: true, - backgroundColor: StreamMessageInputTheme.of(context).inputBackgroundColor, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16), - ), - ), - builder: (context) => EditMessageSheet( - channel: channel, - message: widget.message, - editMessageInputBuilder: widget.editMessageInputBuilder, - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_actions_modal/pin_message_button.dart b/packages/stream_chat_flutter/lib/src/message_actions_modal/pin_message_button.dart deleted file mode 100644 index 07313065ae..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_actions_modal/pin_message_button.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template pinMessageButton} -/// Allows a user to pin or unpin a message. -/// -/// Used by [MessageActionsModal]. Should not be used by itself. -/// {@endtemplate} -class PinMessageButton extends StatelessWidget { - /// {@macro pinMessageButton} - const PinMessageButton({ - super.key, - required this.onTap, - required this.pinned, - }); - - /// The callback to perform when the button is tapped. - final VoidCallback onTap; - - /// Whether the selected message is currently pinned or not. - final bool pinned; - - @override - Widget build(BuildContext context) { - final streamChatThemeData = StreamChatTheme.of(context); - return InkWell( - onTap: onTap, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16), - child: Row( - children: [ - StreamSvgIcon( - icon: StreamSvgIcons.pin, - color: streamChatThemeData.primaryIconTheme.color, - size: 24, - ), - const SizedBox(width: 16), - Text( - context.translations.togglePinUnpinText( - pinned: pinned, - ), - style: streamChatThemeData.textTheme.body, - ), - ], - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_actions_modal/reply_button.dart b/packages/stream_chat_flutter/lib/src/message_actions_modal/reply_button.dart deleted file mode 100644 index b4340d8f96..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_actions_modal/reply_button.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template replyButton} -/// Allows a user to reply to a message. -/// -/// Used by [MessageActionsModal]. Should not be used by itself. -/// {@endtemplate} -class ReplyButton extends StatelessWidget { - /// {@macro replyButton} - const ReplyButton({ - super.key, - required this.onTap, - }); - - /// The callback to perform when the button is tapped. - final VoidCallback onTap; - - @override - Widget build(BuildContext context) { - final streamChatThemeData = StreamChatTheme.of(context); - return InkWell( - onTap: onTap, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16), - child: Row( - children: [ - StreamSvgIcon( - icon: StreamSvgIcons.reply, - color: streamChatThemeData.primaryIconTheme.color, - ), - const SizedBox(width: 16), - Text( - context.translations.replyLabel, - style: streamChatThemeData.textTheme.body, - ), - ], - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_actions_modal/resend_message_button.dart b/packages/stream_chat_flutter/lib/src/message_actions_modal/resend_message_button.dart deleted file mode 100644 index 8c94c621ad..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_actions_modal/resend_message_button.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template resendMessageButton} -/// Allows a user to resend a message that has failed to be sent. -/// -/// Used by [MessageActionsModal]. Should not be used by itself. -/// {@endtemplate} -class ResendMessageButton extends StatelessWidget { - /// {@macro resendMessageButton} - const ResendMessageButton({ - super.key, - required this.message, - required this.channel, - }); - - /// The message to resend. - final Message message; - - /// The [StreamChannel] above this widget. - final Channel channel; - - @override - Widget build(BuildContext context) { - final isUpdateFailed = message.state.isUpdatingFailed; - final streamChatThemeData = StreamChatTheme.of(context); - return InkWell( - onTap: () { - Navigator.of(context).pop(); - channel.retryMessage(message); - }, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16), - child: Row( - children: [ - StreamSvgIcon( - icon: StreamSvgIcons.circleUp, - color: streamChatThemeData.colorTheme.accentPrimary, - ), - const SizedBox(width: 16), - Text( - context.translations.toggleResendOrResendEditedMessage( - isUpdateFailed: isUpdateFailed, - ), - style: streamChatThemeData.textTheme.body, - ), - ], - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_actions_modal/thread_reply_button.dart b/packages/stream_chat_flutter/lib/src/message_actions_modal/thread_reply_button.dart deleted file mode 100644 index f5ffb4a357..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_actions_modal/thread_reply_button.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template threadReplyButton} -/// Allows a user to start a thread reply to a message. -/// -/// Used by [MessageActionsModal]. Should not be used by itself. -/// {@endtemplate} -class ThreadReplyButton extends StatelessWidget { - /// {@macro threadReplyButton} - const ThreadReplyButton({ - super.key, - required this.message, - this.onThreadReplyTap, - }); - - /// The message to start a thread reply to. - final Message message; - - /// The action to perform when "thread reply" is tapped - final OnMessageTap? onThreadReplyTap; - - @override - Widget build(BuildContext context) { - final streamChatThemeData = StreamChatTheme.of(context); - return InkWell( - onTap: () { - Navigator.of(context).pop(); - if (onThreadReplyTap != null) { - onThreadReplyTap?.call(message); - } - }, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16), - child: Row( - children: [ - StreamSvgIcon( - icon: StreamSvgIcons.threadReply, - color: streamChatThemeData.primaryIconTheme.color, - ), - const SizedBox(width: 16), - Text( - context.translations.threadReplyLabel, - style: streamChatThemeData.textTheme.body, - ), - ], - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/attachment_button.dart b/packages/stream_chat_flutter/lib/src/message_input/attachment_button.dart deleted file mode 100644 index 4212938baa..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/attachment_button.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/message_input/stream_message_input_icon_button.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template attachmentButton} -/// A button for adding attachments to a chat on mobile. -/// {@endtemplate} -class AttachmentButton extends StatelessWidget { - /// {@macro attachmentButton} - const AttachmentButton({ - super.key, - required this.onPressed, - this.color, - this.icon, - this.size = kDefaultMessageInputIconSize, - }) : assert( - (icon == null && color == null) || - (icon != null && color == null) || - (icon == null && color != null), - 'Either icon or color should be provided'); - - /// The color of the button. - /// Should be set if no [icon] is provided. - final Color? color; - - /// The callback to perform when the button is tapped or clicked. - final VoidCallback onPressed; - - /// The icon to display inside the button. - /// if not provided, a default icon will be used - /// and [color] property should be set. - final Widget? icon; - - /// The size of the button and splash radius. - final double size; - - /// Returns a copy of this object with the given fields updated. - AttachmentButton copyWith({ - Key? key, - Color? color, - VoidCallback? onPressed, - Widget? icon, - double? size, - }) { - return AttachmentButton( - key: key ?? this.key, - color: color ?? this.color, - onPressed: onPressed ?? this.onPressed, - icon: icon ?? this.icon, - size: size ?? this.size, - ); - } - - @override - Widget build(BuildContext context) { - return StreamMessageInputIconButton( - color: color, - iconSize: size, - onPressed: onPressed, - icon: icon ?? const StreamSvgIcon(icon: StreamSvgIcons.attach), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/options.dart b/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/options.dart deleted file mode 100644 index 2e34169c56..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/options.dart +++ /dev/null @@ -1,5 +0,0 @@ -export 'stream_file_picker.dart'; -export 'stream_gallery_picker.dart'; -export 'stream_image_picker.dart'; -export 'stream_poll_creator.dart'; -export 'stream_video_picker.dart'; diff --git a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_file_picker.dart b/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_file_picker.dart deleted file mode 100644 index c410532d2d..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_file_picker.dart +++ /dev/null @@ -1,117 +0,0 @@ -import 'package:file_picker/file_picker.dart'; -import 'package:flutter/material.dart'; -import 'package:photo_manager/photo_manager.dart'; -import 'package:stream_chat_flutter/src/attachment/handler/stream_attachment_handler.dart'; -import 'package:stream_chat_flutter/src/icons/stream_svg_icon.dart'; -import 'package:stream_chat_flutter/src/message_input/attachment_picker/stream_attachment_picker.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter/src/utils/utils.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// Widget used to pick files from the device -class StreamFilePicker extends StatelessWidget { - /// Creates a [StreamFilePicker] widget. - const StreamFilePicker({ - super.key, - required this.onFilePicked, - this.dialogTitle, - this.initialDirectory, - this.type = FileType.any, - this.allowedExtensions, - this.onFileLoading, - this.allowCompression = true, - this.withData = false, - this.withReadStream = false, - this.lockParentWindow = false, - }); - - /// Callback called when a file is picked. - final ValueSetter onFilePicked; - - /// Title of the file picker dialog. - final String? dialogTitle; - - /// Initial directory of the file picker dialog. - final String? initialDirectory; - - /// Type of the file to pick. - final FileType type; - - /// Allowed extensions of the file to pick. - final List? allowedExtensions; - - /// Callback called when the file picker is loading a file. - final Function(FilePickerStatus)? onFileLoading; - - /// Whether to allow compression of the file. - final bool allowCompression; - - /// Whether to include the file data in the [Attachment]. - final bool withData; - - /// Whether to include the file read stream in the [Attachment]. - final bool withReadStream; - - /// Whether to lock the parent window when the file picker is open. - final bool lockParentWindow; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - return OptionDrawer( - child: EndOfFrameCallbackWidget( - child: StreamSvgIcon( - size: 240, - icon: StreamSvgIcons.files, - color: theme.colorTheme.disabled, - ), - onEndOfFrame: (_) async { - final pickedFile = await runInPermissionRequestLock(() { - return StreamAttachmentHandler.instance.pickFile( - dialogTitle: dialogTitle, - initialDirectory: initialDirectory, - type: type, - allowedExtensions: allowedExtensions, - onFileLoading: onFileLoading, - allowCompression: allowCompression, - withData: withData, - withReadStream: withReadStream, - lockParentWindow: lockParentWindow, - ); - }); - - onFilePicked.call(pickedFile); - }, - errorBuilder: (context, error, stacktrace) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - StreamSvgIcon( - size: 240, - icon: StreamSvgIcons.files, - color: theme.colorTheme.disabled, - ), - Text( - context.translations.enablePhotoAndVideoAccessMessage, - style: theme.textTheme.body.copyWith( - color: theme.colorTheme.textLowEmphasis, - ), - textAlign: TextAlign.center, - ), - const SizedBox(height: 8), - TextButton( - onPressed: PhotoManager.openSetting, - child: Text( - context.translations.allowGalleryAccessMessage, - style: theme.textTheme.bodyBold.copyWith( - color: theme.colorTheme.accentPrimary, - ), - ), - ), - ], - ); - }, - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_gallery_picker.dart b/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_gallery_picker.dart deleted file mode 100644 index ab2d17d26b..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_gallery_picker.dart +++ /dev/null @@ -1,271 +0,0 @@ -import 'dart:io'; -import 'dart:math' as math; - -import 'package:flutter/material.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:photo_manager/photo_manager.dart'; -import 'package:stream_chat_flutter/src/icons/stream_svg_icon.dart'; -import 'package:stream_chat_flutter/src/message_input/attachment_picker/stream_attachment_picker.dart'; -import 'package:stream_chat_flutter/src/scroll_view/photo_gallery/stream_photo_gallery.dart'; -import 'package:stream_chat_flutter/src/scroll_view/photo_gallery/stream_photo_gallery_controller.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter/src/utils/utils.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// Max image resolution which can be resized by the CDN. -// Taken from https://getstream.io/chat/docs/flutter-dart/file_uploads/?language=dart#image-resizing -const maxCDNImageResolution = 16800000; - -/// Widget used to pick media from the device gallery. -class StreamGalleryPicker extends StatefulWidget { - /// Creates a [StreamGalleryPicker] widget. - const StreamGalleryPicker({ - super.key, - this.limit = 50, - required this.selectedMediaItems, - required this.onMediaItemSelected, - this.mediaThumbnailSize = const ThumbnailSize(400, 400), - this.mediaThumbnailFormat = ThumbnailFormat.jpeg, - this.mediaThumbnailQuality = 100, - this.mediaThumbnailScale = 1, - }); - - /// Maximum number of media items that can be selected. - final int limit; - - /// List of selected media items. - final Iterable selectedMediaItems; - - /// Callback called when an media item is selected. - final ValueSetter onMediaItemSelected; - - /// Size of the attachment thumbnails. - /// - /// Defaults to (400, 400). - final ThumbnailSize mediaThumbnailSize; - - /// Format of the attachment thumbnails. - /// - /// Defaults to [ThumbnailFormat.jpeg]. - final ThumbnailFormat mediaThumbnailFormat; - - /// The quality value for the attachment thumbnails. - /// - /// Valid from 1 to 100. - /// Defaults to 100. - final int mediaThumbnailQuality; - - /// The scale to apply on the [attachmentThumbnailSize]. - final double mediaThumbnailScale; - - @override - State createState() => _StreamGalleryPickerState(); -} - -class _StreamGalleryPickerState extends State { - Future? requestPermission; - late StreamPhotoGalleryController _controller; - - @override - void initState() { - super.initState(); - _controller = StreamPhotoGalleryController(limit: widget.limit); - requestPermission = runInPermissionRequestLock( - PhotoManager.requestPermissionExtend, - ); - } - - @override - void didUpdateWidget(StreamGalleryPicker oldWidget) { - super.didUpdateWidget(oldWidget); - if (widget.limit != oldWidget.limit) { - _controller.dispose(); - _controller = StreamPhotoGalleryController(limit: widget.limit); - } - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return FutureBuilder( - future: requestPermission, - builder: (context, snapshot) { - if (!snapshot.hasData) return const SizedBox.shrink(); - - final theme = StreamChatTheme.of(context); - final textTheme = theme.textTheme; - final colorTheme = theme.colorTheme; - - // Available on both Android and iOS. - final isAuthorized = snapshot.data == PermissionState.authorized; - // Only available on iOS. - final isLimited = snapshot.data == PermissionState.limited; - - final isPermissionGranted = isAuthorized || isLimited; - - return OptionDrawer( - actions: [ - if (isLimited) - IconButton( - color: colorTheme.accentPrimary, - icon: const Icon(Icons.add_circle_outline_rounded), - onPressed: () async { - await PhotoManager.presentLimited(); - _controller.doInitialLoad(); - }, - ), - ], - child: Builder( - builder: (context) { - if (!isPermissionGranted) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - StreamSvgIcon( - size: 240, - icon: StreamSvgIcons.pictures, - color: colorTheme.disabled, - ), - Text( - context.translations.enablePhotoAndVideoAccessMessage, - style: textTheme.body.copyWith( - color: colorTheme.textLowEmphasis, - ), - textAlign: TextAlign.center, - ), - const SizedBox(height: 8), - TextButton( - onPressed: PhotoManager.openSetting, - child: Text( - context.translations.allowGalleryAccessMessage, - style: textTheme.bodyBold.copyWith( - color: colorTheme.accentPrimary, - ), - ), - ), - ], - ); - } - - return StreamPhotoGallery( - shrinkWrap: true, - controller: _controller, - onMediaTap: widget.onMediaItemSelected, - loadMoreTriggerIndex: 10, - padding: const EdgeInsets.all(2), - thumbnailSize: widget.mediaThumbnailSize, - thumbnailFormat: widget.mediaThumbnailFormat, - thumbnailQuality: widget.mediaThumbnailQuality, - thumbnailScale: widget.mediaThumbnailScale, - itemBuilder: (context, mediaItems, index, defaultWidget) { - final media = mediaItems[index]; - return defaultWidget.copyWith( - selected: widget.selectedMediaItems.contains(media.id), - ); - }, - ); - }, - ), - ); - }, - ); - } -} - -/// -extension StreamImagePickerX on StreamAttachmentPickerController { - /// - Future addAssetAttachment(AssetEntity asset) async { - final mediaFile = await asset.originFile; - - if (mediaFile == null) return; - - var cachedFile = mediaFile; - - final type = asset.type; - if (type == AssetType.image) { - // Resize image if it's resolution is greater than the - // [maxCDNImageResolution]. - final imageResolution = asset.width * asset.height; - if (imageResolution > maxCDNImageResolution) { - final aspect = imageResolution / maxCDNImageResolution; - final updatedSize = asset.size / (math.sqrt(aspect)); - final resizedImage = await asset.thumbnailDataWithSize( - ThumbnailSize( - updatedSize.width.floor(), - updatedSize.height.floor(), - ), - quality: 70, - ); - - final tempDir = await getTemporaryDirectory(); - cachedFile = await File( - '${tempDir.path}/${mediaFile.path.split('/').last}', - ).create().then((it) => it.writeAsBytes(resizedImage!)); - } - } - - final file = AttachmentFile( - path: cachedFile.path, - size: await cachedFile.length(), - bytes: cachedFile.readAsBytesSync(), - ); - - final extraDataMap = {}; - - final mimeType = file.mediaType?.mimeType; - - if (mimeType != null) { - extraDataMap['mime_type'] = mimeType; - } - - extraDataMap['file_size'] = file.size!; - - final attachment = Attachment( - id: asset.id, - file: file, - type: asset.type.toAttachmentType(), - extraData: extraDataMap, - ); - - return addAttachment(attachment); - } - - /// - Future removeAssetAttachment(AssetEntity asset) async { - if (asset.type == AssetType.image) { - final image = await asset.originFile; - if (image != null) { - final tempDir = await getTemporaryDirectory(); - final cachedFile = - File('${tempDir.path}/${image.path.split('/').last}'); - if (cachedFile.existsSync()) { - cachedFile.deleteSync(); - } - } - } - return removeAttachmentById(asset.id); - } -} - -/// -extension AssetTypeX on AssetType { - /// - String toAttachmentType() { - switch (this) { - case AssetType.image: - return 'image'; - case AssetType.video: - return 'video'; - case AssetType.audio: - return 'audio'; - case AssetType.other: - return 'file'; - } - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_image_picker.dart b/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_image_picker.dart deleted file mode 100644 index a9130ea73b..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_image_picker.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:image_picker/image_picker.dart'; -import 'package:photo_manager/photo_manager.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Widget used to pick images from the device. -class StreamImagePicker extends StatelessWidget { - /// Creates a [StreamImagePicker] widget. - const StreamImagePicker({ - super.key, - required this.onImagePicked, - this.source = ImageSource.camera, - this.maxWidth, - this.maxHeight, - this.imageQuality, - this.preferredCameraDevice = CameraDevice.rear, - }); - - /// Callback called when an image is picked. - final ValueSetter onImagePicked; - - /// Source of the image to pick. - final ImageSource source; - - /// Maximum width of the image. - final double? maxWidth; - - /// Maximum height of the image. - final double? maxHeight; - - /// Quality of the image. - final int? imageQuality; - - /// Preferred camera device to use. - final CameraDevice preferredCameraDevice; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - return OptionDrawer( - child: EndOfFrameCallbackWidget( - child: StreamSvgIcon( - size: 240, - icon: StreamSvgIcons.camera, - color: theme.colorTheme.disabled, - ), - onEndOfFrame: (_) async { - final pickedImage = await runInPermissionRequestLock(() { - return StreamAttachmentHandler.instance.pickImage( - source: source, - maxWidth: maxWidth, - maxHeight: maxHeight, - imageQuality: imageQuality, - preferredCameraDevice: preferredCameraDevice, - ); - }); - - onImagePicked.call(pickedImage); - }, - errorBuilder: (context, error, stacktrace) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - StreamSvgIcon( - size: 240, - icon: StreamSvgIcons.camera, - color: theme.colorTheme.disabled, - ), - Text( - context.translations.enablePhotoAndVideoAccessMessage, - style: theme.textTheme.body.copyWith( - color: theme.colorTheme.textLowEmphasis, - ), - textAlign: TextAlign.center, - ), - const SizedBox(height: 8), - TextButton( - onPressed: PhotoManager.openSetting, - child: Text( - context.translations.allowGalleryAccessMessage, - style: theme.textTheme.bodyBold.copyWith( - color: theme.colorTheme.accentPrimary, - ), - ), - ), - ], - ); - }, - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_poll_creator.dart b/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_poll_creator.dart deleted file mode 100644 index ecf95fe96d..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_poll_creator.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Widget used to create a poll. -class StreamPollCreator extends StatelessWidget { - /// Creates a [StreamPollCreator] widget. - const StreamPollCreator({ - super.key, - this.poll, - this.config, - this.onPollCreated, - }); - - /// The initial poll to be used in the poll creator. - final Poll? poll; - - /// The configuration used to validate the poll. - final PollConfig? config; - - /// Callback called when a poll is created. - final ValueSetter? onPollCreated; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - - Future _openCreatePollFlow() async { - final result = await showStreamPollCreatorDialog( - context: context, - poll: poll, - config: config, - ); - - onPollCreated?.call(result); - } - - return OptionDrawer( - child: EndOfFrameCallbackWidget( - child: StreamSvgIcon( - size: 180, - icon: StreamSvgIcons.polls, - color: theme.colorTheme.disabled, - ), - onEndOfFrame: (_) => _openCreatePollFlow(), - errorBuilder: (context, error, stacktrace) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - StreamSvgIcon( - size: 240, - icon: StreamSvgIcons.polls, - color: theme.colorTheme.disabled, - ), - const SizedBox(height: 8), - TextButton( - onPressed: _openCreatePollFlow, - child: Text( - context.translations.createPollLabel(isNew: true), - style: theme.textTheme.bodyBold.copyWith( - color: theme.colorTheme.accentPrimary, - ), - ), - ), - ], - ); - }, - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_video_picker.dart b/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_video_picker.dart deleted file mode 100644 index 4ba4e8f7b6..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/options/stream_video_picker.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:image_picker/image_picker.dart'; -import 'package:photo_manager/photo_manager.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Widget used to capture video using the device camera. -class StreamVideoPicker extends StatelessWidget { - /// Creates a [StreamVideoPicker] widget. - const StreamVideoPicker({ - super.key, - required this.onVideoPicked, - this.source = ImageSource.camera, - this.preferredCameraDevice = CameraDevice.rear, - this.maxDuration, - }); - - /// Callback called when a video is picked. - final ValueSetter onVideoPicked; - - /// Source of the video to pick. - final ImageSource source; - - /// Preferred camera device to use. - final CameraDevice preferredCameraDevice; - - /// Maximum duration of the video. - final Duration? maxDuration; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - return OptionDrawer( - child: EndOfFrameCallbackWidget( - child: StreamSvgIcon( - size: 240, - icon: StreamSvgIcons.record, - color: theme.colorTheme.disabled, - ), - onEndOfFrame: (_) async { - final pickedVideo = await runInPermissionRequestLock(() { - return StreamAttachmentHandler.instance.pickVideo( - source: source, - preferredCameraDevice: preferredCameraDevice, - maxDuration: maxDuration, - ); - }); - - onVideoPicked.call(pickedVideo); - }, - errorBuilder: (context, error, stacktrace) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - StreamSvgIcon( - size: 240, - icon: StreamSvgIcons.record, - color: theme.colorTheme.disabled, - ), - Text( - context.translations.enablePhotoAndVideoAccessMessage, - style: theme.textTheme.body.copyWith( - color: theme.colorTheme.textLowEmphasis, - ), - textAlign: TextAlign.center, - ), - const SizedBox(height: 8), - TextButton( - onPressed: PhotoManager.openSetting, - child: Text( - context.translations.allowGalleryAccessMessage, - style: theme.textTheme.bodyBold.copyWith( - color: theme.colorTheme.accentPrimary, - ), - ), - ), - ], - ); - }, - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/stream_attachment_picker.dart b/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/stream_attachment_picker.dart deleted file mode 100644 index 50d7e9c1a8..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/stream_attachment_picker.dart +++ /dev/null @@ -1,963 +0,0 @@ -import 'dart:async'; - -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/message_input/attachment_picker/options/options.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// The default maximum size for media attachments. -const kDefaultMaxAttachmentSize = 100 * 1024 * 1024; // 100MB in Bytes - -/// The default maximum number of media attachments. -const kDefaultMaxAttachmentCount = 10; - -/// Value class for [AttachmentPickerController]. -/// -/// This class holds the list of [Poll] and [Attachment] objects. -class AttachmentPickerValue { - /// Creates a new instance of [AttachmentPickerValue]. - const AttachmentPickerValue({ - this.poll, - this.attachments = const [], - }); - - /// The poll object. - final Poll? poll; - - /// The list of [Attachment] objects. - final List attachments; - - /// Returns a copy of this object with the provided values. - AttachmentPickerValue copyWith({ - Poll? poll, - List? attachments, - }) { - return AttachmentPickerValue( - poll: poll ?? this.poll, - attachments: attachments ?? this.attachments, - ); - } -} - -/// Controller class for [StreamAttachmentPicker]. -class StreamAttachmentPickerController - extends ValueNotifier { - /// Creates a new instance of [StreamAttachmentPickerController]. - StreamAttachmentPickerController({ - this.initialPoll, - this.initialAttachments, - this.maxAttachmentSize = kDefaultMaxAttachmentSize, - this.maxAttachmentCount = kDefaultMaxAttachmentCount, - }) : assert( - (initialAttachments?.length ?? 0) <= maxAttachmentCount, - '''The initial attachments count must be less than or equal to maxAttachmentCount''', - ), - super( - AttachmentPickerValue( - poll: initialPoll, - attachments: initialAttachments ?? const [], - ), - ); - - /// The max attachment size allowed in bytes. - final int maxAttachmentSize; - - /// The max attachment count allowed. - final int maxAttachmentCount; - - /// The initial poll. - final Poll? initialPoll; - - /// The initial attachments. - final List? initialAttachments; - - @override - set value(AttachmentPickerValue newValue) { - if (newValue.attachments.length > maxAttachmentCount) { - throw ArgumentError( - 'The maximum number of attachments is $maxAttachmentCount.', - ); - } - super.value = newValue; - } - - /// Adds a new [poll] to the message. - set poll(Poll poll) { - value = value.copyWith(poll: poll); - } - - Future _saveToCache(AttachmentFile file) async { - // Cache the attachment in a temporary file. - return StreamAttachmentHandler.instance.saveAttachmentFile( - attachmentFile: file, - ); - } - - Future _removeFromCache(AttachmentFile file) { - // Remove the cached attachment file. - return StreamAttachmentHandler.instance.deleteAttachmentFile( - attachmentFile: file, - ); - } - - /// Adds a new attachment to the message. - Future addAttachment(Attachment attachment) async { - assert(attachment.fileSize != null, ''); - if (attachment.fileSize! > maxAttachmentSize) { - throw ArgumentError( - 'The size of the attachment is ${attachment.fileSize} bytes, ' - 'but the maximum size allowed is $maxAttachmentSize bytes.', - ); - } - - final file = attachment.file; - final uploadState = attachment.uploadState; - - // No need to cache the attachment if it's already uploaded - // or we are on web. - if (file == null || uploadState.isSuccess || isWeb) { - value = value.copyWith(attachments: [...value.attachments, attachment]); - return; - } - - // Cache the attachment in a temporary file. - final tempFilePath = await _saveToCache(file); - - value = value.copyWith(attachments: [ - ...value.attachments, - attachment.copyWith( - file: file.copyWith( - path: tempFilePath, - ), - ), - ]); - } - - /// Removes the specified [attachment] from the message. - Future removeAttachment(Attachment attachment) async { - final file = attachment.file; - final uploadState = attachment.uploadState; - - if (file == null || uploadState.isSuccess || isWeb) { - final updatedAttachments = [...value.attachments]..remove(attachment); - value = value.copyWith(attachments: updatedAttachments); - return; - } - - // Remove the cached attachment file. - await _removeFromCache(file); - - final updatedAttachments = [...value.attachments]..remove(attachment); - value = value.copyWith(attachments: updatedAttachments); - } - - /// Remove the attachment with the given [attachmentId]. - void removeAttachmentById(String attachmentId) { - final attachment = value.attachments.firstWhereOrNull( - (attachment) => attachment.id == attachmentId, - ); - - if (attachment == null) return; - - removeAttachment(attachment); - } - - /// Clears all the attachments. - Future clear() async { - final attachments = [...value.attachments]; - for (final attachment in attachments) { - final file = attachment.file; - final uploadState = attachment.uploadState; - - if (file == null || uploadState.isSuccess || isWeb) continue; - - await _removeFromCache(file); - } - value = const AttachmentPickerValue(); - } - - /// Resets the controller to its initial state. - Future reset() async { - final attachments = [...value.attachments]; - for (final attachment in attachments) { - final file = attachment.file; - final uploadState = attachment.uploadState; - - if (file == null || uploadState.isSuccess || isWeb) continue; - - await _removeFromCache(file); - } - - value = AttachmentPickerValue( - poll: initialPoll, - attachments: initialAttachments ?? const [], - ); - } -} - -/// The possible picker types of the attachment picker. -enum AttachmentPickerType { - /// The attachment picker will only allow to pick images. - images, - - /// The attachment picker will only allow to pick videos. - videos, - - /// The attachment picker will only allow to pick audios. - audios, - - /// The attachment picker will only allow to pick files or documents. - files, - - /// The attachment picker will only allow to create poll. - poll, -} - -/// Function signature for building the attachment picker option view. -typedef AttachmentPickerOptionViewBuilder = Widget Function( - BuildContext context, - StreamAttachmentPickerController controller, -); - -/// Model class for the attachment picker options. -class AttachmentPickerOption { - /// Creates a new instance of [AttachmentPickerOption]. - const AttachmentPickerOption({ - this.key, - required this.supportedTypes, - required this.icon, - this.title, - this.optionViewBuilder, - }); - - /// A key to identify the option. - final String? key; - - /// The icon of the option. - final Widget icon; - - /// The title of the option. - final String? title; - - /// The supported types of the option. - final Iterable supportedTypes; - - /// The option view builder. - final AttachmentPickerOptionViewBuilder? optionViewBuilder; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is AttachmentPickerOption && - runtimeType == other.runtimeType && - key == other.key && - const IterableEquality().equals(supportedTypes, other.supportedTypes); - - @override - int get hashCode => - key.hashCode ^ const IterableEquality().hash(supportedTypes); -} - -/// The attachment picker option for the web or desktop platforms. -class WebOrDesktopAttachmentPickerOption extends AttachmentPickerOption { - /// Creates a new instance of [WebOrDesktopAttachmentPickerOption]. - WebOrDesktopAttachmentPickerOption({ - super.key, - required AttachmentPickerType type, - required super.icon, - required super.title, - }) : super(supportedTypes: [type]); - - /// Creates a new instance of [WebOrDesktopAttachmentPickerOption] from - /// [option]. - factory WebOrDesktopAttachmentPickerOption.fromAttachmentPickerOption( - AttachmentPickerOption option, - ) { - return WebOrDesktopAttachmentPickerOption( - key: option.key, - type: option.supportedTypes.first, - icon: option.icon, - title: option.title, - ); - } - - @override - String get title => super.title!; - - /// Type of the option. - AttachmentPickerType get type => supportedTypes.first; -} - -/// Helpful extensions for [StreamAttachmentPickerController]. -extension AttachmentPickerOptionTypeX on StreamAttachmentPickerController { - /// Returns the list of available attachment picker options. - Set get currentAttachmentPickerTypes { - final attach = value.attachments; - final containsImage = attach.any((it) => it.type == AttachmentType.image); - final containsVideo = attach.any((it) => it.type == AttachmentType.video); - final containsAudio = attach.any((it) => it.type == AttachmentType.audio); - final containsFile = attach.any((it) => it.type == AttachmentType.file); - final containsPoll = value.poll != null; - - return { - if (containsImage) AttachmentPickerType.images, - if (containsVideo) AttachmentPickerType.videos, - if (containsAudio) AttachmentPickerType.audios, - if (containsFile) AttachmentPickerType.files, - if (containsPoll) AttachmentPickerType.poll, - }; - } - - /// Returns the list of enabled picker types. - Set filterEnabledTypes({ - required Iterable options, - }) { - final availableTypes = currentAttachmentPickerTypes; - final enabledTypes = {}; - for (final option in options) { - final supportedTypes = option.supportedTypes; - if (availableTypes.any(supportedTypes.contains)) { - enabledTypes.addAll(supportedTypes); - } - } - return enabledTypes; - } - - /// Returns true if the [initialAttachments] are changed. - bool get isValueChanged { - final isPollEqual = value.poll == initialPoll; - final areAttachmentsEqual = UnorderedIterableEquality( - EqualityBy((attachment) => attachment.id), - ).equals(value.attachments, initialAttachments); - - return !isPollEqual || !areAttachmentsEqual; - } -} - -/// Function signature for the callback when the web or desktop attachment -/// picker option gets tapped. -typedef OnWebOrDesktopAttachmentPickerOptionTap = void Function( - BuildContext context, - StreamAttachmentPickerController controller, - WebOrDesktopAttachmentPickerOption option, -); - -/// Bottom sheet widget for the web or desktop version of the attachment picker. -class StreamWebOrDesktopAttachmentPickerBottomSheet extends StatelessWidget { - /// Creates a new instance of [StreamWebOrDesktopAttachmentPickerBottomSheet]. - const StreamWebOrDesktopAttachmentPickerBottomSheet({ - super.key, - required this.options, - required this.controller, - this.onOptionTap, - }); - - /// The list of options. - final Set options; - - /// The controller of the attachment picker. - final StreamAttachmentPickerController controller; - - /// The callback when the option gets tapped. - final OnWebOrDesktopAttachmentPickerOptionTap? onOptionTap; - - @override - Widget build(BuildContext context) { - final enabledTypes = controller.filterEnabledTypes(options: options); - return ListView( - shrinkWrap: true, - children: [ - ...options.map((option) { - VoidCallback? onOptionTap; - if (this.onOptionTap != null) { - onOptionTap = () { - this.onOptionTap?.call(context, controller, option); - }; - } - - final enabled = enabledTypes.isEmpty || - enabledTypes.any((it) => it == option.type); - - return ListTile( - enabled: enabled, - leading: option.icon, - title: Text(option.title), - onTap: onOptionTap, - ); - }), - ], - ); - } -} - -/// Bottom sheet widget for the mobile version of the attachment picker. -class StreamMobileAttachmentPickerBottomSheet extends StatefulWidget { - /// Creates a new instance of [StreamMobileAttachmentPickerBottomSheet]. - const StreamMobileAttachmentPickerBottomSheet({ - super.key, - required this.options, - required this.controller, - this.initialOption, - this.onSendValue, - }); - - /// The list of options. - final Set options; - - /// The initial option to be selected. - final AttachmentPickerOption? initialOption; - - /// The controller of the attachment picker. - final StreamAttachmentPickerController controller; - - /// The callback when the send button gets tapped. - final ValueSetter? onSendValue; - - @override - State createState() => - _StreamMobileAttachmentPickerBottomSheetState(); -} - -class _StreamMobileAttachmentPickerBottomSheetState - extends State { - late AttachmentPickerOption _currentOption; - - @override - void initState() { - super.initState(); - if (widget.initialOption == null) { - final enabledTypes = widget.controller.filterEnabledTypes( - options: widget.options, - ); - if (enabledTypes.isNotEmpty) { - _currentOption = widget.options.firstWhere((it) { - return it.supportedTypes.contains(enabledTypes.first); - }); - } else { - _currentOption = widget.options.first; - } - } else { - _currentOption = widget.initialOption!; - } - } - - @override - Widget build(BuildContext context) { - return ValueListenableBuilder( - valueListenable: widget.controller, - builder: (context, attachments, _) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - _AttachmentPickerOptions( - controller: widget.controller, - options: widget.options, - currentOption: _currentOption, - onSendValue: widget.onSendValue, - onOptionSelected: (option) async { - setState(() => _currentOption = option); - }, - ), - Expanded( - child: _currentOption.optionViewBuilder - ?.call(context, widget.controller) ?? - const SizedBox.shrink(), - ), - ], - ); - }, - ); - } -} - -class _AttachmentPickerOptions extends StatelessWidget { - const _AttachmentPickerOptions({ - required this.options, - required this.currentOption, - required this.controller, - this.onOptionSelected, - this.onSendValue, - }); - - final Iterable options; - final AttachmentPickerOption currentOption; - final StreamAttachmentPickerController controller; - final ValueSetter? onOptionSelected; - final ValueSetter? onSendValue; - - @override - Widget build(BuildContext context) { - final colorTheme = StreamChatTheme.of(context).colorTheme; - return ValueListenableBuilder( - valueListenable: controller, - builder: (context, attachments, __) { - final enabledTypes = controller.filterEnabledTypes(options: options); - return Row( - children: [ - Expanded( - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - children: [ - ...options.map( - (option) { - final supportedTypes = option.supportedTypes; - - final isSelected = option == currentOption; - final isEnabled = enabledTypes.isEmpty || - enabledTypes.any(supportedTypes.contains); - - final color = isSelected - ? colorTheme.accentPrimary - : colorTheme.textLowEmphasis; - - final onPressed = - isEnabled ? () => onOptionSelected!(option) : null; - - return IconButton( - color: color, - disabledColor: colorTheme.disabled, - icon: option.icon, - onPressed: onPressed, - ); - }, - ), - ], - ), - ), - ), - Builder( - builder: (context) { - final VoidCallback? onPressed; - if (onSendValue != null && controller.isValueChanged) { - onPressed = () => onSendValue!(attachments); - } else { - onPressed = null; - } - - return IconButton( - color: colorTheme.accentPrimary, - disabledColor: colorTheme.disabled, - icon: const StreamSvgIcon( - icon: StreamSvgIcons.emptyCircleRight, - ), - onPressed: onPressed, - ); - }, - ), - ], - ); - }, - ); - } -} - -/// Signature used by [EndOfFrameCallbackWidget.errorBuilder] to create a -/// replacement widget to render. -typedef EndOfFrameCallbackErrorWidgetBuilder = Widget Function( - BuildContext context, - Object error, - StackTrace? stackTrace, -); - -/// Function signature for a callback that is called when the end of the frame -/// is reached. -typedef EndOfFrameCallback = FutureOr Function(BuildContext context); - -/// A widget that calls the given [callback] when the end of the frame is -/// reached. -class EndOfFrameCallbackWidget extends StatefulWidget { - /// Creates a new instance of [EndOfFrameCallbackWidget]. - const EndOfFrameCallbackWidget({ - super.key, - required this.onEndOfFrame, - this.child, - this.errorBuilder, - }); - - /// The widget below this widget in the tree. - final Widget? child; - - /// The callback that is called when the end of the frame is reached.x - final EndOfFrameCallback onEndOfFrame; - - /// The callback that will be called if the [onEndOfFrame] callback throws an - /// error. - final EndOfFrameCallbackErrorWidgetBuilder? errorBuilder; - - @override - State createState() => - _EndOfFrameCallbackWidgetState(); -} - -class _EndOfFrameCallbackWidgetState extends State { - Object? _error; - StackTrace? _stackTrace; - - @override - void initState() { - super.initState(); - WidgetsBinding.instance.endOfFrame.then((_) async { - if (mounted) { - try { - await widget.onEndOfFrame(context); - } catch (error, stackTrace) { - setState(() { - _error = error; - _stackTrace = stackTrace; - }); - } - } - }); - } - - @override - Widget build(BuildContext context) { - final error = _error; - final stackTrace = _stackTrace; - - if (error != null) { - final errorBuilder = widget.errorBuilder; - if (errorBuilder != null) { - return errorBuilder(context, error, stackTrace); - } - return const Text('An error occurred'); - } - - // Reset the error and stack trace so that we don't keep showing the same - // error over and over. - _error = null; - _stackTrace = null; - - return widget.child ?? const SizedBox.shrink(); - } -} - -const _kDefaultOptionDrawerShape = RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16), - ), -); - -/// A widget that will be shown in the attachment picker. -/// It can be used to show a custom view for each attachment picker option. -class OptionDrawer extends StatelessWidget { - /// Creates a widget that will be shown in the attachment picker. - const OptionDrawer({ - super.key, - required this.child, - this.color, - this.elevation = 2, - this.margin = EdgeInsets.zero, - this.clipBehavior = Clip.hardEdge, - this.shape = _kDefaultOptionDrawerShape, - this.title, - this.actions = const [], - }); - - /// The widget below this widget in the tree. - final Widget child; - - /// The background color of the options card. - /// - /// Defaults to [StreamColorTheme.barsBg]. - final Color? color; - - /// The elevation of the options card. - /// - /// The default value is 2. - final double elevation; - - /// The margin of the options card. - /// - /// The default value is [EdgeInsets.zero]. - final EdgeInsetsGeometry margin; - - /// The clip behavior of the options card. - /// - /// The default value is [Clip.hardEdge]. - final Clip clipBehavior; - - /// The shape of the options card. - final ShapeBorder shape; - - /// The title of the options card. - final Widget? title; - - /// The actions available for the options card. - final List actions; - - @override - Widget build(BuildContext context) { - final colorTheme = StreamChatTheme.of(context).colorTheme; - - var height = 20.0; - if (title != null || actions.isNotEmpty) { - height = 40.0; - } - - final leading = title ?? const SizedBox.shrink(); - - Widget trailing; - if (actions.isNotEmpty) { - trailing = Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: actions, - ); - } else { - trailing = const SizedBox.shrink(); - } - - return Card( - elevation: elevation, - color: color ?? colorTheme.barsBg, - margin: margin, - shape: shape, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - height: height, - child: Row( - children: [ - Expanded(child: leading), - Container( - height: 4, - width: 40, - decoration: BoxDecoration( - color: colorTheme.disabled, - borderRadius: BorderRadius.circular(6), - ), - ), - Expanded(child: trailing), - ], - ), - ), - Expanded(child: child), - ], - ), - ); - } -} - -/// Returns the mobile version of the attachment picker. -Widget mobileAttachmentPickerBuilder({ - required BuildContext context, - required StreamAttachmentPickerController controller, - Poll? initialPoll, - PollConfig? pollConfig, - Iterable? customOptions, - List allowedTypes = AttachmentPickerType.values, - ThumbnailSize attachmentThumbnailSize = const ThumbnailSize(400, 400), - ThumbnailFormat attachmentThumbnailFormat = ThumbnailFormat.jpeg, - int attachmentThumbnailQuality = 100, - double attachmentThumbnailScale = 1, - ErrorListener? onError, -}) { - return StreamMobileAttachmentPickerBottomSheet( - controller: controller, - onSendValue: Navigator.of(context).pop, - options: { - ...{ - if (customOptions != null) ...customOptions, - AttachmentPickerOption( - key: 'gallery-picker', - icon: const StreamSvgIcon(icon: StreamSvgIcons.pictures), - supportedTypes: [ - AttachmentPickerType.images, - AttachmentPickerType.videos, - ], - optionViewBuilder: (context, controller) { - final attachment = controller.value.attachments; - final selectedIds = attachment.map((it) => it.id); - return StreamGalleryPicker( - selectedMediaItems: selectedIds, - mediaThumbnailSize: attachmentThumbnailSize, - mediaThumbnailFormat: attachmentThumbnailFormat, - mediaThumbnailQuality: attachmentThumbnailQuality, - mediaThumbnailScale: attachmentThumbnailScale, - onMediaItemSelected: (media) async { - try { - if (selectedIds.contains(media.id)) { - return await controller.removeAssetAttachment(media); - } - return await controller.addAssetAttachment(media); - } catch (e, stk) { - if (onError != null) return onError.call(e, stk); - rethrow; - } - }, - ); - }, - ), - AttachmentPickerOption( - key: 'file-picker', - icon: const StreamSvgIcon(icon: StreamSvgIcons.files), - supportedTypes: [AttachmentPickerType.files], - optionViewBuilder: (context, controller) { - return StreamFilePicker( - onFilePicked: (file) async { - try { - if (file != null) await controller.addAttachment(file); - return Navigator.pop(context, controller.value); - } catch (e, stk) { - Navigator.pop(context, controller.value); - if (onError != null) return onError.call(e, stk); - - rethrow; - } - }, - ); - }, - ), - AttachmentPickerOption( - key: 'image-picker', - icon: const StreamSvgIcon(icon: StreamSvgIcons.camera), - supportedTypes: [AttachmentPickerType.images], - optionViewBuilder: (context, controller) { - return StreamImagePicker( - onImagePicked: (image) async { - try { - if (image != null) { - await controller.addAttachment(image); - } - return Navigator.pop(context, controller.value); - } catch (e, stk) { - Navigator.pop(context, controller.value); - if (onError != null) return onError.call(e, stk); - - rethrow; - } - }, - ); - }, - ), - AttachmentPickerOption( - key: 'video-picker', - icon: const StreamSvgIcon(icon: StreamSvgIcons.record), - supportedTypes: [AttachmentPickerType.videos], - optionViewBuilder: (context, controller) { - return StreamVideoPicker( - onVideoPicked: (video) async { - try { - if (video != null) { - await controller.addAttachment(video); - } - return Navigator.pop(context, controller.value); - } catch (e, stk) { - Navigator.pop(context, controller.value); - if (onError != null) return onError.call(e, stk); - - rethrow; - } - }, - ); - }, - ), - AttachmentPickerOption( - key: 'poll-creator', - icon: const StreamSvgIcon(icon: StreamSvgIcons.polls), - supportedTypes: [AttachmentPickerType.poll], - optionViewBuilder: (context, controller) { - final initialPoll = controller.value.poll; - return StreamPollCreator( - poll: initialPoll, - config: pollConfig, - onPollCreated: (poll) { - try { - if (poll != null) controller.poll = poll; - return Navigator.pop(context, controller.value); - } catch (e, stk) { - Navigator.pop(context, controller.value); - if (onError != null) return onError.call(e, stk); - - rethrow; - } - }, - ); - }, - ), - }.where((option) => option.supportedTypes.every(allowedTypes.contains)), - }, - ); -} - -/// Returns the web or desktop version of the attachment picker. -Widget webOrDesktopAttachmentPickerBuilder({ - required BuildContext context, - required StreamAttachmentPickerController controller, - Poll? initialPoll, - PollConfig? pollConfig, - Iterable? customOptions, - List allowedTypes = AttachmentPickerType.values, - ThumbnailSize attachmentThumbnailSize = const ThumbnailSize(400, 400), - ThumbnailFormat attachmentThumbnailFormat = ThumbnailFormat.jpeg, - int attachmentThumbnailQuality = 100, - double attachmentThumbnailScale = 1, - ErrorListener? onError, -}) { - return StreamWebOrDesktopAttachmentPickerBottomSheet( - controller: controller, - options: { - ...{ - if (customOptions != null) ...customOptions, - WebOrDesktopAttachmentPickerOption( - key: 'image-picker', - type: AttachmentPickerType.images, - icon: const StreamSvgIcon(icon: StreamSvgIcons.pictures), - title: context.translations.uploadAPhotoLabel, - ), - WebOrDesktopAttachmentPickerOption( - key: 'video-picker', - type: AttachmentPickerType.videos, - icon: const StreamSvgIcon(icon: StreamSvgIcons.record), - title: context.translations.uploadAVideoLabel, - ), - WebOrDesktopAttachmentPickerOption( - key: 'file-picker', - type: AttachmentPickerType.files, - icon: const StreamSvgIcon(icon: StreamSvgIcons.files), - title: context.translations.uploadAFileLabel, - ), - WebOrDesktopAttachmentPickerOption( - key: 'poll-creator', - type: AttachmentPickerType.poll, - icon: const StreamSvgIcon(icon: StreamSvgIcons.polls), - title: context.translations.createPollLabel(isNew: true), - ), - }.where((option) => option.supportedTypes.every(allowedTypes.contains)), - }, - onOptionTap: (context, controller, option) async { - // Handle the polls type option separately - if (option.type case AttachmentPickerType.poll) { - final poll = await showStreamPollCreatorDialog( - context: context, - poll: initialPoll, - config: pollConfig, - ); - - if (poll != null) controller.poll = poll; - return Navigator.pop(context, controller.value); - } - - // Handle the remaining option types. - try { - final attachment = await StreamAttachmentHandler.instance.pickFile( - type: option.type.fileType, - ); - if (attachment != null) { - await controller.addAttachment(attachment); - } - return Navigator.pop(context, controller.value); - } catch (e, stk) { - Navigator.pop(context, controller.value); - if (onError != null) return onError.call(e, stk); - - rethrow; - } - }, - ); -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/stream_attachment_picker_bottom_sheet.dart b/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/stream_attachment_picker_bottom_sheet.dart deleted file mode 100644 index d56a7047d4..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/attachment_picker/stream_attachment_picker_bottom_sheet.dart +++ /dev/null @@ -1,254 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/foundation.dart' show kIsWeb, defaultTargetPlatform; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Shows a modal material design bottom sheet. -/// -/// A modal bottom sheet is an alternative to a menu or a dialog and prevents -/// the user from interacting with the rest of the app. -/// -/// A closely related widget is a persistent bottom sheet, which shows -/// information that supplements the primary content of the app without -/// preventing the use from interacting with the app. Persistent bottom sheets -/// can be created and displayed with the [showBottomSheet] function or the -/// [ScaffoldState.showBottomSheet] method. -/// -/// The `context` argument is used to look up the [Navigator] and [Theme] for -/// the bottom sheet. It is only used when the method is called. Its -/// corresponding widget can be safely removed from the tree before the bottom -/// sheet is closed. -/// -/// The `isScrollControlled` parameter specifies whether this is a route for -/// a bottom sheet that will utilize [DraggableScrollableSheet]. If you wish -/// to have a bottom sheet that has a scrollable child such as a [ListView] or -/// a [GridView] and have the bottom sheet be draggable, you should set this -/// parameter to true. -/// -/// The `useRootNavigator` parameter ensures that the root navigator is used to -/// display the [BottomSheet] when set to `true`. This is useful in the case -/// that a modal [BottomSheet] needs to be displayed above all other content -/// but the caller is inside another [Navigator]. -/// -/// The [isDismissible] parameter specifies whether the bottom sheet will be -/// dismissed when user taps on the scrim. -/// -/// The [enableDrag] parameter specifies whether the bottom sheet can be -/// dragged up and down and dismissed by swiping downwards. -/// -/// The optional [backgroundColor], [elevation], [shape], [clipBehavior], -/// [constraints] and [transitionAnimationController] -/// parameters can be passed in to customize the appearance and behavior of -/// modal bottom sheets (see the documentation for these on [BottomSheet] -/// for more details). -/// -/// The [transitionAnimationController] controls the bottom sheet's entrance and -/// exit animations if provided. -/// -/// The optional `routeSettings` parameter sets the [RouteSettings] -/// of the modal bottom sheet sheet. -/// This is particularly useful in the case that a user wants to observe -/// [PopupRoute]s within a [NavigatorObserver]. -/// -/// Returns a `Future` that resolves to the value (if any) that was passed to -/// [Navigator.pop] when the modal bottom sheet was closed. -/// -/// See also: -/// -/// * [BottomSheet], which becomes the parent of the widget returned by the -/// function passed as the `builder` argument to [showModalBottomSheet]. -/// * [showBottomSheet] and [ScaffoldState.showBottomSheet], for showing -/// non-modal bottom sheets. -/// * [DraggableScrollableSheet], which allows you to create a bottom sheet -/// that grows and then becomes scrollable once it reaches its maximum size. -/// * -Future showStreamAttachmentPickerModalBottomSheet({ - required BuildContext context, - Iterable? customOptions, - List allowedTypes = AttachmentPickerType.values, - Poll? initialPoll, - PollConfig? pollConfig, - List? initialAttachments, - StreamAttachmentPickerController? controller, - ErrorListener? onError, - Color? backgroundColor, - double? elevation, - BoxConstraints? constraints, - Color? barrierColor, - bool isScrollControlled = false, - bool useRootNavigator = false, - bool isDismissible = true, - bool enableDrag = true, - bool useNativeAttachmentPickerOnMobile = false, - RouteSettings? routeSettings, - AnimationController? transitionAnimationController, - Clip? clipBehavior = Clip.hardEdge, - ShapeBorder? shape, - ThumbnailSize attachmentThumbnailSize = const ThumbnailSize(400, 400), - ThumbnailFormat attachmentThumbnailFormat = ThumbnailFormat.jpeg, - int attachmentThumbnailQuality = 100, - double attachmentThumbnailScale = 1, -}) { - final colorTheme = StreamChatTheme.of(context).colorTheme; - final color = backgroundColor ?? colorTheme.inputBg; - - return showModalBottomSheet( - context: context, - backgroundColor: color, - elevation: elevation, - shape: shape, - clipBehavior: clipBehavior, - constraints: constraints, - barrierColor: barrierColor, - isScrollControlled: isScrollControlled, - useRootNavigator: useRootNavigator, - isDismissible: isDismissible, - enableDrag: enableDrag, - routeSettings: routeSettings, - transitionAnimationController: transitionAnimationController, - builder: (BuildContext context) { - return StreamPlatformAttachmentPickerBottomSheetBuilder( - controller: controller, - initialPoll: initialPoll, - initialAttachments: initialAttachments, - builder: (context, controller, child) { - final currentPlatform = defaultTargetPlatform; - final isWebOrDesktop = kIsWeb || - currentPlatform == TargetPlatform.macOS || - currentPlatform == TargetPlatform.linux || - currentPlatform == TargetPlatform.windows; - - if (isWebOrDesktop || useNativeAttachmentPickerOnMobile) { - return webOrDesktopAttachmentPickerBuilder.call( - context: context, - onError: onError, - controller: controller, - allowedTypes: allowedTypes, - customOptions: customOptions?.map( - WebOrDesktopAttachmentPickerOption.fromAttachmentPickerOption, - ), - initialPoll: initialPoll, - pollConfig: pollConfig, - attachmentThumbnailSize: attachmentThumbnailSize, - attachmentThumbnailFormat: attachmentThumbnailFormat, - attachmentThumbnailQuality: attachmentThumbnailQuality, - attachmentThumbnailScale: attachmentThumbnailScale, - ); - } - - return mobileAttachmentPickerBuilder.call( - context: context, - onError: onError, - controller: controller, - allowedTypes: allowedTypes, - customOptions: customOptions, - initialPoll: initialPoll, - pollConfig: pollConfig, - attachmentThumbnailSize: attachmentThumbnailSize, - attachmentThumbnailFormat: attachmentThumbnailFormat, - attachmentThumbnailQuality: attachmentThumbnailQuality, - attachmentThumbnailScale: attachmentThumbnailScale, - ); - }, - ); - }, - ); -} - -/// Builds the attachment picker bottom sheet. -class StreamPlatformAttachmentPickerBottomSheetBuilder extends StatefulWidget { - /// Creates a new instance of the widget. - const StreamPlatformAttachmentPickerBottomSheetBuilder({ - super.key, - this.customOptions, - this.initialPoll, - this.initialAttachments, - this.child, - this.controller, - required this.builder, - }); - - /// The child widget. - final Widget? child; - - /// Builder for the attachment picker bottom sheet. - final Widget Function( - BuildContext context, - StreamAttachmentPickerController controller, - Widget? child, - ) builder; - - /// The custom options to be displayed in the attachment picker. - final List? customOptions; - - /// The initial poll. - final Poll? initialPoll; - - /// The initial attachments. - final List? initialAttachments; - - /// The controller. - final StreamAttachmentPickerController? controller; - - @override - State createState() => - _StreamPlatformAttachmentPickerBottomSheetBuilderState(); -} - -class _StreamPlatformAttachmentPickerBottomSheetBuilderState - extends State { - late StreamAttachmentPickerController _controller; - - @override - void initState() { - super.initState(); - _controller = widget.controller ?? - StreamAttachmentPickerController( - initialPoll: widget.initialPoll, - initialAttachments: widget.initialAttachments, - ); - } - - // Handle a potential change in StreamAttachmentPickerController by properly - // disposing of the old one and setting up the new one, if needed. - void _updateAttachmentPickerController( - StreamAttachmentPickerController? old, - StreamAttachmentPickerController? current, - ) { - if ((old == null && current == null) || old == current) return; - if (old == null) { - _controller.dispose(); - _controller = current!; - } else if (current == null) { - _controller = StreamAttachmentPickerController( - initialPoll: widget.initialPoll, - initialAttachments: widget.initialAttachments, - ); - } else { - _controller = current; - } - } - - @override - void didUpdateWidget( - StreamPlatformAttachmentPickerBottomSheetBuilder oldWidget, - ) { - super.didUpdateWidget(oldWidget); - _updateAttachmentPickerController( - oldWidget.controller, - widget.controller, - ); - } - - @override - void dispose() { - if (widget.controller == null) _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return widget.builder(context, _controller, widget.child); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/audio_recorder/audio_recorder_controller.dart b/packages/stream_chat_flutter/lib/src/message_input/audio_recorder/audio_recorder_controller.dart deleted file mode 100644 index f011cfe75d..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/audio_recorder/audio_recorder_controller.dart +++ /dev/null @@ -1,284 +0,0 @@ -import 'dart:async'; - -import 'package:file_selector/file_selector.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/gestures.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:record/record.dart'; -import 'package:stream_chat_flutter/src/audio/audio_sampling.dart' as sampling; -import 'package:stream_chat_flutter/src/message_input/audio_recorder/audio_recorder_state.dart'; -import 'package:stream_chat_flutter/src/utils/extensions.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// {@template streamAudioRecorderController} -/// A controller for recording audio tracks. It provides methods to start, -/// stop, cancel and finish the recording session. -/// -/// This controller uses the [AudioRecorder] to record audio tracks. It listens -/// to the recorder state changes and updates the [AudioRecorderState] -/// accordingly. -/// {@endtemplate} -class StreamAudioRecorderController extends ValueNotifier { - /// {@macro streamAudioRecorderController} - factory StreamAudioRecorderController({ - RecordConfig? config, - Duration amplitudeInterval = const Duration(milliseconds: 100), - }) { - return StreamAudioRecorderController.raw( - recorder: AudioRecorder(), - amplitudeInterval: amplitudeInterval, - config: switch (config) { - final config? => config, - _ => const RecordConfig( - numChannels: 1, - encoder: kIsWeb ? AudioEncoder.wav : AudioEncoder.aacLc, - ), - }, - ); - } - - /// {@macro streamAudioRecorderController} - @visibleForTesting - StreamAudioRecorderController.raw({ - required this.config, - required AudioRecorder recorder, - AudioRecorderState initialState = const RecordStateIdle(), - Duration amplitudeInterval = const Duration(milliseconds: 100), - }) : _recorder = recorder, - super(initialState) { - // Listen to the recorder amplitude changes - _recorderAmplitudeSubscription = _recorder - .onAmplitudeChanged(amplitudeInterval) // - .listen(_onRecorderAmplitudeChanged); - } - - /// The configuration for the recording session. - final RecordConfig config; - final AudioRecorder _recorder; - - /// Starts a new recording session. - Future startRecord() async { - // Only start the recorder if it is currently idle. - if (value case RecordStateIdle()) { - // Return if the recorder does not have permission to record audio. - final hasPermission = await _recorder.hasPermission(); - if (!hasPermission) return; - - // Start the recording session. - final tempPath = await _getOutputFilePath(config.encoder); - await _recorder.start(config, path: tempPath); - _startDurationTimer(); - - // Update the state to recording hold. - value = const RecordStateRecordingHold(); - } - } - - /// Stops the current recording session and returns the recorded audio track. - /// - /// Optionally, provide a [name] for the recorded audio attachment. - /// - /// Note: [name] only works on web platform. - Future stopRecord({String? name}) async { - // Only stop the recorder if it is currently recording. - if (value case final RecordStateRecording state) { - final path = await _recorder.stop(); - - // Stop the duration timer. - _durationTimer?.cancel(); - _durationTimer = null; - - if (path == null) throw Exception('Failed to stop the recorder'); - final fileName = name ?? 'audio.${config.encoder.extension}'; - final attachment = await state.toAttachment(path: path, name: fileName); - - // Update the state to stopped. - value = RecordStateStopped(audioRecording: attachment); - } - } - - /// Similar to [stopRecord] but does not update any state. Returns the - /// recorded audio track as an attachment. - /// - /// Optionally, you can provide a [name] for the attachment. - /// - /// Note: [name] only works on web platform. - Future finishRecord({String? name}) async { - // Return the audio recording directly if it is already stopped. - if (value case RecordStateStopped(audioRecording: final recording)) { - return recording; - } - - // Finish the recorder if it is currently recording. - if (value case final RecordStateRecording state) { - final path = await _recorder.stop(); - - // Stop the duration timer. - _durationTimer?.cancel(); - _durationTimer = null; - - if (path == null) throw Exception('Failed to stop the recorder'); - final fileName = name ?? 'audio.${config.encoder.extension}'; - final attachment = await state.toAttachment(path: path, name: fileName); - - return attachment; - } - - return null; - } - - /// Cancels the current recording session and discards the recorded track. - /// - /// Pass [discardTrack] as `false` to keep the recorded track, This is useful - /// when you want to cancel the recording session without losing the recorded - /// track. - Future cancelRecord({bool discardTrack = true}) async { - // Only cancel the recorder if it is currently recording or stopped. - if (value case RecordStateRecording() || RecordStateStopped()) { - if (discardTrack) await _recorder.cancel(); - - // Update the state to idle. - value = const RecordStateIdle(); - } - } - - /// Updates the current recording session in the locked state, no longer - /// requiring the user to hold the button. - void lockRecord() { - // Only lock the recorder if it is currently recording in the hold state. - if (value case final RecordStateRecordingHold state) { - // Update the state to recording locked. - value = RecordStateRecordingLocked( - duration: state.duration, - waveform: state.waveform, - ); - } - } - - /// Updates the drag offset of the recording session. - void dragRecord(Offset dragOffset) { - // Only update the offset if it is currently recording in the hold state. - if (value case final RecordStateRecordingHold state) { - // Update the drag offset. - value = state.copyWith(dragOffset: dragOffset); - } - } - - Timer? _infoTimer; - - /// Shows an info message to the user for the given [duration]. - /// - /// This is useful for showing messages like "Hold to record" or "Recording". - void showInfo( - String message, { - Duration duration = const Duration(seconds: 3), - }) { - // Only show the info message if the recorder is currently idle. - if (value case final RecordStateIdle state) { - // Do not show the same message if it is already being shown. - if (state.message == message) return; - - // Cancel the previous info timer. - _infoTimer?.cancel(); - _infoTimer = null; - - // Update the state to show the info message. - value = RecordStateIdle(message: message); - - // Start a timer to hide the info message after the given duration. - _infoTimer = Timer(duration, () { - // Only hide the info message if it is still being shown. - if (value case RecordStateIdle()) value = const RecordStateIdle(); - }); - } - } - - Future _getOutputFilePath(AudioEncoder encoder) async { - // Ignored on web platform. - if (CurrentPlatform.isWeb) return ''; - - // Generate a temporary path for the audio recording. - final tempDir = await getTemporaryDirectory(); - final currentTimestamp = DateTime.now().millisecondsSinceEpoch; - return '${tempDir.path}/audio_$currentTimestamp.${encoder.extension}'; - } - - StreamSubscription? _recorderAmplitudeSubscription; - void _onRecorderAmplitudeChanged(Amplitude amplitude) { - // Only update the waveform if the recorder is currently recording. - if (value case final RecordStateRecording state) { - final normalizedAmplitude = amplitude.current.normalize(-60, 0); - final updatedWaveForm = [...state.waveform, normalizedAmplitude]; - value = state.copyWith(waveform: updatedWaveForm); - } - } - - Timer? _durationTimer; - void _startDurationTimer() { - _durationTimer ??= Timer.periodic(const Duration(seconds: 1), (_) { - if (value case final RecordStateRecording state) { - final updatedDuration = state.duration + const Duration(seconds: 1); - value = state.copyWith(duration: updatedDuration); - } - }); - } - - @override - void dispose() { - _durationTimer?.cancel(); - _durationTimer = null; - _recorderAmplitudeSubscription?.cancel(); - _recorder.dispose(); - super.dispose(); - } -} - -extension on RecordStateRecording { - /// Converts the current recording state to an attachment. - /// - /// Optionally, provide a [name] for the attachment. - /// - /// Note: [name] only works on web platform. - Future toAttachment({ - required String path, - String? name, - }) async { - final attachmentFile = await XFile(path, name: name).toAttachmentFile; - - final attachment = Attachment( - file: attachmentFile, - type: AttachmentType.voiceRecording, - extraData: { - 'duration': duration.inMilliseconds / 1000, - 'waveform_data': sampling.resampleWaveformData(waveform, 100), - }, - ); - - return attachment; - } -} - -extension on double { - /// Normalizes the value between the given [lowerBound] and [upperBound]. - double normalize(double lowerBound, double upperBound) { - if (this < lowerBound) return 0; - if (this >= upperBound) return 1; - - final delta = upperBound - lowerBound; - return ((this - lowerBound) / delta).abs(); - } -} - -extension on AudioEncoder { - /// Returns the file extension for the audio encoder. - String get extension { - return switch (this) { - AudioEncoder.opus => 'opus', - AudioEncoder.flac => 'flac', - AudioEncoder.wav => 'wav', - AudioEncoder.pcm16bits => 'pcm', - AudioEncoder.amrNb || AudioEncoder.amrWb => '3gp', - AudioEncoder.aacLc || AudioEncoder.aacEld || AudioEncoder.aacHe => 'm4a', - }; - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/audio_recorder/audio_recorder_state.dart b/packages/stream_chat_flutter/lib/src/message_input/audio_recorder/audio_recorder_state.dart deleted file mode 100644 index 4f1de25162..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/audio_recorder/audio_recorder_state.dart +++ /dev/null @@ -1,131 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// The state of the audio recorder. -sealed class AudioRecorderState { - const AudioRecorderState._(); -} - -/// {@template recordStateIdle} -/// The audio recorder is currently idle and not recording any audio track. -/// -/// Optionally, provide a [message] to display when the recorder is idle. -/// -/// For example, when the user has not long pressed the record button long -/// enough to start recording. -/// {@endtemplate} -final class RecordStateIdle extends AudioRecorderState { - /// {@macro recordStateIdle} - const RecordStateIdle({this.message}) : super._(); - - /// The optional message to display when the recorder is idle. - final String? message; -} - -/// {@template recordStateRecording} -/// The audio recorder is currently recording an audio track. -/// {@endtemplate} -sealed class RecordStateRecording extends AudioRecorderState { - /// {@macro recordStateRecording} - const RecordStateRecording({ - this.duration = Duration.zero, - this.waveform = const [], - }) : super._(); - - /// The current duration of the audio track being recorded. - /// - /// Defaults to [Duration.zero]. - final Duration duration; - - /// The waveform of the audio track being recorded. - /// - /// Defaults to an empty list. - final List waveform; - - /// Creates a copy of this [RecordStateRecording] but with the given fields - /// replaced by the new values. - RecordStateRecording copyWith({ - Duration? duration, - List? waveform, - }) { - return switch (this) { - RecordStateRecordingHold() => RecordStateRecordingHold( - duration: duration ?? this.duration, - waveform: waveform ?? this.waveform, - ), - RecordStateRecordingLocked() => RecordStateRecordingLocked( - duration: duration ?? this.duration, - waveform: waveform ?? this.waveform, - ), - }; - } -} - -/// {@template recordStateRecordingHold} -/// The audio recorder is currently recording an audio track in a hold state. -/// {@endtemplate} -final class RecordStateRecordingHold extends RecordStateRecording { - /// {@macro recordStateRecordingHold} - const RecordStateRecordingHold({ - super.duration = Duration.zero, - super.waveform = const [], - this.dragOffset = Offset.zero, - }); - - /// The drag offset of the recorder if it is being dragged. - /// - /// Defaults to [Offset.zero]. - final Offset dragOffset; - - /// Creates a copy of this [RecordStateRecordingHold] but with the given - /// fields replaced by the new values. - @override - RecordStateRecordingHold copyWith({ - String? path, - Duration? duration, - List? waveform, - Offset? dragOffset, - }) { - return RecordStateRecordingHold( - duration: duration ?? this.duration, - waveform: waveform ?? this.waveform, - dragOffset: dragOffset ?? this.dragOffset, - ); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is RecordStateRecordingHold && - runtimeType == other.runtimeType && - duration == other.duration && - waveform == other.waveform && - dragOffset == other.dragOffset; - - @override - int get hashCode => Object.hash(duration, waveform, dragOffset); -} - -/// {@template recordStateRecordingLocked} -/// The audio recorder is currently recording an audio track in a locked state. -/// {@endtemplate} -final class RecordStateRecordingLocked extends RecordStateRecording { - /// {@macro recordStateRecordingLocked} - const RecordStateRecordingLocked({ - super.duration = Duration.zero, - super.waveform = const [], - }); -} - -/// {@template recordStateStopped} -/// The audio recorder has stopped recording and has a recorded audio track. -/// {@endtemplate} -final class RecordStateStopped extends AudioRecorderState { - /// {@macro recordStateStopped} - const RecordStateStopped({ - required this.audioRecording, - }) : super._(); - - /// The audio recording that was recorded. - final Attachment audioRecording; -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/audio_recorder/stream_audio_recorder.dart b/packages/stream_chat_flutter/lib/src/message_input/audio_recorder/stream_audio_recorder.dart deleted file mode 100644 index 8a7e48a991..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/audio_recorder/stream_audio_recorder.dart +++ /dev/null @@ -1,1024 +0,0 @@ -import 'dart:math' as math; - -import 'package:flutter/material.dart'; -import 'package:flutter_portal/flutter_portal.dart'; -import 'package:stream_chat_flutter/src/audio/audio_playlist_controller.dart'; -import 'package:stream_chat_flutter/src/audio/audio_playlist_state.dart'; -import 'package:stream_chat_flutter/src/audio/audio_sampling.dart'; -import 'package:stream_chat_flutter/src/icons/stream_svg_icon.dart'; -import 'package:stream_chat_flutter/src/message_input/audio_recorder/audio_recorder_state.dart'; -import 'package:stream_chat_flutter/src/message_input/stream_message_input_icon_button.dart'; -import 'package:stream_chat_flutter/src/misc/audio_waveform.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter/src/utils/extensions.dart'; - -/// {@template audioRecorderBuilder} -/// A builder function for constructing the audio recorder UI. -/// -/// See also: -/// - [StreamAudioRecorderButton], which uses this builder function. -/// - [StreamAudioRecorderState], which provides the state of the recorder. -/// {@endtemplate} -typedef AudioRecorderBuilder = Widget Function( - BuildContext, - AudioRecorderState, - Widget, -); - -/// {@template streamAudioRecorderButton} -/// A configurable audio recording button with interactive states and gestures. -/// -/// Manages different recording states: idle, recording, locked, and stopped. -/// Provides fine-grained control over recording interactions through callbacks. -/// -/// {@tool snippet} -/// Basic usage example: -/// ```dart -/// StreamAudioRecorderButton( -/// recordState: _recordState, -/// onRecordStart: () => _startRecording(), -/// onRecordFinish: () => _finishRecording(), -/// ) -/// ``` -/// {@end-tool} -/// {@endtemplate} -class StreamAudioRecorderButton extends StatelessWidget { - /// {@macro streamAudioRecorderButton} - const StreamAudioRecorderButton({ - super.key, - required this.recordState, - this.onRecordStart, - this.onRecordPause, - this.onRecordResume, - this.onRecordDragUpdate, - this.onRecordCancel, - this.onRecordLock, - this.onRecordFinish, - this.onRecordStop, - this.onRecordStartCancel, - this.lockRecordThreshold = 50, - this.cancelRecordThreshold = 75, - }); - - /// The current state of the recorder. - /// - /// This is used to determine the icon and the behavior of the button. - final AudioRecorderState recordState; - - /// The callback to call when the recording is started. - final VoidCallback? onRecordStart; - - /// The callback to call when the recording is paused. - final VoidCallback? onRecordPause; - - /// The callback to call when the recording is resumed. - final VoidCallback? onRecordResume; - - /// The callback to call when the recording is canceled. - final VoidCallback? onRecordCancel; - - /// The callback to call when the recording is locked. - final VoidCallback? onRecordLock; - - /// The callback to call when the recording is finished. - final VoidCallback? onRecordFinish; - - /// The callback to call when the recording is stopped. - final VoidCallback? onRecordStop; - - /// The callback to call when the recorder will not end up starting. - /// - /// This is called when the recording is canceled before it starts. - final VoidCallback? onRecordStartCancel; - - /// The callback to call when the recording drag is updated. - final ValueSetter? onRecordDragUpdate; - - /// The threshold to lock the recording. - final double lockRecordThreshold; - - /// The threshold to cancel the recording. - final double cancelRecordThreshold; - - @override - Widget build(BuildContext context) { - final isRecording = recordState is! RecordStateIdle; - final isLocked = isRecording && recordState is! RecordStateRecordingHold; - - return GestureDetector( - onLongPressStart: (_) { - // Return if the recording is already started. - if (isRecording) return; - return onRecordStart?.call(); - }, - onLongPressEnd: (_) { - // Return if the recording not yet started or already locked. - if (!isRecording || isLocked) return; - return onRecordFinish?.call(); - }, - onLongPressCancel: () { - // Notify the parent that the recorder is canceled before it starts. - return onRecordStartCancel?.call(); - }, - onLongPressMoveUpdate: (details) { - // Return if the recording not yet started or already locked. - if (!isRecording || isLocked) return; - final dragOffset = details.offsetFromOrigin; - - // Lock recording if the drag offset is greater than the threshold. - if (dragOffset.dy <= -lockRecordThreshold) { - return onRecordLock?.call(); - } - // Cancel recording if the drag offset is greater than the threshold. - if (dragOffset.dx <= -cancelRecordThreshold) { - return onRecordCancel?.call(); - } - - // Update the drag offset. - return onRecordDragUpdate?.call(dragOffset); - }, - child: StreamAudioRecorder( - state: recordState, - button: RecordButton( - onPressed: () {}, // Allows showing ripple effect on tap. - icon: const StreamSvgIcon(icon: StreamSvgIcons.mic), - ), - builder: (context, state, recordButton) => switch (state) { - // Show only the record button if the recording is not in progress. - RecordStateIdle() => RecordStateIdleContent( - state: state, - recordButton: recordButton, - ), - RecordStateRecordingHold() => RecordStateHoldRecordingContent( - state: state, - recordButton: recordButton, - cancelThreshold: cancelRecordThreshold, - ), - RecordStateRecordingLocked() => RecordStateLockedRecordingContent( - state: state, - onRecordEnd: onRecordFinish, - onRecordPause: onRecordPause, - onRecordCancel: onRecordCancel, - onRecordStop: onRecordStop, - ), - RecordStateStopped() => RecordStateStoppedContent( - state: state, - onRecordCancel: onRecordCancel, - onRecordFinish: onRecordFinish, - ), - }, - ), - ); - } -} - -/// {@template recordButton} -/// A widget representing the record button for the audio recorder. -/// {@endtemplate} -class RecordButton extends StatelessWidget { - /// {@macro recordButton} - const RecordButton({ - super.key, - required this.icon, - this.onPressed, - }); - - /// The icon to display inside the button. - final Widget icon; - - /// The callback that is called when the button is tapped or otherwise - /// activated. - /// - /// If this is set to null, the button will be disabled. - final VoidCallback? onPressed; - - @override - Widget build(BuildContext context) { - return StreamMessageInputIconButton( - onPressed: onPressed, - icon: icon, - ); - } -} - -/// {@template recordStateIdleContent} -/// Represents the idle state content of the audio recorder. -/// -/// Displays the record button and potentially an informational tooltip. -/// -/// Used when no recording is in progress and the recorder is ready to start. -/// {@endtemplate} -class RecordStateIdleContent extends StatelessWidget { - /// {@macro recordStateIdleContent} - const RecordStateIdleContent({ - super.key, - required this.state, - required this.recordButton, - }); - - /// The record button widget to display. - final Widget recordButton; - - /// The current state of the recorder. - final RecordStateIdle state; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - - final child = IconTheme( - data: IconThemeData(color: theme.colorTheme.textLowEmphasis), - child: recordButton, - ); - - final info = state.message; - if (info == null || info.isEmpty) return child; - - return PortalTarget( - anchor: const Aligned( - target: Alignment.topRight, - follower: Alignment.bottomRight, - ), - portalFollower: HoldToRecordInfoTooltip(message: info), - child: child, - ); - } -} - -/// {@template recordStateRecordingContent} -/// Represents the recording state when user is holding the record button. -/// -/// Provides visual feedback during recording with timer and cancellation -/// indicators. -/// -/// Manages the interactive state of recording while the button is being held. -/// Allows sliding to cancel or lock the recording. -/// {@endtemplate} -class RecordStateHoldRecordingContent extends StatelessWidget { - /// {@macro recordStateRecordingContent} - const RecordStateHoldRecordingContent({ - super.key, - required this.state, - required this.recordButton, - this.cancelThreshold = 96, - }); - - /// The record button widget to display. - final Widget recordButton; - - /// The threshold to cancel the recording. - final double cancelThreshold; - - /// The current state of the recorder. - final RecordStateRecordingHold state; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - - final recordingTime = state.duration; - final dragOffset = Offset( - math.min(state.dragOffset.dx, 0), - math.min(state.dragOffset.dy, 0), - ); - - // Calculate the progress of the cancel threshold. - final cancelProgress = (dragOffset.dx.abs() / cancelThreshold).clamp(0, 1); - - return PortalTarget( - // Show the swipe to lock button once the recording starts. - visible: recordingTime.inSeconds > 0, - anchor: Aligned( - offset: Offset(4, dragOffset.dy - 16), - target: Alignment.topRight, - follower: Alignment.bottomRight, - ), - portalFollower: const SlideTransitionWidget( - begin: Offset(0, 0.7), - end: Offset.zero, - child: SwipeToLockButton(), - ), - child: Row( - spacing: 8, - children: [ - IgnorePointer( - child: PlaybackTimerIndicator(duration: recordingTime), - ), - Expanded( - child: IgnorePointer( - child: SlideToCancelIndicator( - progress: cancelProgress.toDouble(), - ), - ), - ), - DecoratedBox( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: theme.colorTheme.inputBg, - ), - child: IconTheme( - data: IconThemeData(color: theme.colorTheme.accentPrimary), - child: recordButton, - ), - ), - ], - ), - ); - } -} - -/// {@template recordStateLockedRecordingContent} -/// Represents the locked recording state with full recording controls. -/// -/// Provides options to pause, stop, or finish the recording after locking. -/// -/// Activated when recording is locked, enabling advanced recording management. -/// {@endtemplate} -class RecordStateLockedRecordingContent extends StatelessWidget { - /// {@macro recordStateLockedRecordingContent} - const RecordStateLockedRecordingContent({ - super.key, - required this.state, - this.onRecordEnd, - this.onRecordPause, - this.onRecordCancel, - this.onRecordStop, - }); - - /// The current state of the recorder. - final RecordStateRecordingLocked state; - - /// The callback to call when the recording is finished. - final VoidCallback? onRecordEnd; - - /// The callback to call when the recording is paused. - final VoidCallback? onRecordPause; - - /// The callback to call when the recording is canceled. - final VoidCallback? onRecordCancel; - - /// The callback to call when the recording is stopped. - final VoidCallback? onRecordStop; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - - return PortalTarget( - anchor: const Aligned( - offset: Offset(4, -16), - target: Alignment.topRight, - follower: Alignment.bottomRight, - ), - portalFollower: const SwipeToLockButton(isLocked: true), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - children: [ - PlaybackTimerText( - duration: state.duration, - style: theme.textTheme.headline.copyWith( - color: theme.colorTheme.textLowEmphasis, - ), - ), - const SizedBox(width: 8), - Expanded( - child: SizedBox( - height: kDefaultMessageInputIconSize, - child: StreamAudioWaveform( - limit: 50, - waveform: state.waveform, - ), - ), - ), - ], - ), - const SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - StreamMessageInputIconButton( - icon: const StreamSvgIcon(icon: StreamSvgIcons.delete), - color: theme.colorTheme.accentPrimary, - onPressed: onRecordCancel, - ), - StreamMessageInputIconButton( - icon: const StreamSvgIcon(icon: StreamSvgIcons.stop), - color: theme.colorTheme.accentError, - onPressed: onRecordStop, - ), - StreamMessageInputIconButton( - icon: const StreamSvgIcon(icon: StreamSvgIcons.checkSend), - color: theme.colorTheme.accentPrimary, - onPressed: onRecordEnd, - ) - ], - ), - ], - ), - ); - } -} - -/// {@template recordStateStoppedContent} -/// Manages the stopped recording state with audio preview and interaction -/// options. -/// -/// Allows reviewing the recorded audio and provides actions to cancel or -/// finish. -/// -/// Provides a UI for previewing and managing a completed audio recording. -/// {@endtemplate} -class RecordStateStoppedContent extends StatefulWidget { - /// {@macro recordStateStoppedContent} - const RecordStateStoppedContent({ - super.key, - required this.state, - this.onRecordFinish, - this.onRecordCancel, - }); - - /// The current state of the recorder. - final RecordStateStopped state; - - /// The callback to call when the recording is finished. - final VoidCallback? onRecordCancel; - - /// The callback to call when the recording is canceled. - final VoidCallback? onRecordFinish; - - @override - State createState() => - _RecordStateStoppedContentState(); -} - -class _RecordStateStoppedContentState extends State { - StreamAudioPlaylistController? _audioController; - - @override - void initState() { - super.initState(); - if (widget.state.audioRecording case final recording) { - _audioController = StreamAudioPlaylistController( - [recording].toPlaylist(), - )..initialize(); - } - } - - @override - void dispose() { - _audioController?.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - - return PortalTarget( - anchor: const Aligned( - offset: Offset(4, -16), - target: Alignment.topRight, - follower: Alignment.bottomRight, - ), - portalFollower: const SwipeToLockButton(isLocked: true), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (_audioController case final controller?) - ValueListenableBuilder( - valueListenable: controller, - builder: (context, state, _) { - final track = state.tracks.firstOrNull; - if (track == null) return const SizedBox.shrink(); - - return Row( - children: [ - PlaybackControlButton( - state: track.state, - onPause: _audioController?.pause, - onPlay: () async { - // Play the track directly if it is already loaded. - if (state.currentIndex != null) { - return _audioController?.play(); - } - - // Otherwise, load the track first and then play it. - return _audioController?.skipToItem(0); - }, - ), - const SizedBox(width: 2), - PlaybackTimerText( - duration: track.duration, - position: track.position, - style: theme.textTheme.headline.copyWith( - color: theme.colorTheme.textLowEmphasis, - ), - ), - const SizedBox(width: 8), - Expanded( - child: SizedBox( - height: 28, - child: StreamAudioWaveformSlider( - limit: 50, - progress: track.progress, - waveform: resampleWaveformData(track.waveform, 50), - // Only allow seeking if the current track is the one - // being interacted with. - onChangeStart: (_) async { - if (state.currentIndex == null) return; - return _audioController?.pause(); - }, - onChangeEnd: (_) async { - if (state.currentIndex == null) return; - return _audioController?.play(); - }, - onChanged: (progress) async { - if (state.currentIndex == null) return; - - final duration = track.duration.inMicroseconds; - final seekPosition = (duration * progress).toInt(); - final seekDuration = Duration( - microseconds: seekPosition, - ); - - return _audioController?.seek(seekDuration); - }, - ), - ), - ), - const SizedBox(width: 8), - ], - ); - }, - ), - const SizedBox(height: 2), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - StreamMessageInputIconButton( - icon: const StreamSvgIcon(icon: StreamSvgIcons.delete), - color: theme.colorTheme.accentPrimary, - onPressed: widget.onRecordCancel, - ), - StreamMessageInputIconButton( - icon: const StreamSvgIcon(icon: StreamSvgIcons.checkSend), - color: theme.colorTheme.accentPrimary, - onPressed: widget.onRecordFinish, - ) - ], - ), - ], - ), - ); - } -} - -/// {@template swipeToLockButton} -/// Button indicating the ability to lock or unlock audio recording. -/// -/// Provides a visual representation of the recording lock state. -/// -/// Allows users to lock the recording mode, preventing accidental cancellation. -/// {@endtemplate} -class SwipeToLockButton extends StatelessWidget { - /// {@macro swipeToLockButton} - const SwipeToLockButton({ - super.key, - this.isLocked = false, - }); - - /// Determines if the recording is locked. - final bool isLocked; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - return Container( - padding: const EdgeInsets.all(8), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(24), - color: theme.colorTheme.inputBg, - ), - child: Column( - spacing: 8, - mainAxisSize: MainAxisSize.min, - children: [ - StreamSvgIcon( - icon: StreamSvgIcons.lock, - size: kDefaultMessageInputIconSize, - color: switch (isLocked) { - true => theme.colorTheme.accentPrimary, - false => theme.colorTheme.textLowEmphasis, - }, - ), - if (!isLocked) ...[ - StreamSvgIcon( - icon: StreamSvgIcons.up, - color: theme.colorTheme.textLowEmphasis, - ), - ], - ], - ), - ); - } -} - -/// {@template playbackControlButton} -/// Playback control button with state-based icon and interaction. -/// -/// Supports different interactions based on current track state. -/// -/// Provides a flexible button for controlling audio playback with multiple -/// states. -/// {@endtemplate} -class PlaybackControlButton extends StatelessWidget { - /// {@macro playbackControlButton} - const PlaybackControlButton({ - super.key, - required this.state, - this.onPlay, - this.onPause, - this.onReplay, - }); - - /// The current state of the track. - final TrackState state; - - /// The callback to call when the track is played. - final VoidCallback? onPlay; - - /// The callback to call when the track is paused. - final VoidCallback? onPause; - - /// The callback to call when the track is replayed. - final VoidCallback? onReplay; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - - return StreamMessageInputIconButton( - color: theme.colorTheme.accentPrimary, - onPressed: switch (state) { - TrackState.loading => null, - TrackState.idle => onPlay, - TrackState.playing => onPause, - TrackState.paused => onPlay, - }, - icon: switch (state) { - TrackState.loading => Builder( - builder: (context) { - final iconTheme = IconTheme.of(context); - return SizedBox.fromSize( - size: Size.square(iconTheme.size!), - child: Padding( - padding: const EdgeInsets.all(8), - child: CircularProgressIndicator.adaptive( - valueColor: AlwaysStoppedAnimation( - theme.colorTheme.accentPrimary, - ), - ), - ), - ); - }, - ), - TrackState.idle => const StreamSvgIcon(icon: StreamSvgIcons.play), - TrackState.paused => const StreamSvgIcon(icon: StreamSvgIcons.play), - TrackState.playing => const StreamSvgIcon(icon: StreamSvgIcons.pause), - }, - ); - } -} - -/// {@template playbackTimerIndicator} -/// Displays the current recording or playback duration. -/// -/// Shows an icon and formatted time with low emphasis styling. -/// -/// Provides a visual representation of recording or playback time. -/// {@endtemplate} -class PlaybackTimerIndicator extends StatelessWidget { - /// {@macro playbackTimerIndicator} - const PlaybackTimerIndicator({ - super.key, - required this.duration, - }); - - /// The current duration of the recording or playback. - final Duration duration; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - return Row( - children: [ - StreamSvgIcon( - icon: StreamSvgIcons.mic, - size: kDefaultMessageInputIconSize, - color: switch (duration.inSeconds) { - > 0 => theme.colorTheme.accentError, - _ => theme.colorTheme.textLowEmphasis, - }, - ), - const SizedBox(width: 8), - PlaybackTimerText( - duration: duration, - style: theme.textTheme.headline.copyWith( - color: theme.colorTheme.textLowEmphasis, - ), - ), - ], - ); - } -} - -/// {@template playbackTimerText} -/// Displays the formatted time of the recording or playback. -/// -/// Formats the time in minutes and seconds with tabular figures. -/// {@endtemplate} -class PlaybackTimerText extends StatelessWidget { - /// {@macro playbackTimerText} - const PlaybackTimerText({ - super.key, - required this.duration, - this.position = Duration.zero, - this.style, - }); - - /// The total duration of the recording or playback. - final Duration duration; - - /// The current position of the recording or playback. - final Duration position; - - /// The text style to apply to the formatted time. - final TextStyle? style; - - @override - Widget build(BuildContext context) { - return Text( - switch (position.inMilliseconds > 0) { - true => position.toMinutesAndSeconds(), - false => duration.toMinutesAndSeconds(), - }, - style: style?.copyWith( - // Use mono space for each num character. - fontFeatures: [const FontFeature.tabularFigures()], - ), - ); - } -} - -/// {@template slideToCancelIndicator} -/// Indicator showing progress of sliding to cancel recording. -/// -/// Provides visual feedback during recording cancellation gesture. -/// -/// Visualizes the user's progress when attempting to cancel a recording. -/// {@endtemplate} -class SlideToCancelIndicator extends StatelessWidget { - /// {@macro slideToCancelIndicator} - const SlideToCancelIndicator({ - super.key, - required this.progress, - }); - - /// The progress of the cancel threshold. - final double progress; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - - return Opacity( - opacity: 1 - progress, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - context.translations.slideToCancelLabel, - style: theme.textTheme.headline.copyWith( - color: theme.colorTheme.textLowEmphasis, - ), - ), - const SizedBox(width: 8), - StreamSvgIcon( - icon: StreamSvgIcons.left, - color: theme.colorTheme.textLowEmphasis, - ), - ], - ), - ); - } -} - -/// {@template streamAudioRecorder} -/// Builder widget for constructing audio recorder UI based on state. -/// -/// Allows dynamic UI rendering depending on the current audio recorder state. -/// -/// Provides a flexible mechanism for rendering audio recorder UI dynamically. -/// -/// see also: -/// - [StreamAudioRecorderButton], which uses this builder function. -/// - [StreamAudioRecorderState], which provides the state of the recorder. -/// {@endtemplate} -class StreamAudioRecorder extends StatelessWidget { - /// {@macro streamAudioRecorder} - const StreamAudioRecorder({ - super.key, - required this.state, - required this.builder, - required this.button, - }); - - /// The button widget to display. - final Widget button; - - /// The current state of the audio recorder. - final AudioRecorderState state; - - /// The builder function to construct the audio recorder UI. - final AudioRecorderBuilder builder; - - @override - Widget build(BuildContext context) => builder(context, state, button); -} - -/// {@template slideTransitionWidget} -/// Reusable widget for creating slide-based transitions. -/// -/// Provides a configurable animation for sliding widgets in and out. -/// -/// Enables smooth sliding transitions with customizable parameters. -/// {@endtemplate} -class SlideTransitionWidget extends StatefulWidget { - /// {@macro slideTransitionWidget} - const SlideTransitionWidget({ - super.key, - required this.begin, - required this.end, - this.curve = Curves.easeOut, - this.duration = const Duration(milliseconds: 300), - required this.child, - }); - - /// The starting offset of the slide transition. - final Offset begin; - - /// The ending offset of the slide transition. - final Offset end; - - /// The duration of the slide transition. - final Duration duration; - - /// The curve of the slide transition. - final Curve curve; - - /// The child widget to slide. - final Widget child; - - @override - State createState() => _SlideTransitionWidgetState(); -} - -class _SlideTransitionWidgetState extends State - with SingleTickerProviderStateMixin { - late final _controller = AnimationController( - duration: widget.duration, - vsync: this, - )..forward(); - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final position = Tween( - begin: widget.begin, - end: widget.end, - ).animate(CurvedAnimation( - parent: _controller, - curve: widget.curve, - )); - - return SlideTransition( - position: position, - child: widget.child, - ); - } -} - -/// {@template holdToRecordInfoTooltip} -/// Tooltip to guide users on initiating audio recording. -/// -/// Provides an informative message with a custom-painted arrow and styling. -/// -/// Displays instructional information for audio recording interaction. -/// {@endtemplate} -class HoldToRecordInfoTooltip extends StatelessWidget { - /// {@macro holdToRecordInfoTooltip} - const HoldToRecordInfoTooltip({ - super.key, - required this.message, - }); - - /// The message to show in the tooltip. - final String message; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - - const recordButtonWidth = kDefaultMessageInputIconSize + - kDefaultMessageInputIconPadding * 2; // right, left padding. - - const arrowSize = Size(recordButtonWidth / 2, 6); - - return Padding( - padding: EdgeInsets.only(bottom: arrowSize.height), - child: CustomPaint( - painter: TooltipPainter( - arrowSize: arrowSize, - arrowMargin: arrowSize.width / 2, - color: theme.colorTheme.textLowEmphasis, - borderRadius: BorderRadius.circular(24), - ), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 12), - child: Text( - message, - style: theme.textTheme.body.copyWith( - color: theme.colorTheme.barsBg, - ), - ), - ), - ), - ); - } -} - -/// {@template tooltipPainter} -/// Custom painter for creating tooltips with an arrow indicator. -/// -/// Enables precise rendering of custom-shaped tooltips with configurable -/// styling. -/// -/// Provides a flexible mechanism for painting custom-shaped tooltips. -/// {@endtemplate} -class TooltipPainter extends CustomPainter { - /// {@macro tooltipPainter} - const TooltipPainter({ - this.color = Colors.grey, - this.arrowSize = const Size(16, 8), - this.arrowMargin = 8, - this.borderRadius = BorderRadius.zero, - }); - - /// The background color of the tooltip. - final Color color; - - /// The size of the arrow indicator. - final Size arrowSize; - - /// The margin between the arrow and the tooltip. - final double arrowMargin; - - /// The border radius of the tooltip. - final BorderRadius borderRadius; - - @override - void paint(Canvas canvas, Size size) { - final paint = Paint() - ..color = color - ..strokeWidth = 1 - ..style = PaintingStyle.fill; - - final width = size.width; - final height = size.height; - - final rect = Rect.fromLTRB(0, 0, width, height); - final outer = borderRadius.toRRect(rect); - canvas.drawRRect(outer, paint); - - final arrowWidth = arrowSize.width; - final arrowHeight = arrowSize.height; - - final arrowPath = Path() - ..moveTo(width - arrowWidth - arrowMargin, height) - ..lineTo(width - arrowWidth / 2 - arrowMargin, height + arrowHeight) - ..lineTo(width, height / 2) - ..close(); - - canvas.drawPath(arrowPath, paint); - } - - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) => false; -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/clear_input_item_button.dart b/packages/stream_chat_flutter/lib/src/message_input/clear_input_item_button.dart deleted file mode 100644 index ddfb0c20e0..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/clear_input_item_button.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template clearInputItemButton} -/// Used to clear items from the [MessageInput] field, such as attachments -/// or message quotes. -/// {@endtemplate} -class ClearInputItemButton extends StatelessWidget { - /// {@macro clearInputItemButton} - const ClearInputItemButton({ - super.key, - required this.onTap, - }); - - /// The callback to be performed when the button is tapped or clicked. - final VoidCallback? onTap; - - @override - Widget build(BuildContext context) { - final _streamChatTheme = StreamChatTheme.of(context); - return SizedBox( - height: 20, - width: 20, - child: RawMaterialButton( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - elevation: 0, - highlightElevation: 0, - focusElevation: 0, - hoverElevation: 0, - onPressed: onTap, - fillColor: - // ignore: deprecated_member_use - _streamChatTheme.colorTheme.textHighEmphasis.withOpacity(0.5), - child: Center( - child: StreamSvgIcon( - size: 24, - icon: StreamSvgIcons.close, - color: _streamChatTheme.colorTheme.barsBg, - ), - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/command_button.dart b/packages/stream_chat_flutter/lib/src/message_input/command_button.dart deleted file mode 100644 index 352a628f17..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/command_button.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/icons/stream_svg_icon.dart'; -import 'package:stream_chat_flutter/src/message_input/stream_message_input_icon_button.dart'; - -/// {@template commandButton} -/// The button that allows a user to use commands in a chat. -/// {@endtemplate} -class CommandButton extends StatelessWidget { - /// {@macro commandButton} - const CommandButton({ - super.key, - required this.onPressed, - this.color, - this.icon, - this.size = kDefaultMessageInputIconSize, - }) : assert( - (icon == null && color == null) || - (icon != null && color == null) || - (icon == null && color != null), - 'Either icon or color should be provided'); - - /// The color of the button. - /// Should be set if no [icon] is provided. - final Color? color; - - /// The callback to perform when the button is tapped or clicked. - final VoidCallback onPressed; - - /// The icon to display inside the button. - /// if not provided, a default icon will be used - /// and [color] property should be set. - final Widget? icon; - - /// The size of the button and splash radius. - final double size; - - /// Returns a copy of this object with the given fields updated. - CommandButton copyWith({ - Key? key, - Color? color, - VoidCallback? onPressed, - Widget? icon, - double? size, - }) { - return CommandButton( - key: key ?? this.key, - color: color ?? this.color, - onPressed: onPressed ?? this.onPressed, - icon: icon ?? this.icon, - size: size ?? this.size, - ); - } - - @override - Widget build(BuildContext context) { - return StreamMessageInputIconButton( - color: color, - iconSize: size, - onPressed: onPressed, - icon: icon ?? const StreamSvgIcon(icon: StreamSvgIcons.lightning), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/countdown_button.dart b/packages/stream_chat_flutter/lib/src/message_input/countdown_button.dart deleted file mode 100644 index 155e549e4f..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/countdown_button.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/message_input/stream_message_input_icon_button.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Button for showing visual component of slow mode. -class StreamCountdownButton extends StatelessWidget { - /// Constructor for creating [StreamCountdownButton]. - const StreamCountdownButton({ - super.key, - required this.count, - }); - - /// The amount of time remaining until the user can send a message again. - /// Measured in seconds. - final int count; - - @override - Widget build(BuildContext context) { - return DecoratedBox( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: StreamChatTheme.of(context).colorTheme.disabled, - ), - child: SizedBox.square( - dimension: kDefaultMessageInputIconSize, - child: Center(child: Text('$count')), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/dm_checkbox.dart b/packages/stream_chat_flutter/lib/src/message_input/dm_checkbox.dart deleted file mode 100644 index bb39797da5..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/dm_checkbox.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template dmCheckbox} -/// Prompts the user to send a reply to a message thread as a DM. -/// {@endtemplate} -class DmCheckbox extends StatelessWidget { - /// {@macro dmCheckbox} - const DmCheckbox({ - super.key, - required this.foregroundDecoration, - required this.color, - required this.onTap, - required this.crossFadeState, - }); - - /// The decoration to use for the button's foreground. - final BoxDecoration foregroundDecoration; - - /// The color to use for the button. - final Color color; - - /// The action to perform when the button is tapped or clicked. - final VoidCallback onTap; - - /// The [CrossFadeState] of the animation. - final CrossFadeState crossFadeState; - - @override - Widget build(BuildContext context) { - final _streamChatTheme = StreamChatTheme.of(context); - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - height: 16, - width: 16, - foregroundDecoration: foregroundDecoration, - child: Center( - child: Material( - borderRadius: BorderRadius.circular(3), - color: color, - child: InkWell( - onTap: onTap, - child: AnimatedCrossFade( - duration: const Duration(milliseconds: 300), - reverseDuration: const Duration(milliseconds: 300), - crossFadeState: crossFadeState, - firstChild: StreamSvgIcon( - size: 16, - icon: StreamSvgIcons.check, - color: _streamChatTheme.colorTheme.barsBg, - ), - secondChild: const SizedBox( - height: 16, - width: 16, - ), - ), - ), - ), - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 12), - child: Text( - context.translations.alsoSendAsDirectMessageLabel, - style: _streamChatTheme.textTheme.footnote.copyWith( - color: - // ignore: deprecated_member_use - _streamChatTheme.colorTheme.textHighEmphasis.withOpacity(0.5), - ), - ), - ), - ], - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/enums.dart b/packages/stream_chat_flutter/lib/src/message_input/enums.dart deleted file mode 100644 index 5b34b7f132..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/enums.dart +++ /dev/null @@ -1,35 +0,0 @@ -/// Location for actions on the [StreamMessageInput]. -enum ActionsLocation { - /// Align to left - left, - - /// Align to right - right, - - /// Align to left but inside the [TextField] - leftInside, - - /// Align to right but inside the [TextField] - rightInside, -} - -/// Default attachments for widget. -enum DefaultAttachmentTypes { - /// Image Attachment - image, - - /// Video Attachment - video, - - /// File Attachment - file, -} - -/// Available locations for the `sendMessage` button relative to the textField. -enum SendButtonLocation { - /// inside the textField - inside, - - /// outside the textField - outside, -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/quoted_message_widget.dart b/packages/stream_chat_flutter/lib/src/message_input/quoted_message_widget.dart deleted file mode 100644 index 3e71e174c9..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/quoted_message_widget.dart +++ /dev/null @@ -1,346 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/message_input/clear_input_item_button.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -typedef _Builders = Map; - -/// {@template streamQuotedMessage} -/// Widget for the quoted message. -/// {@endtemplate} -class StreamQuotedMessageWidget extends StatelessWidget { - /// {@macro streamQuotedMessage} - const StreamQuotedMessageWidget({ - super.key, - required this.message, - required this.messageTheme, - this.reverse = false, - this.showBorder = false, - this.textLimit = 170, - this.textBuilder, - this.attachmentThumbnailBuilders, - this.padding = const EdgeInsets.all(8), - this.onQuotedMessageClear, - }); - - /// The message - final Message message; - - /// The message theme - final StreamMessageThemeData messageTheme; - - /// If true the widget will be mirrored - final bool reverse; - - /// If true the message will show a grey border - final bool showBorder; - - /// limit of the text message shown - final int textLimit; - - /// Map that defines a thumbnail builder for an attachment type - final _Builders? attachmentThumbnailBuilders; - - /// Padding around the widget - final EdgeInsetsGeometry padding; - - /// Callback for clearing quoted messages. - final VoidCallback? onQuotedMessageClear; - - /// {@macro textBuilder} - final Widget Function(BuildContext, Message)? textBuilder; - - @override - Widget build(BuildContext context) { - final children = [ - Flexible( - child: _QuotedMessage( - message: message, - textLimit: textLimit, - messageTheme: messageTheme, - showBorder: showBorder, - reverse: reverse, - textBuilder: textBuilder, - onQuotedMessageClear: onQuotedMessageClear, - attachmentThumbnailBuilders: attachmentThumbnailBuilders, - ), - ), - const SizedBox(width: 8), - if (message.user != null) - StreamUserAvatar( - user: message.user!, - constraints: const BoxConstraints.tightFor( - height: 24, - width: 24, - ), - showOnlineStatus: false, - ), - ]; - return Padding( - padding: padding, - child: Row( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisSize: MainAxisSize.min, - children: reverse ? children.reversed.toList() : children, - ), - ); - } -} - -class _QuotedMessage extends StatelessWidget { - const _QuotedMessage({ - required this.message, - required this.textLimit, - required this.messageTheme, - required this.showBorder, - required this.reverse, - this.textBuilder, - this.onQuotedMessageClear, - this.attachmentThumbnailBuilders, - }); - - final Message message; - final int textLimit; - final VoidCallback? onQuotedMessageClear; - final StreamMessageThemeData messageTheme; - final bool showBorder; - final bool reverse; - final Widget Function(BuildContext, Message)? textBuilder; - - final _Builders? attachmentThumbnailBuilders; - - bool get _hasAttachments => message.attachments.isNotEmpty; - - bool get _containsText => message.text?.isNotEmpty == true; - - bool get _containsLinkAttachment => - message.attachments.any((it) => it.type == AttachmentType.urlPreview); - - bool get _isGiphy => message.attachments - .any((element) => element.type == AttachmentType.giphy); - - bool get _isDeleted => message.isDeleted || message.deletedAt != null; - - bool get _isPoll => message.poll != null; - - @override - Widget build(BuildContext context) { - final isOnlyEmoji = message.text!.isOnlyEmoji; - var msg = _hasAttachments && !_containsText - ? message.copyWith(text: message.attachments.last.title ?? '') - : message; - if (msg.text!.length > textLimit) { - msg = msg.copyWith(text: '${msg.text!.substring(0, textLimit - 3)}...'); - } - - List children; - if (_isDeleted) { - // Show deleted message text - children = [ - Text( - context.translations.messageDeletedLabel, - style: messageTheme.messageTextStyle?.copyWith( - fontStyle: FontStyle.italic, - color: messageTheme.createdAtStyle?.color, - ), - ), - ]; - } else if (_isPoll) { - // Show poll message - children = [ - Flexible( - child: Text( - 'šŸ“Š ${message.poll?.name}', - style: messageTheme.messageTextStyle?.copyWith( - fontSize: 12, - ), - ), - ), - ]; - } else { - // Show quoted message - children = [ - if (_hasAttachments) - _ParseAttachments( - message: message, - messageTheme: messageTheme, - attachmentThumbnailBuilders: attachmentThumbnailBuilders, - ), - if (msg.text!.isNotEmpty && !_isGiphy) - Flexible( - child: textBuilder?.call(context, msg) ?? - StreamMessageText( - message: msg, - messageTheme: isOnlyEmoji && _containsText - ? messageTheme.copyWith( - messageTextStyle: - messageTheme.messageTextStyle?.copyWith( - fontSize: 32, - ), - ) - : messageTheme.copyWith( - messageTextStyle: - messageTheme.messageTextStyle?.copyWith( - fontSize: 12, - ), - ), - ), - ), - ]; - } - - // Add clear button if needed. - if (isDesktopDeviceOrWeb && onQuotedMessageClear != null) { - children.insert( - 0, - ClearInputItemButton(onTap: onQuotedMessageClear), - ); - } - - return Container( - decoration: BoxDecoration( - color: _getBackgroundColor(context), - border: showBorder - ? Border.all( - color: StreamChatTheme.of(context).colorTheme.disabled, - ) - : null, - borderRadius: BorderRadius.only( - topRight: const Radius.circular(12), - topLeft: const Radius.circular(12), - bottomRight: reverse ? const Radius.circular(12) : Radius.zero, - bottomLeft: reverse ? Radius.zero : const Radius.circular(12), - ), - ), - padding: const EdgeInsets.all(8), - child: Row( - spacing: 8, - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: - reverse ? MainAxisAlignment.end : MainAxisAlignment.start, - children: reverse ? children.reversed.toList() : children, - ), - ); - } - - Color? _getBackgroundColor(BuildContext context) { - if (_containsLinkAttachment && !_isDeleted) { - return messageTheme.urlAttachmentBackgroundColor; - } - return messageTheme.messageBackgroundColor; - } -} - -class _ParseAttachments extends StatelessWidget { - const _ParseAttachments({ - required this.message, - required this.messageTheme, - this.attachmentThumbnailBuilders, - }); - - final Message message; - final StreamMessageThemeData messageTheme; - final _Builders? attachmentThumbnailBuilders; - - @override - Widget build(BuildContext context) { - final attachment = message.attachments.first; - - var attachmentBuilders = attachmentThumbnailBuilders; - attachmentBuilders ??= _createDefaultAttachmentBuilders(); - - // Build the attachment widget using the builder for the attachment type. - final attachmentWidget = attachmentBuilders[attachment.type]?.call( - context, - attachment, - ); - - // Return empty container if no attachment widget is returned. - if (attachmentWidget == null) return const SizedBox.shrink(); - - final colorTheme = StreamChatTheme.of(context).colorTheme; - - var clipBehavior = Clip.none; - ShapeDecoration? decoration; - if (attachment.type != AttachmentType.file && - attachment.type != AttachmentType.voiceRecording) { - clipBehavior = Clip.hardEdge; - decoration = ShapeDecoration( - shape: RoundedRectangleBorder( - side: BorderSide( - color: colorTheme.borders, - strokeAlign: BorderSide.strokeAlignOutside, - ), - borderRadius: BorderRadius.circular(8), - ), - ); - } - - return Container( - key: Key(attachment.id), - clipBehavior: clipBehavior, - decoration: decoration, - constraints: const BoxConstraints.tightFor(width: 36, height: 36), - child: AbsorbPointer(child: attachmentWidget), - ); - } - - _Builders _createDefaultAttachmentBuilders() { - Widget _createMediaThumbnail(BuildContext context, Attachment media) { - return StreamImageAttachmentThumbnail( - image: media, - width: double.infinity, - height: double.infinity, - fit: BoxFit.cover, - ); - } - - Widget _createUrlThumbnail(BuildContext context, Attachment media) { - return StreamImageAttachmentThumbnail( - image: media, - width: double.infinity, - height: double.infinity, - fit: BoxFit.cover, - ); - } - - Widget _createFileThumbnail(BuildContext context, Attachment file) { - Widget thumbnail = StreamFileAttachmentThumbnail( - file: file, - width: double.infinity, - height: double.infinity, - fit: BoxFit.cover, - ); - - final mediaType = file.title?.mediaType; - final isImage = mediaType?.type == AttachmentType.image; - final isVideo = mediaType?.type == AttachmentType.video; - if (isImage || isVideo) { - final colorTheme = StreamChatTheme.of(context).colorTheme; - thumbnail = Container( - clipBehavior: Clip.hardEdge, - decoration: ShapeDecoration( - shape: RoundedRectangleBorder( - side: BorderSide( - color: colorTheme.borders, - strokeAlign: BorderSide.strokeAlignOutside, - ), - borderRadius: BorderRadius.circular(8), - ), - ), - child: thumbnail, - ); - } - - return thumbnail; - } - - return { - AttachmentType.image: _createMediaThumbnail, - AttachmentType.giphy: _createMediaThumbnail, - AttachmentType.video: _createMediaThumbnail, - AttachmentType.urlPreview: _createUrlThumbnail, - AttachmentType.file: _createFileThumbnail, - AttachmentType.voiceRecording: _createFileThumbnail, - }; - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/quoting_message_top_area.dart b/packages/stream_chat_flutter/lib/src/message_input/quoting_message_top_area.dart deleted file mode 100644 index c81a5e1391..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/quoting_message_top_area.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/message_input/stream_message_input_icon_button.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template quotingMessageTopArea} -/// The area that appears above [MessageInput] when the user is quoting a -/// message. -/// -/// Should only be used on mobile platforms. -/// {@endtemplate} -class QuotingMessageTopArea extends StatelessWidget { - /// {@macro quotingMessageTopArea} - const QuotingMessageTopArea({ - super.key, - required this.hasQuotedMessage, - this.onQuotedMessageCleared, - }); - - /// - final bool hasQuotedMessage; - - /// The callback to perform when the "close" button is tapped. - /// - /// Should be [MessageInput.onQuotedMessageCleared]. - final VoidCallback? onQuotedMessageCleared; - - @override - Widget build(BuildContext context) { - final _streamChatTheme = StreamChatTheme.of(context); - if (hasQuotedMessage) { - return Padding( - padding: const EdgeInsets.fromLTRB(12, 8, 12, 0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - StreamMessageInputIconButton( - iconSize: 24, - color: _streamChatTheme.colorTheme.disabled, - icon: const StreamSvgIcon(icon: StreamSvgIcons.reply), - onPressed: null, - ), - Text( - context.translations.replyToMessageLabel, - style: const TextStyle(fontWeight: FontWeight.bold), - ), - StreamMessageInputIconButton( - iconSize: 24, - color: _streamChatTheme.colorTheme.textLowEmphasis, - icon: const StreamSvgIcon(icon: StreamSvgIcons.closeSmall), - onPressed: onQuotedMessageCleared?.call, - ), - ], - ), - ); - } else { - return const SizedBox.shrink(); - } - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/simple_safe_area.dart b/packages/stream_chat_flutter/lib/src/message_input/simple_safe_area.dart deleted file mode 100644 index 013cf08465..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/simple_safe_area.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:flutter/material.dart'; - -/// A [SafeArea] with an enabled toggle -class SimpleSafeArea extends StatelessWidget { - /// Constructor for [SimpleSafeArea] - const SimpleSafeArea({ - super.key, - this.enabled = true, - required this.child, - }); - - /// Wrap [child] with [SafeArea] - final bool enabled; - - /// Child widget to wrap - final Widget child; - - @override - Widget build(BuildContext context) => SafeArea( - left: enabled, - top: enabled, - right: enabled, - bottom: enabled, - child: child, - ); -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart deleted file mode 100644 index 01d7ecfe2d..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart +++ /dev/null @@ -1,1654 +0,0 @@ -import 'dart:async'; -import 'dart:math'; - -import 'package:desktop_drop/desktop_drop.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:photo_manager/photo_manager.dart'; -import 'package:stream_chat_flutter/platform_widget_builder/src/platform_widget_builder.dart'; -import 'package:stream_chat_flutter/src/message_input/attachment_button.dart'; -import 'package:stream_chat_flutter/src/message_input/command_button.dart'; -import 'package:stream_chat_flutter/src/message_input/dm_checkbox.dart'; -import 'package:stream_chat_flutter/src/message_input/quoted_message_widget.dart'; -import 'package:stream_chat_flutter/src/message_input/quoting_message_top_area.dart'; -import 'package:stream_chat_flutter/src/message_input/simple_safe_area.dart'; -import 'package:stream_chat_flutter/src/message_input/stream_message_input_icon_button.dart'; -import 'package:stream_chat_flutter/src/message_input/tld.dart'; -import 'package:stream_chat_flutter/src/misc/gradient_box_border.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -const _kCommandTrigger = '/'; -const _kMentionTrigger = '@'; - -/// Signature for the function that determines if a [matchedUri] should be -/// previewed as an OG Attachment. -typedef OgPreviewFilter = bool Function( - Uri matchedUri, - String messageText, -); - -/// Different types of hints that can be shown in [StreamMessageInput]. -enum HintType { - /// Hint for [StreamMessageInput] when the command is enabled and the command - /// is 'giphy'. - searchGif, - - /// Hint for [StreamMessageInput] when there are attachments. - addACommentOrSend, - - /// Hint for [StreamMessageInput] when slow mode is enabled. - slowModeOn, - - /// Hint for [StreamMessageInput] when other conditions are not met. - writeAMessage, -} - -/// Function that returns the hint text for [StreamMessageInput] based on -/// [type]. -typedef HintGetter = String? Function(BuildContext context, HintType type); - -/// The signature for the function that builds the list of actions. -typedef ActionsBuilder = List Function( - BuildContext context, - List defaultActions, -); - -/// Inactive state: -/// -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/message_input.png) -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/message_input_paint.png) -/// -/// Focused state: -/// -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/message_input2.png) -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/message_input2_paint.png) -/// -/// Widget used to enter a message and add attachments: -/// -/// ```dart -/// class ChannelPage extends StatelessWidget { -/// const ChannelPage({ -/// Key? key, -/// }) : super(key: key); -/// -/// @override -/// Widget build(BuildContext context) => Scaffold( -/// appBar: const StreamChannelHeader(), -/// body: Column( -/// children: [ -/// Expanded( -/// child: StreamMessageListView( -/// threadBuilder: (_, parentMessage) => ThreadPage( -/// parent: parentMessage, -/// ), -/// ), -/// ), -/// const StreamMessageInput(), -/// ], -/// ), -/// ); -/// } -/// ``` -/// -/// You usually put this widget in the same page of a [StreamMessageListView] -/// as the bottom widget. -/// -/// The widget renders the ui based on the first ancestor of -/// type [StreamChatTheme]. Modify it to change the widget appearance. -class StreamMessageInput extends StatefulWidget { - /// Instantiate a new MessageInput - const StreamMessageInput({ - super.key, - this.onMessageSent, - this.preMessageSending, - this.maxHeight = 150, - this.maxLines, - this.minLines, - this.textInputAction, - this.keyboardType, - this.textCapitalization = TextCapitalization.sentences, - this.disableAttachments = false, - this.messageInputController, - this.actionsBuilder, - this.spaceBetweenActions = 0, - this.actionsLocation = ActionsLocation.left, - this.attachmentListBuilder, - this.fileAttachmentListBuilder, - this.mediaAttachmentListBuilder, - this.voiceRecordingAttachmentListBuilder, - this.fileAttachmentBuilder, - this.mediaAttachmentBuilder, - this.voiceRecordingAttachmentBuilder, - this.focusNode, - this.sendButtonLocation = SendButtonLocation.outside, - this.autofocus = false, - this.hideSendAsDm = false, - this.enableVoiceRecording = false, - this.sendVoiceRecordingAutomatically = false, - this.idleSendButton, - this.activeSendButton, - this.showCommandsButton = true, - this.userMentionsTileBuilder, - this.maxAttachmentSize = kDefaultMaxAttachmentSize, - this.onError, - this.attachmentLimit = 10, - this.allowedAttachmentPickerTypes = AttachmentPickerType.values, - this.onAttachmentLimitExceed, - this.attachmentButtonBuilder, - this.commandButtonBuilder, - this.customAutocompleteTriggers = const [], - this.mentionAllAppUsers = false, - this.sendButtonBuilder, - this.quotedMessageBuilder, - this.quotedMessageAttachmentThumbnailBuilders, - this.shouldKeepFocusAfterMessage, - this.validator = _defaultValidator, - this.restorationId, - this.enableSafeArea, - this.elevation, - this.shadow, - this.autoCorrect = true, - this.enableMentionsOverlay = true, - this.onQuotedMessageCleared, - this.enableActionAnimation = true, - this.sendMessageKeyPredicate = _defaultSendMessageKeyPredicate, - this.clearQuotedMessageKeyPredicate = - _defaultClearQuotedMessageKeyPredicate, - this.ogPreviewFilter = _defaultOgPreviewFilter, - this.hintGetter = _defaultHintGetter, - this.contentInsertionConfiguration, - this.useNativeAttachmentPickerOnMobile = false, - this.pollConfig, - }); - - /// The predicate used to send a message on desktop/web - final KeyEventPredicate sendMessageKeyPredicate; - - /// The predicate used to clear the quoted message on desktop/web - final KeyEventPredicate clearQuotedMessageKeyPredicate; - - /// If true the message input will animate the actions while you type - final bool enableActionAnimation; - - /// List of triggers for showing autocomplete. - final Iterable customAutocompleteTriggers; - - /// Max attachment size in bytes: - /// - Defaults to 20 MB - /// - Do not set it if you're using our default CDN - final int maxAttachmentSize; - - /// Function called after sending the message. - final void Function(Message)? onMessageSent; - - /// Function called right before sending the message. - /// - /// Use this to transform the message. - final FutureOr Function(Message)? preMessageSending; - - /// Maximum Height for the TextField to grow before it starts scrolling. - final double maxHeight; - - /// The maximum lines of text the input can span. - final int? maxLines; - - /// The minimum lines of text the input can span. - final int? minLines; - - /// The type of action button to use for the keyboard. - final TextInputAction? textInputAction; - - /// The keyboard type assigned to the TextField. - final TextInputType? keyboardType; - - /// {@macro flutter.widgets.editableText.textCapitalization} - final TextCapitalization textCapitalization; - - /// If true the attachments button will not be displayed. - final bool disableAttachments; - - /// Use this property to hide/show the commands button. - final bool showCommandsButton; - - /// Hide send as dm checkbox. - final bool hideSendAsDm; - - /// If true the voice recording button will be displayed. - /// - /// Defaults to true. - final bool enableVoiceRecording; - - /// If True, the voice recording will be sent automatically after the user - /// releases the microphone button. - /// - /// Defaults to false. - final bool sendVoiceRecordingAutomatically; - - /// The text controller of the TextField. - final StreamMessageInputController? messageInputController; - - /// List of action widgets. - final ActionsBuilder? actionsBuilder; - - /// Space between the actions. - final double spaceBetweenActions; - - /// The location of the custom actions. - final ActionsLocation actionsLocation; - - /// Builder used to build the attachment list present in the message input. - /// - /// In case you want to customize only sub-parts of the attachment list, - /// consider using [fileAttachmentListBuilder], [mediaAttachmentListBuilder]. - final AttachmentListBuilder? attachmentListBuilder; - - /// Builder used to build the file type attachment list. - /// - /// In case you want to customize the attachment item, consider using - /// [fileAttachmentBuilder]. - final AttachmentListBuilder? fileAttachmentListBuilder; - - /// Builder used to build the media type attachment list. - /// - /// In case you want to customize the attachment item, consider using - /// [mediaAttachmentBuilder]. - final AttachmentListBuilder? mediaAttachmentListBuilder; - - /// Builder used to build the voice recording attachment list. - /// - /// In case you want to customize the attachment item, consider using - /// [voiceRecordingAttachmentBuilder]. - final AttachmentListBuilder? voiceRecordingAttachmentListBuilder; - - /// Builder used to build the file attachment item. - final AttachmentItemBuilder? fileAttachmentBuilder; - - /// Builder used to build the media attachment item. - final AttachmentItemBuilder? mediaAttachmentBuilder; - - /// Builder used to build the voice recording attachment item. - final AttachmentItemBuilder? voiceRecordingAttachmentBuilder; - - /// Map that defines a thumbnail builder for an attachment type. - /// - /// This is used to build the thumbnail for the attachment in the quoted - /// message. - final Map? - quotedMessageAttachmentThumbnailBuilders; - - /// The focus node associated to the TextField. - final FocusNode? focusNode; - - /// The location of the send button - final SendButtonLocation sendButtonLocation; - - /// Autofocus property passed to the TextField - final bool autofocus; - - /// Send button widget in an idle state - final Widget? idleSendButton; - - /// Send button widget in an active state - final Widget? activeSendButton; - - /// Customize the tile for the mentions overlay. - final UserMentionTileBuilder? userMentionsTileBuilder; - - /// A callback for error reporting - final ErrorListener? onError; - - /// A limit for the no. of attachments that can be sent with a single message. - final int attachmentLimit; - - /// The list of allowed attachment types which can be picked using the - /// attachment button. - /// - /// By default, all the attachment types are allowed. - final List allowedAttachmentPickerTypes; - - /// A callback for when the [attachmentLimit] is exceeded. - /// - /// This will override the default error alert behaviour. - final AttachmentLimitExceedListener? onAttachmentLimitExceed; - - /// Builder for customizing the attachment button. - /// - /// The builder contains the default [AttachmentButton] that can be customized - /// by calling `.copyWith`. - final AttachmentButtonBuilder? attachmentButtonBuilder; - - /// Builder for customizing the command button. - /// - /// The builder contains the default [CommandButton] that can be customized by - /// calling `.copyWith`. - final CommandButtonBuilder? commandButtonBuilder; - - /// When enabled mentions search users across the entire app. - /// - /// Defaults to false. - final bool mentionAllAppUsers; - - /// Builder for creating send button - final MessageRelatedBuilder? sendButtonBuilder; - - /// Builder for building quoted message - final Widget Function(BuildContext, Message)? quotedMessageBuilder; - - /// Defines if the [StreamMessageInput] loses focuses after a message is sent. - /// The default behaviour keeps focus until a command is enabled. - final bool? shouldKeepFocusAfterMessage; - - /// A callback function that validates the message. - final MessageValidator validator; - - /// Restoration ID to save and restore the state of the MessageInput. - final String? restorationId; - - /// Wrap [StreamMessageInput] with a [SafeArea widget] - final bool? enableSafeArea; - - /// Elevation of the [StreamMessageInput] - final double? elevation; - - /// Shadow for the [StreamMessageInput] widget - final BoxShadow? shadow; - - /// Disable autoCorrect by passing false - /// autoCorrect is enabled by default - final bool autoCorrect; - - /// Disable the mentions overlay by passing false - /// Enabled by default - final bool enableMentionsOverlay; - - /// Callback for when the quoted message is cleared - final VoidCallback? onQuotedMessageCleared; - - /// The filter used to determine if a link should be shown as an OpenGraph - /// preview. - final OgPreviewFilter ogPreviewFilter; - - /// Returns the hint text for the message input. - final HintGetter hintGetter; - - /// {@macro flutter.widgets.editableText.contentInsertionConfiguration} - final ContentInsertionConfiguration? contentInsertionConfiguration; - - /// Forces use of native attachment picker on mobile instead of the custom - /// Stream attachment picker. - final bool useNativeAttachmentPickerOnMobile; - - /// The configuration to use while creating a poll. - /// - /// If not provided, the default configuration is used. - final PollConfig? pollConfig; - - static String? _defaultHintGetter( - BuildContext context, - HintType type, - ) { - switch (type) { - case HintType.searchGif: - return context.translations.searchGifLabel; - case HintType.addACommentOrSend: - return context.translations.addACommentOrSendLabel; - case HintType.slowModeOn: - return context.translations.slowModeOnLabel; - case HintType.writeAMessage: - return context.translations.writeAMessageLabel; - } - } - - static bool _defaultOgPreviewFilter( - Uri matchedUri, - String messageText, - ) { - // Show the preview for all links - return true; - } - - static bool _defaultValidator(Message message) => - message.text?.isNotEmpty == true || message.attachments.isNotEmpty; - - static bool _defaultSendMessageKeyPredicate( - FocusNode node, - KeyEvent event, - ) { - if (CurrentPlatform.isWeb || - CurrentPlatform.isMacOS || - CurrentPlatform.isWindows || - CurrentPlatform.isLinux) { - // On desktop/web, send the message when the user presses the enter key. - return event is KeyUpEvent && - event.logicalKey == LogicalKeyboardKey.enter; - } - - return false; - } - - static bool _defaultClearQuotedMessageKeyPredicate( - FocusNode node, - KeyEvent event, - ) { - if (CurrentPlatform.isWeb || - CurrentPlatform.isMacOS || - CurrentPlatform.isWindows || - CurrentPlatform.isLinux) { - // On desktop/web, clear the quoted message when the user presses the escape key. - return event is KeyUpEvent && - event.logicalKey == LogicalKeyboardKey.escape; - } - - return false; - } - - @override - StreamMessageInputState createState() => StreamMessageInputState(); -} - -/// State of [StreamMessageInput] -class StreamMessageInputState extends State - with RestorationMixin, WidgetsBindingObserver { - bool get _commandEnabled => _effectiveController.message.command != null; - - bool _actionsShrunk = false; - - late StreamChatThemeData _streamChatTheme; - late StreamMessageInputThemeData _messageInputTheme; - - bool get _hasQuotedMessage => - _effectiveController.message.quotedMessage != null; - - bool get _isEditing => !_effectiveController.message.state.isInitial; - - late final _audioRecorderController = StreamAudioRecorderController(); - - FocusNode get _effectiveFocusNode => - widget.focusNode ?? (_focusNode ??= FocusNode()); - FocusNode? _focusNode; - - StreamMessageInputController get _effectiveController => - widget.messageInputController ?? _controller!.value; - StreamRestorableMessageInputController? _controller; - - void _createLocalController([Message? message]) { - assert(_controller == null, ''); - _controller = StreamRestorableMessageInputController(message: message); - } - - void _registerController() { - assert(_controller != null, ''); - - registerForRestoration(_controller!, 'messageInputController'); - _effectiveController - ..removeListener(_onChangedDebounced) - ..addListener(_onChangedDebounced); - if (!_isEditing && _timeOut <= 0) _startSlowMode(); - } - - void _initialiseEffectiveController() { - _effectiveController - ..removeListener(_onChangedDebounced) - ..addListener(_onChangedDebounced); - if (!_isEditing && _timeOut <= 0) _startSlowMode(); - } - - @override - void initState() { - super.initState(); - WidgetsBinding.instance.addObserver(this); - if (widget.messageInputController == null) { - _createLocalController(); - } else { - _initialiseEffectiveController(); - } - _effectiveFocusNode.addListener(_focusNodeListener); - } - - @override - void didChangeDependencies() { - _streamChatTheme = StreamChatTheme.of(context); - _messageInputTheme = StreamMessageInputTheme.of(context); - super.didChangeDependencies(); - } - - bool _askingForPermission = false; - - @override - void didChangeAppLifecycleState(AppLifecycleState state) async { - if (state == AppLifecycleState.resumed && - _permissionState != null && - !_askingForPermission) { - _askingForPermission = true; - - try { - final newPermissionState = await PhotoManager.requestPermissionExtend(); - if (newPermissionState != _permissionState && mounted) { - setState(() { - _permissionState = newPermissionState; - }); - } - } catch (_) {} - - _askingForPermission = false; - } - super.didChangeAppLifecycleState(state); - } - - @override - void didUpdateWidget(covariant StreamMessageInput oldWidget) { - super.didUpdateWidget(oldWidget); - if (widget.messageInputController == null && - oldWidget.messageInputController != null) { - _createLocalController(oldWidget.messageInputController!.message); - } else if (widget.messageInputController != null && - oldWidget.messageInputController == null) { - unregisterFromRestoration(_controller!); - _controller!.dispose(); - _controller = null; - _initialiseEffectiveController(); - } - - // Update _focusNode - if (widget.focusNode != oldWidget.focusNode) { - (oldWidget.focusNode ?? _focusNode)?.removeListener(_focusNodeListener); - (widget.focusNode ?? _focusNode)?.addListener(_focusNodeListener); - } - } - - @override - void restoreState(RestorationBucket? oldBucket, bool initialRestore) { - if (_controller != null) { - _registerController(); - } - } - - @override - String? get restorationId => widget.restorationId; - - // ignore: no-empty-block - void _focusNodeListener() {} - - int _timeOut = 0; - Timer? _slowModeTimer; - - PermissionState? _permissionState; - - void _startSlowMode() { - if (!mounted) { - return; - } - final channel = StreamChannel.of(context).channel; - final cooldownStartedAt = channel.cooldownStartedAt; - if (cooldownStartedAt != null) { - final diff = DateTime.now().difference(cooldownStartedAt).inSeconds; - if (diff < channel.cooldown) { - _timeOut = channel.cooldown - diff; - if (_timeOut > 0) { - _slowModeTimer = Timer.periodic(const Duration(seconds: 1), (timer) { - if (_timeOut == 0) { - timer.cancel(); - } else { - if (mounted) { - setState(() => _timeOut -= 1); - } - } - }); - } - } - } - } - - void _stopSlowMode() => _slowModeTimer?.cancel(); - - @override - Widget build(BuildContext context) { - final channel = StreamChannel.of(context).channel; - if (channel.state != null && - !channel.ownCapabilities.contains(PermissionType.sendMessage)) { - return SafeArea( - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 24, - vertical: 15, - ), - child: Text( - context.translations.sendMessagePermissionError, - style: _messageInputTheme.inputTextStyle, - ), - ), - ); - } - - return StreamMessageValueListenableBuilder( - valueListenable: _effectiveController, - builder: (context, value, _) { - Widget child = DecoratedBox( - decoration: BoxDecoration( - color: _messageInputTheme.inputBackgroundColor, - boxShadow: widget.shadow == null - ? (_streamChatTheme.messageInputTheme.shadow == null - ? [] - : [_streamChatTheme.messageInputTheme.shadow!]) - : [widget.shadow!], - ), - child: SimpleSafeArea( - enabled: widget.enableSafeArea ?? - _streamChatTheme.messageInputTheme.enableSafeArea ?? - true, - child: GestureDetector( - onPanUpdate: (details) { - if (details.delta.dy > 0) { - _effectiveFocusNode.unfocus(); - } - }, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (_hasQuotedMessage && !_isEditing) - // Ensure this doesn't show on web & desktop - PlatformWidgetBuilder( - mobile: (context, child) => child, - child: QuotingMessageTopArea( - hasQuotedMessage: _hasQuotedMessage, - onQuotedMessageCleared: widget.onQuotedMessageCleared, - ), - ) - else if (_effectiveController.ogAttachment != null) - OGAttachmentPreview( - attachment: _effectiveController.ogAttachment!, - onDismissPreviewPressed: () { - _effectiveController.clearOGAttachment(); - _effectiveFocusNode.unfocus(); - }, - ), - Padding( - padding: const EdgeInsets.all(8), - child: _buildTextField(context), - ), - if (_effectiveController.message.parentId != null && - !widget.hideSendAsDm) - Padding( - padding: const EdgeInsets.only( - right: 16, - left: 16, - bottom: 12, - ), - child: DmCheckbox( - foregroundDecoration: BoxDecoration( - border: _effectiveController.showInChannel - ? null - : Border.all( - color: _streamChatTheme - .colorTheme.textHighEmphasis - // ignore: deprecated_member_use - .withOpacity(0.5), - width: 2, - ), - borderRadius: BorderRadius.circular(3), - ), - color: _effectiveController.showInChannel - ? _streamChatTheme.colorTheme.accentPrimary - : _streamChatTheme.colorTheme.barsBg, - onTap: () { - _effectiveController.showInChannel = - !_effectiveController.showInChannel; - }, - crossFadeState: _effectiveController.showInChannel - ? CrossFadeState.showFirst - : CrossFadeState.showSecond, - ), - ), - ], - ), - ), - ), - ); - if (!_isEditing) { - child = Material( - elevation: widget.elevation ?? - _streamChatTheme.messageInputTheme.elevation ?? - 8, - color: _messageInputTheme.inputBackgroundColor, - child: child, - ); - } - - return StreamAutocomplete( - focusNode: _effectiveFocusNode, - messageEditingController: _effectiveController, - fieldViewBuilder: (_, __, ___) => child, - autocompleteTriggers: [ - ...widget.customAutocompleteTriggers, - StreamAutocompleteTrigger( - trigger: _kCommandTrigger, - triggerOnlyAtStart: true, - optionsViewBuilder: ( - context, - autocompleteQuery, - messageEditingController, - ) { - final query = autocompleteQuery.query; - return StreamCommandAutocompleteOptions( - query: query, - channel: StreamChannel.of(context).channel, - onCommandSelected: (command) { - _effectiveController.command = command.name; - // removing the overlay after the command is selected - StreamAutocomplete.of(context).closeSuggestions(); - }, - ); - }, - ), - if (widget.enableMentionsOverlay) - StreamAutocompleteTrigger( - trigger: _kMentionTrigger, - optionsViewBuilder: ( - context, - autocompleteQuery, - messageEditingController, - ) { - final query = autocompleteQuery.query; - return StreamMentionAutocompleteOptions( - query: query, - channel: StreamChannel.of(context).channel, - mentionAllAppUsers: widget.mentionAllAppUsers, - mentionsTileBuilder: widget.userMentionsTileBuilder, - onMentionUserTap: (user) { - // adding the mentioned user to the controller. - _effectiveController.addMentionedUser(user); - - // accepting the autocomplete option. - StreamAutocomplete.of(context) - .acceptAutocompleteOption(user.name); - }, - ); - }, - ), - ], - ); - }, - ); - } - - Widget _buildTextField(BuildContext context) { - return ValueListenableBuilder( - valueListenable: _audioRecorderController, - builder: (context, state, _) { - final isAudioRecordingFlowActive = state is! RecordStateIdle; - - return Row( - children: [ - if (!isAudioRecordingFlowActive) ...[ - if (!_commandEnabled && - widget.actionsLocation == ActionsLocation.left) - _buildExpandActionsButton(context), - const SizedBox(width: 4), - Expanded(child: _buildTextInput(context)), - const SizedBox(width: 4), - if (!_commandEnabled && - widget.actionsLocation == ActionsLocation.right) - _buildExpandActionsButton(context), - if (widget.sendButtonLocation == SendButtonLocation.outside) - _buildSendButton(context), - ], - if (widget.enableVoiceRecording) - Expanded( - // This is to make sure the audio recorder button will be given - // the full width when it's visible. - flex: isAudioRecordingFlowActive ? 1 : 0, - child: StreamAudioRecorderButton( - recordState: state, - onRecordStart: _audioRecorderController.startRecord, - onRecordCancel: _audioRecorderController.cancelRecord, - onRecordStop: _audioRecorderController.stopRecord, - onRecordLock: _audioRecorderController.lockRecord, - onRecordDragUpdate: _audioRecorderController.dragRecord, - onRecordStartCancel: () { - // Show a message to the user to hold to record. - _audioRecorderController.showInfo( - context.translations.holdToRecordLabel, - ); - }, - onRecordFinish: () async { - //isVoiceRecordingConfirmationRequiredEnabled - // Finish the recording session and add the audio to the - // message input controller. - final audio = await _audioRecorderController.finishRecord(); - if (audio != null) { - _effectiveController.addAttachment(audio); - } - - // Once the recording is finished, cancel the recorder. - _audioRecorderController.cancelRecord(discardTrack: false); - - // Send the message if the user has enabled the option to - // send the voice recording automatically. - if (widget.sendVoiceRecordingAutomatically) { - return sendMessage(); - } - }, - ), - ), - ], - ); - }, - ); - } - - Widget _buildSendButton(BuildContext context) { - if (widget.sendButtonBuilder != null) { - return widget.sendButtonBuilder!(context, _effectiveController); - } - - return StreamMessageSendButton( - onSendMessage: sendMessage, - timeOut: _timeOut, - isIdle: !widget.validator(_effectiveController.message), - idleSendButton: widget.idleSendButton, - activeSendButton: widget.activeSendButton, - ); - } - - Widget _buildExpandActionsButton(BuildContext context) { - return AnimatedCrossFade( - duration: const Duration(milliseconds: 200), - crossFadeState: switch (widget.enableActionAnimation && _actionsShrunk) { - true => CrossFadeState.showFirst, - false => CrossFadeState.showSecond, - }, - layoutBuilder: (top, topKey, bottom, bottomKey) => Stack( - clipBehavior: Clip.none, - alignment: Alignment.center, - children: [ - Positioned(key: bottomKey, top: 0, child: bottom), - Positioned(key: topKey, child: top), - ], - ), - firstChild: StreamMessageInputIconButton( - color: _messageInputTheme.expandButtonColor, - icon: Transform.rotate( - angle: (widget.actionsLocation == ActionsLocation.right || - widget.actionsLocation == ActionsLocation.rightInside) - ? pi - : 0, - child: const StreamSvgIcon(icon: StreamSvgIcons.emptyCircleRight), - ), - onPressed: () { - if (_actionsShrunk) { - setState(() => _actionsShrunk = false); - } - }, - ), - secondChild: widget.disableAttachments && - !widget.showCommandsButton && - !(widget.actionsBuilder != null) - ? const Offstage() - : Row( - spacing: widget.spaceBetweenActions, - mainAxisSize: MainAxisSize.min, - children: _actionsList(), - ), - ); - } - - List _actionsList() { - final channel = StreamChannel.of(context).channel; - final defaultActions = [ - if (!widget.disableAttachments && - channel.ownCapabilities.contains(PermissionType.uploadFile)) - _buildAttachmentButton(context), - if (widget.showCommandsButton && - !_isEditing && - channel.state != null && - channel.config?.commands.isNotEmpty == true) - _buildCommandButton(context), - ]; - - if (widget.actionsBuilder case final builder?) { - return builder(context, defaultActions); - } - - return defaultActions; - } - - Widget _buildAttachmentButton(BuildContext context) { - final defaultButton = AttachmentButton( - color: _messageInputTheme.actionButtonIdleColor, - onPressed: _onAttachmentButtonPressed, - ); - - return widget.attachmentButtonBuilder?.call(context, defaultButton) ?? - defaultButton; - } - - Future _sendPoll(Poll poll) { - final streamChannel = StreamChannel.of(context); - final channel = streamChannel.channel; - - return channel.sendPoll(poll); - } - - Future _updatePoll(Poll poll) { - final streamChannel = StreamChannel.of(context); - final channel = streamChannel.channel; - - return channel.updatePoll(poll); - } - - Future _deletePoll(Poll poll) { - final streamChannel = StreamChannel.of(context); - final channel = streamChannel.channel; - - return channel.deletePoll(poll); - } - - Future _createOrUpdatePoll(Poll? old, Poll? current) async { - // If both are null or the same, return - if ((old == null && current == null) || old == current) return; - - // If old is null, i.e., there was no poll before, create the poll. - if (old == null) return _sendPoll(current!); - - // If current is null, i.e., the poll is removed, delete the poll. - if (current == null) return _deletePoll(old); - - // Otherwise, update the poll. - return _updatePoll(current); - } - - /// Handle the platform-specific logic for selecting files. - /// - /// On mobile, this will open the file selection bottom sheet. On desktop, - /// this will open the native file system and allow the user to select one - /// or more files. - Future _onAttachmentButtonPressed() async { - final initialPoll = _effectiveController.poll; - final initialAttachments = _effectiveController.attachments; - - // Remove AttachmentPickerType.poll if the user doesn't have the permission - // to send a poll or if this is a thread message. - final allowedTypes = [...widget.allowedAttachmentPickerTypes] - ..removeWhere((it) { - if (it != AttachmentPickerType.poll) return false; - if (_effectiveController.message.parentId != null) return true; - final channel = StreamChannel.of(context).channel; - if (channel.ownCapabilities.contains(PermissionType.sendPoll)) { - return false; - } - - return true; - }); - - final value = await showStreamAttachmentPickerModalBottomSheet( - context: context, - onError: widget.onError, - allowedTypes: allowedTypes, - pollConfig: widget.pollConfig, - initialPoll: initialPoll, - initialAttachments: initialAttachments, - useNativeAttachmentPickerOnMobile: - widget.useNativeAttachmentPickerOnMobile, - ); - - if (value == null || value is! AttachmentPickerValue) return; - - // Add the attachments to the controller. - _effectiveController.attachments = value.attachments; - - // Create or update the poll. - await _createOrUpdatePoll(initialPoll, value.poll); - } - - Widget _buildTextInput(BuildContext context) { - final margin = (widget.sendButtonLocation == SendButtonLocation.inside - ? const EdgeInsets.only(right: 8) - : EdgeInsets.zero) + - (widget.actionsLocation != ActionsLocation.left || _commandEnabled - ? const EdgeInsets.only(left: 8) - : EdgeInsets.zero); - - return DropTarget( - onDragDone: (details) async { - final files = details.files; - final attachments = []; - for (final file in files) { - final attachment = await file.toAttachment(type: AttachmentType.file); - attachments.add(attachment); - } - - if (attachments.isNotEmpty) _addAttachments(attachments); - }, - onDragEntered: (details) { - setState(() {}); - }, - onDragExited: (details) {}, - child: Container( - margin: margin, - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration( - borderRadius: _messageInputTheme.borderRadius, - color: _messageInputTheme.inputBackgroundColor, - border: GradientBoxBorder( - gradient: _effectiveFocusNode.hasFocus - ? _messageInputTheme.activeBorderGradient! - : _messageInputTheme.idleBorderGradient!, - ), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildReplyToMessage(), - _buildAttachments(), - LimitedBox( - maxHeight: widget.maxHeight, - child: PlatformWidgetBuilder( - web: (context, child) => Focus( - skipTraversal: true, - onKeyEvent: _handleKeyPressed, - child: child!, - ), - desktop: (context, child) => Focus( - skipTraversal: true, - onKeyEvent: _handleKeyPressed, - child: child!, - ), - mobile: (context, child) => Focus( - skipTraversal: true, - onKeyEvent: _handleKeyPressed, - child: child!, - ), - child: StreamMessageTextField( - key: const Key('messageInputText'), - maxLines: widget.maxLines, - minLines: widget.minLines, - textInputAction: widget.textInputAction, - onSubmitted: (_) => sendMessage(), - keyboardType: widget.keyboardType, - controller: _effectiveController, - focusNode: _effectiveFocusNode, - style: _messageInputTheme.inputTextStyle, - autofocus: widget.autofocus, - textAlignVertical: TextAlignVertical.center, - decoration: _getInputDecoration(context), - textCapitalization: widget.textCapitalization, - autocorrect: widget.autoCorrect, - contentInsertionConfiguration: - widget.contentInsertionConfiguration, - ), - ), - ), - ], - ), - ), - ); - } - - KeyEventResult _handleKeyPressed(FocusNode node, KeyEvent event) { - // Check for send message key. - if (widget.sendMessageKeyPredicate(node, event)) { - sendMessage(); - return KeyEventResult.handled; - } - - // Check for clear quoted message key. - if (widget.clearQuotedMessageKeyPredicate(node, event)) { - if (_hasQuotedMessage && _effectiveController.text.isEmpty) { - widget.onQuotedMessageCleared?.call(); - } - return KeyEventResult.handled; - } - - // Return ignored to allow other key events to be handled. - return KeyEventResult.ignored; - } - - InputDecoration _getInputDecoration(BuildContext context) { - final passedDecoration = _messageInputTheme.inputDecoration; - return InputDecoration( - isDense: true, - hintText: _getHint(context), - hintStyle: _messageInputTheme.inputTextStyle!.copyWith( - color: _streamChatTheme.colorTheme.textLowEmphasis, - ), - border: const OutlineInputBorder( - borderSide: BorderSide( - color: Colors.transparent, - ), - ), - focusedBorder: const OutlineInputBorder( - borderSide: BorderSide( - color: Colors.transparent, - ), - ), - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide( - color: Colors.transparent, - ), - ), - errorBorder: const OutlineInputBorder( - borderSide: BorderSide( - color: Colors.transparent, - ), - ), - disabledBorder: const OutlineInputBorder( - borderSide: BorderSide( - color: Colors.transparent, - ), - ), - contentPadding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16), - prefixIcon: _commandEnabled - ? Container( - margin: const EdgeInsets.all(6), - padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), - decoration: BoxDecoration( - color: _streamChatTheme.colorTheme.accentPrimary, - borderRadius: _messageInputTheme.borderRadius?.add( - BorderRadius.circular(6), - ), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const StreamSvgIcon( - size: 16, - color: Colors.white, - icon: StreamSvgIcons.lightning, - ), - Text( - _effectiveController.message.command!.toUpperCase(), - style: _streamChatTheme.textTheme.footnoteBold.copyWith( - color: Colors.white, - ), - ), - ], - ), - ) - : (widget.actionsLocation == ActionsLocation.leftInside - ? Row( - mainAxisSize: MainAxisSize.min, - children: [_buildExpandActionsButton(context)], - ) - : null), - suffixIconConstraints: const BoxConstraints.tightFor(height: 40), - prefixIconConstraints: const BoxConstraints.tightFor(height: 40), - suffixIcon: Row( - mainAxisSize: MainAxisSize.min, - children: [ - if (_commandEnabled) - Padding( - padding: const EdgeInsets.only(right: 8), - child: StreamMessageInputIconButton( - iconSize: 24, - color: _messageInputTheme.actionButtonIdleColor, - icon: const StreamSvgIcon(icon: StreamSvgIcons.closeSmall), - onPressed: _effectiveController.clear, - ), - ), - if (!_commandEnabled && - widget.actionsLocation == ActionsLocation.rightInside) - _buildExpandActionsButton(context), - if (widget.sendButtonLocation == SendButtonLocation.inside) - _buildSendButton(context), - ].nonNulls.toList(), - ), - ).merge(passedDecoration); - } - - late final _onChangedDebounced = debounce( - () { - var value = _effectiveController.text; - if (!mounted) return; - value = value.trim(); - - final channel = StreamChannel.of(context).channel; - if (value.isNotEmpty && - channel.ownCapabilities.contains(PermissionType.sendTypingEvents)) { - // Notify the server that the user started typing. - channel.keyStroke(_effectiveController.message.parentId).onError( - (error, stackTrace) { - widget.onError?.call(error!, stackTrace); - }, - ); - } - - int actionsLength; - if (widget.actionsBuilder != null) { - actionsLength = widget.actionsBuilder!(context, []).length; - } else { - actionsLength = 0; - } - if (widget.showCommandsButton) actionsLength += 1; - if (!widget.disableAttachments) actionsLength += 1; - - setState(() => _actionsShrunk = value.isNotEmpty && actionsLength > 1); - - _checkContainsUrl(value, context); - }, - const Duration(milliseconds: 350), - leading: true, - ); - - String? _getHint(BuildContext context) { - HintType hintType; - - if (_commandEnabled && _effectiveController.message.command == 'giphy') { - hintType = HintType.searchGif; - } else if (_effectiveController.attachments.isNotEmpty) { - hintType = HintType.addACommentOrSend; - } else if (_timeOut != 0) { - hintType = HintType.slowModeOn; - } else { - hintType = HintType.writeAMessage; - } - - return widget.hintGetter.call(context, hintType); - } - - String? _lastSearchedContainsUrlText; - CancelableOperation? _enrichUrlOperation; - final _urlRegex = RegExp( - r'https?://(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)', - caseSensitive: false, - ); - - void _checkContainsUrl(String value, BuildContext context) async { - // Cancel the previous operation if it's still running - _enrichUrlOperation?.cancel(); - - // If the text is same as the last time, don't do anything - if (_lastSearchedContainsUrlText == value) return; - _lastSearchedContainsUrlText = value; - - final matchedUrls = _urlRegex.allMatches(value).where((it) { - final _parsedMatch = Uri.tryParse(it.group(0) ?? '')?.withScheme; - if (_parsedMatch == null) return false; - - return _parsedMatch.host.split('.').last.isValidTLD() && - widget.ogPreviewFilter.call(_parsedMatch, value); - }).toList(); - - // Reset the og attachment if the text doesn't contain any url - if (matchedUrls.isEmpty || - !StreamChannel.of(context) - .channel - .ownCapabilities - .contains(PermissionType.sendLinks)) { - _effectiveController.clearOGAttachment(); - return; - } - - final firstMatchedUrl = matchedUrls.first.group(0)!; - - // If the parsed url matches the ogAttachment url, don't do anything - if (_effectiveController.ogAttachment?.titleLink == firstMatchedUrl) { - return; - } - - final client = StreamChat.of(context).client; - - _enrichUrlOperation = CancelableOperation.fromFuture( - _enrichUrl(firstMatchedUrl, client), - ).then( - (ogAttachment) { - final attachment = Attachment.fromOGAttachment(ogAttachment); - _effectiveController.setOGAttachment(attachment); - }, - onError: (error, stackTrace) { - // Reset the ogAttachment if there was an error - _effectiveController.clearOGAttachment(); - widget.onError?.call(error, stackTrace); - }, - ); - } - - final _ogAttachmentCache = {}; - - Future _enrichUrl( - String url, - StreamChatClient client, - ) async { - var response = _ogAttachmentCache[url]; - if (response == null) { - final client = StreamChat.of(context).client; - try { - response = await client.enrichUrl(url); - _ogAttachmentCache[url] = response; - } catch (e, stk) { - return Future.error(e, stk); - } - } - return response; - } - - Widget _buildReplyToMessage() { - if (!_hasQuotedMessage) return const Offstage(); - final quotedMessage = _effectiveController.message.quotedMessage!; - - final quotedMessageBuilder = widget.quotedMessageBuilder; - if (quotedMessageBuilder != null) { - return quotedMessageBuilder( - context, - _effectiveController.message.quotedMessage!, - ); - } - - final containsUrl = quotedMessage.attachments.any((it) { - return it.type == AttachmentType.urlPreview; - }); - - return StreamQuotedMessageWidget( - reverse: true, - showBorder: !containsUrl, - message: quotedMessage, - messageTheme: _streamChatTheme.otherMessageTheme, - onQuotedMessageClear: widget.onQuotedMessageCleared, - attachmentThumbnailBuilders: - widget.quotedMessageAttachmentThumbnailBuilders, - ); - } - - Widget _buildAttachments() { - final attachments = _effectiveController.attachments; - final nonOGAttachments = attachments.where((it) { - return it.titleLink == null; - }).toList(growable: false); - - // If there are no attachments, return an empty widget - if (nonOGAttachments.isEmpty) return const Offstage(); - - // If the user has provided a custom attachment list builder, use that. - final attachmentListBuilder = widget.attachmentListBuilder; - if (attachmentListBuilder != null) { - return attachmentListBuilder( - context, - nonOGAttachments, - _onAttachmentRemovePressed, - ); - } - - // Otherwise, use the default attachment list builder. - return LimitedBox( - maxHeight: 240, - child: StreamMessageInputAttachmentList( - attachments: nonOGAttachments, - onRemovePressed: _onAttachmentRemovePressed, - fileAttachmentListBuilder: widget.fileAttachmentListBuilder, - mediaAttachmentListBuilder: widget.mediaAttachmentListBuilder, - voiceRecordingAttachmentBuilder: widget.voiceRecordingAttachmentBuilder, - fileAttachmentBuilder: widget.fileAttachmentBuilder, - mediaAttachmentBuilder: widget.mediaAttachmentBuilder, - voiceRecordingAttachmentListBuilder: - widget.voiceRecordingAttachmentListBuilder, - ), - ); - } - - // Default callback for removing an attachment. - Future _onAttachmentRemovePressed(Attachment attachment) async { - final file = attachment.file; - final uploadState = attachment.uploadState; - - if (file != null && !uploadState.isSuccess && !isWeb) { - await StreamAttachmentHandler.instance.deleteAttachmentFile( - attachmentFile: file, - ); - } - - _effectiveController.removeAttachmentById(attachment.id); - } - - Widget _buildCommandButton(BuildContext context) { - final s = _effectiveController.text.trim(); - final isCommandOptionsVisible = s.startsWith(_kCommandTrigger); - final defaultButton = CommandButton( - color: s.isNotEmpty - ? _streamChatTheme.colorTheme.disabled - : (isCommandOptionsVisible - ? _messageInputTheme.actionButtonColor! - : _messageInputTheme.actionButtonIdleColor!), - onPressed: () async { - // Clear the text if the commands options are already visible. - if (isCommandOptionsVisible) { - _effectiveController.clear(); - _effectiveFocusNode.unfocus(); - } else { - // This triggers the [StreamAutocomplete] to show the command trigger. - _effectiveController.textEditingValue = const TextEditingValue( - text: _kCommandTrigger, - selection: TextSelection.collapsed(offset: _kCommandTrigger.length), - ); - _effectiveFocusNode.requestFocus(); - } - }, - ); - - return widget.commandButtonBuilder?.call(context, defaultButton) ?? - defaultButton; - } - - /// Adds an attachment to the [messageInputController.attachments] map - void _addAttachments(Iterable attachments) { - final limit = widget.attachmentLimit; - final length = _effectiveController.attachments.length + attachments.length; - if (length > limit) { - final onAttachmentLimitExceed = widget.onAttachmentLimitExceed; - if (onAttachmentLimitExceed != null) { - return onAttachmentLimitExceed( - widget.attachmentLimit, - context.translations.attachmentLimitExceedError(limit), - ); - } - return _showErrorAlert( - context.translations.attachmentLimitExceedError(limit), - ); - } - for (final attachment in attachments) { - _effectiveController.addAttachment(attachment); - } - } - - /// Sends the current message - Future sendMessage() async { - if (_timeOut > 0 || - (_effectiveController.text.trim().isEmpty && - _effectiveController.attachments.isEmpty)) { - return; - } - - final streamChannel = StreamChannel.of(context); - final channel = streamChannel.channel; - var message = _effectiveController.value; - - if (!channel.ownCapabilities.contains(PermissionType.sendLinks) && - _urlRegex.allMatches(message.text ?? '').any((element) => - element.group(0)?.split('.').last.isValidTLD() == true)) { - showInfoBottomSheet( - context, - icon: StreamSvgIcon( - icon: StreamSvgIcons.error, - color: StreamChatTheme.of(context).colorTheme.accentError, - size: 24, - ), - title: 'Links are disabled', - details: 'Sending links is not allowed in this conversation.', - okText: context.translations.okLabel, - ); - return; - } - - final containsCommand = message.command != null; - // If the message contains command we should append it to the text - // before sending it. - if (containsCommand) { - message = message.copyWith(text: '/${message.command} ${message.text}'); - } - - var shouldKeepFocus = widget.shouldKeepFocusAfterMessage; - shouldKeepFocus ??= !_commandEnabled; - - widget.onQuotedMessageCleared?.call(); - - _effectiveController.reset(); - - if (widget.preMessageSending != null) { - message = await widget.preMessageSending!(message); - } - - message = message.replaceMentionsWithId(); - - // If the channel is not up to date, we should reload it before sending - // the message. - if (!channel.state!.isUpToDate) { - await streamChannel.reloadChannel(); - - // We need to wait for the frame to be rendered with the updated channel - // state before sending the message. - await WidgetsBinding.instance.endOfFrame; - } - - await _sendOrUpdateMessage(message: message); - - if (mounted) { - if (shouldKeepFocus) { - FocusScope.of(context).requestFocus(_effectiveFocusNode); - } else { - FocusScope.of(context).unfocus(); - } - } - } - - Future _sendOrUpdateMessage({ - required Message message, - }) async { - final channel = StreamChannel.of(context).channel; - - try { - Future sendingFuture; - if (_isEditing) { - sendingFuture = channel.updateMessage(message); - } else { - sendingFuture = channel.sendMessage(message); - } - - final resp = await sendingFuture; - if (resp.message?.isError ?? false) { - _effectiveController.message = message; - } - _startSlowMode(); - widget.onMessageSent?.call(resp.message); - } catch (e, stk) { - if (widget.onError != null) { - return widget.onError?.call(e, stk); - } - - rethrow; - } - } - - void _showErrorAlert(String description) { - showModalBottomSheet( - backgroundColor: _streamChatTheme.colorTheme.barsBg, - context: context, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16), - ), - ), - builder: (context) => ErrorAlertSheet( - errorDescription: context.translations.somethingWentWrongError, - ), - ); - } - - @override - void dispose() { - _effectiveController.removeListener(_onChangedDebounced); - _controller?.dispose(); - _effectiveFocusNode.removeListener(_focusNodeListener); - _focusNode?.dispose(); - _stopSlowMode(); - _onChangedDebounced.cancel(); - _audioRecorderController.dispose(); - WidgetsBinding.instance.removeObserver(this); - super.dispose(); - } -} - -/// Preview of an Open Graph attachment. -class OGAttachmentPreview extends StatelessWidget { - /// Returns a new instance of [OGAttachmentPreview] - const OGAttachmentPreview({ - super.key, - required this.attachment, - this.onDismissPreviewPressed, - }); - - /// The attachment to be rendered. - final Attachment attachment; - - /// Called when the dismiss button is pressed. - final VoidCallback? onDismissPreviewPressed; - - @override - Widget build(BuildContext context) { - final chatTheme = StreamChatTheme.of(context); - final textTheme = chatTheme.textTheme; - final colorTheme = chatTheme.colorTheme; - - final attachmentTitle = attachment.title; - final attachmentText = attachment.text; - - return Row( - children: [ - Padding( - padding: const EdgeInsets.all(8), - child: StreamSvgIcon( - icon: StreamSvgIcons.link, - color: colorTheme.accentPrimary, - ), - ), - Expanded( - child: Container( - decoration: BoxDecoration( - border: Border( - left: BorderSide( - color: colorTheme.accentPrimary, - width: 2, - ), - ), - ), - padding: const EdgeInsets.only(left: 6), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (attachmentTitle != null) - Text( - attachmentTitle.trim(), - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: textTheme.body.copyWith(fontWeight: FontWeight.w700), - ), - if (attachmentText != null) - Text( - attachmentText, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: textTheme.body.copyWith(fontWeight: FontWeight.w400), - ), - ], - ), - ), - ), - IconButton( - visualDensity: VisualDensity.compact, - icon: const StreamSvgIcon(icon: StreamSvgIcons.closeSmall), - onPressed: onDismissPreviewPressed, - ), - ], - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input_attachment_list.dart b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input_attachment_list.dart deleted file mode 100644 index 436312b3cb..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input_attachment_list.dart +++ /dev/null @@ -1,477 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/attachment/file_attachment.dart'; -import 'package:stream_chat_flutter/src/attachment/thumbnail/media_attachment_thumbnail.dart'; -import 'package:stream_chat_flutter/src/attachment/voice_recording_attachment.dart'; -import 'package:stream_chat_flutter/src/audio/audio_playlist_controller.dart'; -import 'package:stream_chat_flutter/src/icons/stream_svg_icon.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter/src/utils/utils.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// WidgetBuilder used to build the message input attachment list. -/// -/// see more: -/// - [StreamMessageInputAttachmentList] -typedef AttachmentListBuilder = Widget Function( - BuildContext context, - List attachments, - ValueSetter? onRemovePressed, -); - -/// WidgetBuilder used to build the message input attachment item. -/// -/// see more: -/// - [StreamMessageInputAttachmentList] -typedef AttachmentItemBuilder = Widget Function( - BuildContext context, - Attachment attachment, - ValueSetter? onRemovePressed, -); - -/// {@template stream_message_input_attachment_list} -/// Widget used to display the list of attachments added to the message input. -/// -/// By default, it displays the list of file attachments and media attachments -/// separately. -/// -/// You can customize the list of file attachments and media attachments using -/// [fileAttachmentListBuilder] and [mediaAttachmentListBuilder] respectively. -/// -/// You can also customize the attachment item using [fileAttachmentBuilder] and -/// [mediaAttachmentBuilder] respectively. -/// -/// You can override the default action of removing an attachment by providing -/// [onRemovePressed]. -/// {@endtemplate} -class StreamMessageInputAttachmentList extends StatelessWidget { - /// {@macro stream_message_input_attachment_list} - const StreamMessageInputAttachmentList({ - super.key, - required this.attachments, - this.onRemovePressed, - this.fileAttachmentBuilder, - this.mediaAttachmentBuilder, - this.voiceRecordingAttachmentBuilder, - this.fileAttachmentListBuilder, - this.mediaAttachmentListBuilder, - this.voiceRecordingAttachmentListBuilder, - }); - - /// List of attachments to display thumbnails for. - /// - /// Open graph should be filtered out. - final Iterable attachments; - - /// Builder used to build the file attachment item. - final AttachmentItemBuilder? fileAttachmentBuilder; - - /// Builder used to build the media attachment item. - final AttachmentItemBuilder? mediaAttachmentBuilder; - - /// Builder used to build the voice recording attachment item. - final AttachmentItemBuilder? voiceRecordingAttachmentBuilder; - - /// Builder used to build the file attachment list. - final AttachmentListBuilder? fileAttachmentListBuilder; - - /// Builder used to build the media attachment list. - final AttachmentListBuilder? mediaAttachmentListBuilder; - - /// Builder used to build the voice recording attachment list. - final AttachmentListBuilder? voiceRecordingAttachmentListBuilder; - - /// Callback called when the remove button is pressed. - final ValueSetter? onRemovePressed; - - @override - Widget build(BuildContext context) { - final groupedAttachments = attachments.groupListsBy((it) => it.type); - final (:files, :media, :voices) = ( - files: [...?groupedAttachments[AttachmentType.file]], - voices: [...?groupedAttachments[AttachmentType.voiceRecording]], - media: [ - ...?groupedAttachments[AttachmentType.image], - ...?groupedAttachments[AttachmentType.video], - ...?groupedAttachments[AttachmentType.giphy], - ...?groupedAttachments[AttachmentType.audio], - ], - ); - - // If there are no attachments, return an empty widget. - if (files.isEmpty && media.isEmpty && voices.isEmpty) { - return const SizedBox.shrink(); - } - - return SingleChildScrollView( - padding: const EdgeInsets.only(top: 6), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (media.isNotEmpty) - Flexible( - child: switch (mediaAttachmentListBuilder) { - final builder? => builder(context, media, onRemovePressed), - _ => MessageInputMediaAttachments( - attachments: media, - attachmentBuilder: mediaAttachmentBuilder, - onRemovePressed: onRemovePressed, - ), - }, - ), - if (voices.isNotEmpty) - Flexible( - child: switch (voiceRecordingAttachmentListBuilder) { - final builder? => builder(context, voices, onRemovePressed), - _ => MessageInputVoiceRecordingAttachments( - attachments: voices, - attachmentBuilder: voiceRecordingAttachmentBuilder, - onRemovePressed: onRemovePressed, - ), - }, - ), - if (files.isNotEmpty) - Flexible( - child: switch (fileAttachmentListBuilder) { - final builder? => builder(context, files, onRemovePressed), - _ => MessageInputFileAttachments( - attachments: files, - attachmentBuilder: fileAttachmentBuilder, - onRemovePressed: onRemovePressed, - ), - }, - ), - ].insertBetween( - Divider( - height: 16, - indent: 16, - endIndent: 16, - thickness: 1, - color: StreamChatTheme.of(context).colorTheme.disabled, - ), - ), - ), - ); - } -} - -/// Widget used to display the list of file type attachments added to the -/// message input. -class MessageInputFileAttachments extends StatelessWidget { - /// Creates a new FileAttachments widget. - const MessageInputFileAttachments({ - super.key, - required this.attachments, - this.attachmentBuilder, - this.onRemovePressed, - }); - - /// List of file type attachments to display thumbnails for. - final List attachments; - - /// Builder used to build the file type attachment item. - final AttachmentItemBuilder? attachmentBuilder; - - /// Callback called when the remove button is pressed. - final ValueSetter? onRemovePressed; - - @override - Widget build(BuildContext context) { - return ListView( - reverse: true, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - padding: const EdgeInsets.symmetric(horizontal: 8), - children: attachments.reversed.map( - (attachment) { - // If a custom builder is provided, use it. - final builder = attachmentBuilder; - if (builder != null) { - return builder(context, attachment, onRemovePressed); - } - - // Otherwise, use the default builder. - return StreamFileAttachment( - message: Message(), // Dummy message - file: attachment, - constraints: BoxConstraints.loose(Size( - MediaQuery.of(context).size.width * 0.65, - 56, - )), - trailing: Padding( - padding: const EdgeInsets.all(8), - child: RemoveAttachmentButton( - onPressed: onRemovePressed != null - ? () => onRemovePressed!(attachment) - : null, - ), - ), - ); - }, - ).insertBetween(const SizedBox(height: 8)), - ); - } -} - -/// Widget used to display the list of voice recording type attachments added to -/// the message input. -class MessageInputVoiceRecordingAttachments extends StatefulWidget { - /// Creates a new MessageInputVoiceRecordingAttachments widget. - const MessageInputVoiceRecordingAttachments({ - super.key, - required this.attachments, - this.attachmentBuilder, - this.onRemovePressed, - }); - - /// List of voice recording type attachments to display thumbnails for. - /// - /// Only attachments of type [AttachmentType.voiceRecording] are supported. - final List attachments; - - /// Builder used to build the voice recording type attachment item. - final AttachmentItemBuilder? attachmentBuilder; - - /// Callback called when the remove button is pressed. - final ValueSetter? onRemovePressed; - - @override - State createState() => - _MessageInputVoiceRecordingAttachmentsState(); -} - -class _MessageInputVoiceRecordingAttachmentsState - extends State { - late final _controller = StreamAudioPlaylistController( - widget.attachments.toPlaylist(), - ); - - @override - void initState() { - super.initState(); - _controller.initialize(); - } - - @override - void didUpdateWidget( - covariant MessageInputVoiceRecordingAttachments oldWidget, - ) { - super.didUpdateWidget(oldWidget); - final equals = const ListEquality().equals; - if (!equals(widget.attachments, oldWidget.attachments)) { - // If the attachments have changed, update the playlist. - _controller.updatePlaylist(widget.attachments.toPlaylist()); - } - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return ValueListenableBuilder( - valueListenable: _controller, - builder: (context, state, _) { - return MediaQuery.removePadding( - context: context, - // Workaround for the bottom padding issue. - // Link: https://github.com/flutter/flutter/issues/156149 - removeTop: true, - removeBottom: true, - child: ListView.separated( - shrinkWrap: true, - padding: const EdgeInsets.symmetric(horizontal: 8), - physics: const NeverScrollableScrollPhysics(), - itemCount: state.tracks.length, - separatorBuilder: (_, __) => const SizedBox(height: 8), - itemBuilder: (context, index) { - final track = state.tracks[index]; - - return StreamVoiceRecordingAttachment( - track: track, - speed: state.speed, - trailingBuilder: (_, __, ___, ____) { - final attachment = widget.attachments[index]; - return RemoveAttachmentButton( - onPressed: switch (widget.onRemovePressed) { - final callback? => () => callback(attachment), - _ => null, - }, - ); - }, - onTrackPause: _controller.pause, - onChangeSpeed: _controller.setSpeed, - onTrackPlay: () async { - // Play the track directly if it is already loaded. - if (state.currentIndex == index) return _controller.play(); - // Otherwise, load the track first and then play it. - return _controller.skipToItem(index); - }, - // Only allow seeking if the current track is the one being - // interacted with. - onTrackSeekStart: (_) async { - if (state.currentIndex != index) return; - return _controller.pause(); - }, - onTrackSeekEnd: (_) async { - if (state.currentIndex != index) return; - return _controller.play(); - }, - onTrackSeekChanged: (progress) async { - if (state.currentIndex != index) return; - - final duration = track.duration.inMicroseconds; - final seekPosition = (duration * progress).toInt(); - final seekDuration = Duration(microseconds: seekPosition); - - return _controller.seek(seekDuration); - }, - ); - }, - ), - ); - }, - ); - } -} - -/// Widget used to display the list of media type attachments added to the -/// message input. -class MessageInputMediaAttachments extends StatelessWidget { - /// Creates a new MediaAttachments widget. - const MessageInputMediaAttachments({ - super.key, - required this.attachments, - this.attachmentBuilder, - this.onRemovePressed, - }); - - /// List of media type attachments to display thumbnails for. - /// - /// Only attachments of type `image`, `video` and `giphy` are supported. Shows - /// a placeholder for other types of attachments. - final List attachments; - - /// Builder used to build the media type attachment item. - final AttachmentItemBuilder? attachmentBuilder; - - /// Callback called when the remove button is pressed. - final ValueSetter? onRemovePressed; - - @override - Widget build(BuildContext context) { - return SizedBox( - height: 104, - child: ListView( - scrollDirection: Axis.horizontal, - padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 8), - cacheExtent: 104 * 10, // Cache 10 items ahead. - children: attachments.map( - (attachment) { - // If a custom builder is provided, use it. - final builder = attachmentBuilder; - if (builder != null) { - return builder(context, attachment, onRemovePressed); - } - - return StreamMediaAttachmentBuilder( - attachment: attachment, - onRemovePressed: onRemovePressed, - ); - }, - ).insertBetween(const SizedBox(width: 8)), - ), - ); - } -} - -/// Widget used to display a media type attachment item. -class StreamMediaAttachmentBuilder extends StatelessWidget { - /// Creates a new media attachment item. - const StreamMediaAttachmentBuilder( - {super.key, required this.attachment, this.onRemovePressed}); - - /// The media attachment to display. - final Attachment attachment; - - /// Callback called when the remove button is pressed. - final ValueSetter? onRemovePressed; - - @override - Widget build(BuildContext context) { - final colorTheme = StreamChatTheme.of(context).colorTheme; - final shape = RoundedRectangleBorder( - side: BorderSide( - color: colorTheme.borders, - strokeAlign: BorderSide.strokeAlignOutside, - ), - borderRadius: BorderRadius.circular(14), - ); - - return Container( - key: Key(attachment.id), - clipBehavior: Clip.hardEdge, - decoration: ShapeDecoration(shape: shape), - child: AspectRatio( - aspectRatio: 1, - child: Stack( - alignment: Alignment.center, - children: [ - StreamMediaAttachmentThumbnail( - media: attachment, - width: double.infinity, - height: double.infinity, - fit: BoxFit.cover, - ), - if (attachment.type == AttachmentType.video) - const Positioned( - left: 8, - bottom: 8, - child: StreamSvgIcon(icon: StreamSvgIcons.videoCall), - ), - Positioned( - top: 8, - right: 8, - child: RemoveAttachmentButton( - onPressed: onRemovePressed != null - ? () => onRemovePressed!(attachment) - : null, - ), - ), - ], - ), - ), - ); - } -} - -/// Material Button used for removing attachments. -class RemoveAttachmentButton extends StatelessWidget { - /// Creates a new remove attachment button. - const RemoveAttachmentButton({super.key, this.onPressed}); - - /// Callback when the remove attachment button is pressed. - final VoidCallback? onPressed; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - final colorTheme = theme.colorTheme; - - return IconButton.filled( - onPressed: onPressed, - color: colorTheme.barsBg, - padding: EdgeInsets.zero, - icon: const StreamSvgIcon(icon: StreamSvgIcons.close), - style: IconButton.styleFrom( - minimumSize: const Size(24, 24), - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - // ignore: deprecated_member_use - backgroundColor: colorTheme.textHighEmphasis.withOpacity(0.6), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input_icon_button.dart b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input_icon_button.dart deleted file mode 100644 index 7708ba8c2d..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input_icon_button.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'package:flutter/material.dart'; - -/// The default size for the icon inside the message input icon button. -const double kDefaultMessageInputIconSize = 32; - -/// The default padding around the icon inside the message input icon button. -const double kDefaultMessageInputIconPadding = 4; - -/// {@template streamMessageInputIconButton} -/// A customized [IconButton] for the message input. -/// -/// This is used to create the send button, command button, and other icon -/// buttons in the message input. -/// {@endtemplate} -class StreamMessageInputIconButton extends StatelessWidget { - /// {@macro streamMessageInputIconButton} - const StreamMessageInputIconButton({ - super.key, - required this.icon, - required this.onPressed, - this.color, - this.disabledColor, - this.iconSize = kDefaultMessageInputIconSize, - this.padding = const EdgeInsets.all(kDefaultMessageInputIconPadding), - }); - - /// The icon to display inside the button. - final Widget icon; - - /// The color to use for the icon inside the button, if the icon is enabled. - /// Defaults to leaving this up to the [icon] widget. - final Color? color; - - /// The color to use for the icon inside the button, if the icon is disabled. - /// - /// The icon is disabled if [onPressed] is null. - final Color? disabledColor; - - /// The size of the icon inside the button. - /// - /// Defaults to 32.0. - final double iconSize; - - /// The padding around the button's icon. The entire padded icon will react - /// to input gestures. - /// - /// Defaults to EdgeInsets.zero. - final EdgeInsetsGeometry padding; - - /// The callback that is called when the button is tapped or otherwise - /// activated. - /// - /// If this is set to null, the button will be disabled. - final VoidCallback? onPressed; - - @override - Widget build(BuildContext context) { - return IconButton( - icon: icon, - color: color, - disabledColor: disabledColor, - iconSize: iconSize, - onPressed: onPressed, - padding: padding, - style: ButtonStyle( - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - minimumSize: WidgetStateProperty.all(Size.square(iconSize)), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/stream_message_send_button.dart b/packages/stream_chat_flutter/lib/src/message_input/stream_message_send_button.dart deleted file mode 100644 index 1dfb29ae57..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/stream_message_send_button.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/message_input/stream_message_input_icon_button.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// A widget that displays a sending button. -class StreamMessageSendButton extends StatelessWidget { - /// Returns a [StreamMessageSendButton] with the given [timeOut], [isIdle], - /// [isCommandEnabled], [isEditEnabled], [idleSendButton], [activeSendButton], - /// [onSendMessage]. - const StreamMessageSendButton({ - super.key, - this.timeOut = 0, - this.isIdle = true, - @Deprecated('Will be removed in the next major version') - this.isCommandEnabled = false, - @Deprecated('Will be removed in the next major version') - this.isEditEnabled = false, - this.idleSendButton, - this.activeSendButton, - required this.onSendMessage, - }); - - /// Time out related to slow mode. - final int timeOut; - - /// If true the button will be disabled. - final bool isIdle; - - /// True if a command is being sent. - @Deprecated('It will be removed in the next major version') - final bool isCommandEnabled; - - /// True if in editing mode. - @Deprecated('It will be removed in the next major version') - final bool isEditEnabled; - - /// The widget to display when the button is disabled. - final Widget? idleSendButton; - - /// The widget to display when the button is enabled. - final Widget? activeSendButton; - - /// The callback to call when the button is pressed. - final VoidCallback onSendMessage; - - @override - Widget build(BuildContext context) { - final theme = StreamMessageInputTheme.of(context); - - final button = _buildButton(context); - return AnimatedSwitcher( - duration: theme.sendAnimationDuration!, - child: button, - ); - } - - Widget _buildButton(BuildContext context) { - if (timeOut > 0) { - return StreamCountdownButton( - key: const Key('countdown_button'), - count: timeOut, - ); - } - - final theme = StreamMessageInputTheme.of(context); - final onPressed = isIdle ? null : onSendMessage; - return StreamMessageInputIconButton( - key: const Key('send_button'), - icon: StreamSvgIcon(icon: _sendButtonIcon), - color: theme.sendButtonColor, - disabledColor: theme.sendButtonIdleColor, - onPressed: onPressed, - ); - } - - StreamSvgIconData get _sendButtonIcon { - if (isIdle) return StreamSvgIcons.sendMessage; - return StreamSvgIcons.circleUp; - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/stream_message_text_field.dart b/packages/stream_chat_flutter/lib/src/message_input/stream_message_text_field.dart deleted file mode 100644 index 0279be811d..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/stream_message_text_field.dart +++ /dev/null @@ -1,719 +0,0 @@ -import 'dart:ui' as ui show BoxHeightStyle, BoxWidthStyle; - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter/services.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -export 'package:flutter/services.dart' - show - TextInputType, - TextInputAction, - TextCapitalization, - SmartQuotesType, - SmartDashesType; - -/// A widget the wraps the [TextField] and adds some StreamChat specifics. -class StreamMessageTextField extends StatefulWidget { - /// Creates a Material Design text field. - /// - /// If [decoration] is non-null (which is the default), the text field - /// requires one of its ancestors to be a [Material] widget. - /// - /// To remove the decoration entirely (including the extra padding introduced - /// by the decoration to save space for the labels), set the [decoration] to - /// null. - /// - /// The [maxLines] property can be set to null to remove the restriction on - /// the number of lines. By default, it is one, meaning this is a single-line - /// text field. [maxLines] must not be zero. - /// - /// The [maxLength] property is set to null by default, which means the - /// number of characters allowed in the text field is not restricted. If - /// [maxLength] is set a character counter will be displayed below the - /// field showing how many characters have been entered. If the value is - /// set to a positive integer it will also display the maximum allowed - /// number of characters to be entered. If the value is set to - /// [TextField.noMaxLength] then only the current length is displayed. - /// - /// After [maxLength] characters have been input, additional input - /// is ignored, unless [maxLengthEnforcement] is set to - /// [MaxLengthEnforcement.none]. - /// The text field enforces the length with a - /// [LengthLimitingTextInputFormatter], - /// which is evaluated after the supplied [inputFormatters], if any. - /// The [maxLength] value must be either null or greater than zero. - /// - /// The text cursor is not shown if [showCursor] is false or if [showCursor] - /// is null (the default) and [readOnly] is true. - /// - /// The [selectionHeightStyle] and [selectionWidthStyle] properties allow - /// changing the shape of the selection highlighting. These properties default - /// to [ui.BoxHeightStyle.tight] and [ui.BoxWidthStyle.tight] respectively and - /// must not be null. - /// - /// The [textAlign], [autofocus], [obscureText], [readOnly], [autocorrect], - /// [scrollPadding], [maxLines], [maxLength], - /// [selectionHeightStyle], [selectionWidthStyle], [enableSuggestions], and - /// [enableIMEPersonalizedLearning] arguments must not be null. - /// - /// See also: - /// - /// * [maxLength], which discusses the precise meaning of "number of - /// characters" and how it may differ from the intuitive meaning. - const StreamMessageTextField({ - super.key, - this.controller, - this.focusNode, - this.decoration = const InputDecoration(), - TextInputType? keyboardType, - this.textInputAction, - this.textCapitalization = TextCapitalization.none, - this.style, - this.strutStyle, - this.textAlign = TextAlign.start, - this.textAlignVertical, - this.textDirection, - this.readOnly = false, - this.showCursor, - this.autofocus = false, - this.obscuringCharacter = '•', - this.obscureText = false, - this.autocorrect = true, - SmartDashesType? smartDashesType, - SmartQuotesType? smartQuotesType, - this.enableSuggestions = true, - this.maxLines, - this.minLines, - this.expands = false, - this.maxLength, - this.maxLengthEnforcement, - this.onChanged, - this.onEditingComplete, - this.onSubmitted, - this.onAppPrivateCommand, - this.inputFormatters, - this.enabled, - this.cursorWidth = 2.0, - this.cursorHeight, - this.cursorRadius, - this.cursorColor, - this.selectionHeightStyle = ui.BoxHeightStyle.tight, - this.selectionWidthStyle = ui.BoxWidthStyle.tight, - this.keyboardAppearance, - this.scrollPadding = const EdgeInsets.all(20), - this.dragStartBehavior = DragStartBehavior.start, - bool? enableInteractiveSelection, - this.selectionControls, - this.onTap, - this.mouseCursor, - this.buildCounter, - this.scrollController, - this.scrollPhysics, - this.autofillHints = const [], - this.clipBehavior = Clip.hardEdge, - this.restorationId, - this.scribbleEnabled = true, - this.enableIMEPersonalizedLearning = true, - this.contentInsertionConfiguration, - }) : assert(obscuringCharacter.length == 1, ''), - smartDashesType = smartDashesType ?? - (obscureText ? SmartDashesType.disabled : SmartDashesType.enabled), - smartQuotesType = smartQuotesType ?? - (obscureText ? SmartQuotesType.disabled : SmartQuotesType.enabled), - assert(maxLines == null || maxLines > 0, ''), - assert(minLines == null || minLines > 0, ''), - assert( - (maxLines == null) || (minLines == null) || (maxLines >= minLines), - "minLines can't be greater than maxLines", - ), - assert( - !expands || (maxLines == null && minLines == null), - 'minLines and maxLines must be null when expands is true.', - ), - assert(!obscureText || maxLines == 1, - 'Obscured fields cannot be multiline.'), - assert( - maxLength == null || - maxLength == TextField.noMaxLength || - maxLength > 0, - 'maxLength must be null or a positive integer.'), - - // Assert the following instead of setting it directly to avoid - // surprising the user by silently changing the value they set. - assert( - !identical(textInputAction, TextInputAction.newline) || - maxLines == 1 || - !identical(keyboardType, TextInputType.text), - 'Use keyboardType TextInputType.multiline when using ' - 'TextInputAction.newline on a multiline TextField.', - ), - keyboardType = keyboardType ?? - (maxLines == 1 ? TextInputType.text : TextInputType.multiline), - enableInteractiveSelection = - enableInteractiveSelection ?? (!readOnly || !obscureText); - - /// Controls the message being edited. - /// - /// If null, this widget will create its own [StreamMessageInputController]. - final StreamMessageInputController? controller; - - /// Defines the keyboard focus for this widget. - /// - /// The [focusNode] is a long-lived object that's typically managed by a - /// [StatefulWidget] parent. See [FocusNode] for more information. - /// - /// To give the keyboard focus to this widget, provide a [focusNode] and then - /// use the current [FocusScope] to request the focus: - /// - /// ```dart - /// FocusScope.of(context).requestFocus(myFocusNode); - /// ``` - /// - /// This happens automatically when the widget is tapped. - /// - /// To be notified when the widget gains or loses the focus, add a listener - /// to the [focusNode]: - /// - /// ```dart - /// focusNode.addListener(() { print(myFocusNode.hasFocus); }); - /// ``` - /// - /// If null, this widget will create its own [FocusNode]. - /// - /// ## Keyboard - /// - /// Requesting the focus will typically cause the keyboard to be shown - /// if it's not showing already. - /// - /// On Android, the user can hide the keyboard - without changing the focus - - /// with the system back button. They can restore the keyboard's visibility - /// by tapping on a text field. The user might hide the keyboard and - /// switch to a physical keyboard, or they might just need to get it - /// out of the way for a moment, to expose something it's - /// obscuring. In this case requesting the focus again will not - /// cause the focus to change, and will not make the keyboard visible. - /// - /// This widget builds an [EditableText] and will ensure that the keyboard is - /// showing when it is tapped by calling - /// [EditableTextState.requestKeyboard()]. - final FocusNode? focusNode; - - /// The decoration to show around the text field. - /// - /// By default, draws a horizontal line under the text field but can be - /// configured to show an icon, label, hint text, and error text. - /// - /// Specify null to remove the decoration entirely (including the - /// extra padding introduced by the decoration to save space for the labels). - final InputDecoration? decoration; - - /// {@macro flutter.widgets.editableText.keyboardType} - final TextInputType keyboardType; - - /// The type of action button to use for the keyboard. - /// - /// Defaults to [TextInputAction.newline] if [keyboardType] is - /// [TextInputType.multiline] and [TextInputAction.done] otherwise. - final TextInputAction? textInputAction; - - /// {@macro flutter.widgets.editableText.textCapitalization} - final TextCapitalization textCapitalization; - - /// The style to use for the text being edited. - /// - /// This text style is also used as the base style for the [decoration]. - /// - /// If null, defaults to the `subtitle1` text style from the current [Theme]. - final TextStyle? style; - - /// {@macro flutter.widgets.editableText.strutStyle} - final StrutStyle? strutStyle; - - /// {@macro flutter.widgets.editableText.textAlign} - final TextAlign textAlign; - - /// {@macro flutter.material.InputDecorator.textAlignVertical} - final TextAlignVertical? textAlignVertical; - - /// {@macro flutter.widgets.editableText.textDirection} - final TextDirection? textDirection; - - /// {@macro flutter.widgets.editableText.autofocus} - final bool autofocus; - - /// {@macro flutter.widgets.editableText.obscuringCharacter} - final String obscuringCharacter; - - /// {@macro flutter.widgets.editableText.obscureText} - final bool obscureText; - - /// {@macro flutter.widgets.editableText.autocorrect} - final bool autocorrect; - - /// {@macro flutter.services.TextInputConfiguration.smartDashesType} - final SmartDashesType smartDashesType; - - /// {@macro flutter.services.TextInputConfiguration.smartQuotesType} - final SmartQuotesType smartQuotesType; - - /// {@macro flutter.services.TextInputConfiguration.enableSuggestions} - final bool enableSuggestions; - - /// {@macro flutter.widgets.editableText.maxLines} - /// * [expands], which determines whether the field should fill the height of - /// its parent. - final int? maxLines; - - /// {@macro flutter.widgets.editableText.minLines} - /// * [expands], which determines whether the field should fill the height of - /// its parent. - final int? minLines; - - /// {@macro flutter.widgets.editableText.expands} - final bool expands; - - /// {@macro flutter.widgets.editableText.readOnly} - final bool readOnly; - - /// {@macro flutter.widgets.editableText.showCursor} - final bool? showCursor; - - /// If [maxLength] is set to this value, only the "current input length" - /// part of the character counter is shown. - static const int noMaxLength = -1; - - /// The maximum number of characters (Unicode scalar values) to allow in the - /// text field. - /// - /// If set, a character counter will be displayed below the - /// field showing how many characters have been entered. If set to a number - /// greater than 0, it will also display the maximum number allowed. If set - /// to [TextField.noMaxLength] then only the current character count is - /// displayed. - /// - /// After [maxLength] characters have been input, additional input - /// is ignored, unless [maxLengthEnforcement] is set to - /// [MaxLengthEnforcement.none]. - /// - /// The text field enforces the length with a - /// [LengthLimitingTextInputFormatter], which is evaluated after the supplied - /// [inputFormatters], if any. - /// - /// This value must be either null, [TextField.noMaxLength], or greater than - /// 0. - /// - /// If null (the default) then there is no limit to the number of characters - /// that can be entered. If set to [TextField.noMaxLength], then no limit will - /// be enforced, but the number of characters entered will still be displayed. - /// - /// Whitespace characters (e.g. newline, space, tab) are included in the - /// character count. - /// - /// {@macro flutter.services.lengthLimitingTextInputFormatter.maxLength} - final int? maxLength; - - /// Determines how the [maxLength] limit should be enforced. - /// - /// {@macro flutter.services.textFormatter.effectiveMaxLengthEnforcement} - /// - /// {@macro flutter.services.textFormatter.maxLengthEnforcement} - final MaxLengthEnforcement? maxLengthEnforcement; - - /// {@macro flutter.widgets.editableText.onChanged} - /// - /// See also: - /// - /// * [inputFormatters], which are called before [onChanged] - /// runs and can validate and change ("format") the input value. - /// * [onEditingComplete], [onSubmitted]: - /// which are more specialized input change notifications. - final ValueChanged? onChanged; - - /// {@macro flutter.widgets.editableText.onEditingComplete} - final VoidCallback? onEditingComplete; - - /// {@macro flutter.widgets.editableText.onSubmitted} - /// - /// See also: - /// - /// * [TextInputAction.next] and [TextInputAction.previous], which - /// automatically shift the focus to the next/previous focusable item when - /// the user is done editing. - final ValueChanged? onSubmitted; - - /// {@macro flutter.widgets.editableText.onAppPrivateCommand} - final AppPrivateCommandCallback? onAppPrivateCommand; - - /// {@macro flutter.widgets.editableText.inputFormatters} - final List? inputFormatters; - - /// If false the text field is "disabled": it ignores taps and its - /// [decoration] is rendered in grey. - /// - /// If non-null this property overrides the [decoration]'s - /// [InputDecoration.enabled] property. - final bool? enabled; - - /// {@macro flutter.widgets.editableText.cursorWidth} - final double cursorWidth; - - /// {@macro flutter.widgets.editableText.cursorHeight} - final double? cursorHeight; - - /// {@macro flutter.widgets.editableText.cursorRadius} - final Radius? cursorRadius; - - /// The color of the cursor. - /// - /// The cursor indicates the current location of text insertion point in - /// the field. - /// - /// If this is null it will default to the ambient - /// [TextSelectionThemeData.cursorColor]. If that is null, and the - /// [ThemeData.platform] is [TargetPlatform.iOS] or [TargetPlatform.macOS] - /// it will use [CupertinoThemeData.primaryColor]. Otherwise it will use - /// the value of [ColorScheme.primary] of [ThemeData.colorScheme]. - final Color? cursorColor; - - /// Controls how tall the selection highlight boxes are computed to be. - /// - /// See [ui.BoxHeightStyle] for details on available styles. - final ui.BoxHeightStyle selectionHeightStyle; - - /// Controls how wide the selection highlight boxes are computed to be. - /// - /// See [ui.BoxWidthStyle] for details on available styles. - final ui.BoxWidthStyle selectionWidthStyle; - - /// The appearance of the keyboard. - /// - /// This setting is only honored on iOS devices. - /// - /// If unset, defaults to the brightness of [ThemeData.brightness]. - final Brightness? keyboardAppearance; - - /// {@macro flutter.widgets.editableText.scrollPadding} - final EdgeInsets scrollPadding; - - /// {@macro flutter.widgets.editableText.enableInteractiveSelection} - final bool enableInteractiveSelection; - - /// {@macro flutter.widgets.editableText.selectionControls} - final TextSelectionControls? selectionControls; - - /// {@macro flutter.widgets.scrollable.dragStartBehavior} - final DragStartBehavior dragStartBehavior; - - /// {@macro flutter.widgets.editableText.selectionEnabled} - bool get selectionEnabled => enableInteractiveSelection; - - /// {@template flutter.material.textfield.onTap} - /// Called for each distinct tap except for every second tap of a double tap. - /// - /// The text field builds a [GestureDetector] to handle input events like tap, - /// to trigger focus requests, to move the caret, adjust the selection, etc. - /// Handling some of those events by wrapping the text field with a competing - /// GestureDetector is problematic. - /// - /// To unconditionally handle taps, without interfering with the text field's - /// internal gesture detector, provide this callback. - /// - /// If the text field is created with [enabled] false, taps will not be - /// recognized. - /// - /// To be notified when the text field gains or loses the focus, provide a - /// [focusNode] and add a listener to that. - /// - /// To listen to arbitrary pointer events without competing with the - /// text field's internal gesture detector, use a [Listener]. - /// {@endtemplate} - final GestureTapCallback? onTap; - - /// The [mouseCursor] is the only property of [TextField] that controls the - /// appearance of the mouse pointer. All other properties related to "cursor" - /// stand for the text cursor, which is usually a blinking vertical line at - /// the editing position. - final MouseCursor? mouseCursor; - - /// Callback that generates a custom [InputDecoration.counter] widget. - /// - /// See [InputCounterWidgetBuilder] for an explanation of the passed in - /// arguments. The returned widget will be placed below the line in place of - /// the default widget built when [InputDecoration.counterText] is specified. - /// - /// The returned widget will be wrapped in a [Semantics] widget for - /// accessibility, but it also needs to be accessible itself. For example, - /// if returning a Text widget, set the [Text.semanticsLabel] property. - /// - /// {@tool snippet} - /// ```dart - /// Widget counter( - /// BuildContext context, - /// { - /// required int currentLength, - /// required int? maxLength, - /// required bool isFocused, - /// } - /// ) { - /// return Text( - /// '$currentLength of $maxLength characters', - /// semanticsLabel: 'character count', - /// ); - /// } - /// ``` - /// {@end-tool} - /// - /// If buildCounter returns null, then no counter and no Semantics widget will - /// be created at all. - final InputCounterWidgetBuilder? buildCounter; - - /// {@macro flutter.widgets.editableText.scrollPhysics} - final ScrollPhysics? scrollPhysics; - - /// {@macro flutter.widgets.editableText.scrollController} - final ScrollController? scrollController; - - /// {@macro flutter.widgets.editableText.autofillHints} - /// {@macro flutter.services.AutofillConfiguration.autofillHints} - final Iterable? autofillHints; - - /// {@macro flutter.material.Material.clipBehavior} - /// - /// Defaults to [Clip.hardEdge]. - final Clip clipBehavior; - - /// {@template flutter.material.textfield.restorationId} - /// Restoration ID to save and restore the state of the text field. - /// - /// If non-null, the text field will persist and restore its current scroll - /// offset and - if no [controller] has been provided - the content of the - /// text field. If a [controller] has been provided, it is the responsibility - /// of the owner of that controller to persist and restore it, e.g. by using - /// a [RestorableTextEditingController]. - /// - /// The state of this widget is persisted in a [RestorationBucket] claimed - /// from the surrounding [RestorationScope] using the provided restoration ID. - /// - /// See also: - /// - /// * [RestorationManager], which explains how state restoration works in - /// Flutter. - /// {@endtemplate} - final String? restorationId; - - /// {@macro flutter.widgets.editableText.scribbleEnabled} - final bool scribbleEnabled; - - // ignore: lines_longer_than_80_chars - /// {@macro flutter.services.TextInputConfiguration.enableIMEPersonalizedLearning} - final bool enableIMEPersonalizedLearning; - - /// {@macro flutter.widgets.editableText.contentInsertionConfiguration} - final ContentInsertionConfiguration? contentInsertionConfiguration; - - @override - _StreamMessageTextFieldState createState() => _StreamMessageTextFieldState(); - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty( - 'controller', controller, - defaultValue: null)) - ..add(DiagnosticsProperty('focusNode', focusNode, - defaultValue: null)) - ..add(DiagnosticsProperty('enabled', enabled, defaultValue: null)) - ..add(DiagnosticsProperty('decoration', decoration, - defaultValue: const InputDecoration())) - ..add(DiagnosticsProperty('keyboardType', keyboardType, - defaultValue: TextInputType.text)) - ..add(DiagnosticsProperty('style', style, defaultValue: null)) - ..add(DiagnosticsProperty('autofocus', autofocus, - defaultValue: false)) - ..add(DiagnosticsProperty( - 'obscuringCharacter', obscuringCharacter, - defaultValue: '•')) - ..add(DiagnosticsProperty('obscureText', obscureText, - defaultValue: false)) - ..add(DiagnosticsProperty('autocorrect', autocorrect, - defaultValue: true)) - ..add(EnumProperty('smartDashesType', smartDashesType, - defaultValue: - obscureText ? SmartDashesType.disabled : SmartDashesType.enabled)) - ..add(EnumProperty('smartQuotesType', smartQuotesType, - defaultValue: - obscureText ? SmartQuotesType.disabled : SmartQuotesType.enabled)) - ..add(DiagnosticsProperty('enableSuggestions', enableSuggestions, - defaultValue: true)) - ..add(IntProperty('maxLines', maxLines, defaultValue: 1)) - ..add(IntProperty('minLines', minLines, defaultValue: null)) - ..add(DiagnosticsProperty('expands', expands, defaultValue: false)) - ..add(IntProperty('maxLength', maxLength, defaultValue: null)) - ..add(EnumProperty( - 'maxLengthEnforcement', maxLengthEnforcement, - defaultValue: null)) - ..add(EnumProperty('textInputAction', textInputAction, - defaultValue: null)) - ..add(EnumProperty( - 'textCapitalization', textCapitalization, - defaultValue: TextCapitalization.none)) - ..add(EnumProperty('textAlign', textAlign, - defaultValue: TextAlign.start)) - ..add(DiagnosticsProperty( - 'textAlignVertical', textAlignVertical, - defaultValue: null)) - ..add(EnumProperty('textDirection', textDirection, - defaultValue: null)) - ..add(DoubleProperty('cursorWidth', cursorWidth, defaultValue: 2.0)) - ..add(DoubleProperty('cursorHeight', cursorHeight, defaultValue: null)) - ..add(DiagnosticsProperty('cursorRadius', cursorRadius, - defaultValue: null)) - ..add(ColorProperty('cursorColor', cursorColor, defaultValue: null)) - ..add(DiagnosticsProperty( - 'keyboardAppearance', keyboardAppearance, - defaultValue: null)) - ..add(DiagnosticsProperty( - 'scrollPadding', scrollPadding, - defaultValue: const EdgeInsets.all(20))) - ..add(FlagProperty('selectionEnabled', - value: selectionEnabled, - defaultValue: true, - ifFalse: 'selection disabled')) - ..add(DiagnosticsProperty( - 'selectionControls', selectionControls, - defaultValue: null)) - ..add(DiagnosticsProperty( - 'scrollController', scrollController, - defaultValue: null)) - ..add(DiagnosticsProperty('scrollPhysics', scrollPhysics, - defaultValue: null)) - ..add(DiagnosticsProperty('clipBehavior', clipBehavior, - defaultValue: Clip.hardEdge)) - ..add(DiagnosticsProperty('scribbleEnabled', scribbleEnabled, - defaultValue: true)) - ..add(DiagnosticsProperty( - 'enableIMEPersonalizedLearning', enableIMEPersonalizedLearning, - defaultValue: true)) - ..add(DiagnosticsProperty( - 'contentInsertionConfiguration', contentInsertionConfiguration, - defaultValue: null)); - } -} - -class _StreamMessageTextFieldState extends State - with RestorationMixin { - StreamMessageInputController get _effectiveController => - widget.controller ?? _controller!.value; - StreamRestorableMessageInputController? _controller; - - @override - void initState() { - super.initState(); - if (widget.controller == null) { - _createLocalController(); - } - } - - void _createLocalController([Message? message]) { - assert(_controller == null, ''); - _controller = StreamRestorableMessageInputController(message: message); - } - - @override - void didUpdateWidget(covariant StreamMessageTextField oldWidget) { - super.didUpdateWidget(oldWidget); - if (widget.controller == null && oldWidget.controller != null) { - _createLocalController(oldWidget.controller!.message); - } else if (widget.controller != null && oldWidget.controller == null) { - unregisterFromRestoration(_controller!); - _controller!.dispose(); - _controller = null; - } - } - - @override - void restoreState(RestorationBucket? oldBucket, bool initialRestore) { - if (_controller != null) { - _registerController(); - } - } - - @override - String? get restorationId => widget.restorationId; - - void _registerController() { - assert(_controller != null, ''); - registerForRestoration(_controller!, 'controller'); - } - - @override - Widget build(BuildContext context) => TextField( - controller: _effectiveController.textFieldController, - focusNode: widget.focusNode, - decoration: widget.decoration, - keyboardType: widget.keyboardType, - textInputAction: widget.textInputAction ?? - (widget.keyboardType == TextInputType.multiline - ? TextInputAction.newline - : TextInputAction.send), - textCapitalization: widget.textCapitalization, - style: widget.style, - strutStyle: widget.strutStyle, - textAlign: widget.textAlign, - textAlignVertical: widget.textAlignVertical, - textDirection: widget.textDirection, - readOnly: widget.readOnly, - showCursor: widget.showCursor, - autofocus: widget.autofocus, - obscuringCharacter: widget.obscuringCharacter, - obscureText: widget.obscureText, - autocorrect: widget.autocorrect, - smartDashesType: widget.smartDashesType, - smartQuotesType: widget.smartQuotesType, - enableSuggestions: widget.enableSuggestions, - maxLines: widget.maxLines, - minLines: widget.minLines, - expands: widget.expands, - maxLength: widget.maxLength, - maxLengthEnforcement: widget.maxLengthEnforcement, - onEditingComplete: widget.onEditingComplete, - onSubmitted: widget.onSubmitted, - onAppPrivateCommand: widget.onAppPrivateCommand, - inputFormatters: widget.inputFormatters, - enabled: widget.enabled, - cursorWidth: widget.cursorWidth, - cursorHeight: widget.cursorHeight, - cursorRadius: widget.cursorRadius, - cursorColor: widget.cursorColor, - selectionHeightStyle: widget.selectionHeightStyle, - selectionWidthStyle: widget.selectionWidthStyle, - keyboardAppearance: widget.keyboardAppearance, - scrollPadding: widget.scrollPadding, - dragStartBehavior: widget.dragStartBehavior, - enableInteractiveSelection: widget.enableInteractiveSelection, - selectionControls: widget.selectionControls, - onTap: widget.onTap, - mouseCursor: widget.mouseCursor, - buildCounter: widget.buildCounter, - scrollController: widget.scrollController, - scrollPhysics: widget.scrollPhysics, - autofillHints: widget.autofillHints, - clipBehavior: widget.clipBehavior, - restorationId: widget.restorationId, - // ignore: deprecated_member_use - scribbleEnabled: widget.scribbleEnabled, - enableIMEPersonalizedLearning: widget.enableIMEPersonalizedLearning, - contentInsertionConfiguration: widget.contentInsertionConfiguration, - ); - - @override - void dispose() { - _controller?.dispose(); - super.dispose(); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_input/tld.dart b/packages/stream_chat_flutter/lib/src/message_input/tld.dart deleted file mode 100644 index 2bfa52578f..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_input/tld.dart +++ /dev/null @@ -1,1553 +0,0 @@ -/// Extension on String adding utilities checking TLD validity. -extension TLDString on String { - /// Returns true if the string is a valid TLD. - bool isValidTLD() => - isNotEmpty && - tlds.containsKey(this[0].toUpperCase()) && - tlds[this[0].toUpperCase()]!.contains(toUpperCase()); -} - -/// List of valid TLDs. -/// https://data.iana.org/TLD/tlds-alpha-by-domain.txt -const tlds = { - 'A': [ - 'AAA', - 'AARP', - 'ABARTH', - 'ABB', - 'ABBOTT', - 'ABBVIE', - 'ABC', - 'ABLE', - 'ABOGADO', - 'ABUDHABI', - 'AC', - 'ACADEMY', - 'ACCENTURE', - 'ACCOUNTANT', - 'ACCOUNTANTS', - 'ACO', - 'ACTOR', - 'AD', - 'ADAC', - 'ADS', - 'ADULT', - 'AE', - 'AEG', - 'AERO', - 'AETNA', - 'AF', - 'AFL', - 'AFRICA', - 'AG', - 'AGAKHAN', - 'AGENCY', - 'AI', - 'AIG', - 'AIRBUS', - 'AIRFORCE', - 'AIRTEL', - 'AKDN', - 'AL', - 'ALFAROMEO', - 'ALIBABA', - 'ALIPAY', - 'ALLFINANZ', - 'ALLSTATE', - 'ALLY', - 'ALSACE', - 'ALSTOM', - 'AM', - 'AMAZON', - 'AMERICANEXPRESS', - 'AMERICANFAMILY', - 'AMEX', - 'AMFAM', - 'AMICA', - 'AMSTERDAM', - 'ANALYTICS', - 'ANDROID', - 'ANQUAN', - 'ANZ', - 'AO', - 'AOL', - 'APARTMENTS', - 'APP', - 'APPLE', - 'AQ', - 'AQUARELLE', - 'AR', - 'ARAB', - 'ARAMCO', - 'ARCHI', - 'ARMY', - 'ARPA', - 'ART', - 'ARTE', - 'AS', - 'ASDA', - 'ASIA', - 'ASSOCIATES', - 'AT', - 'ATHLETA', - 'ATTORNEY', - 'AU', - 'AUCTION', - 'AUDI', - 'AUDIBLE', - 'AUDIO', - 'AUSPOST', - 'AUTHOR', - 'AUTO', - 'AUTOS', - 'AVIANCA', - 'AW', - 'AWS', - 'AX', - 'AXA', - 'AZ', - 'AZURE', - ], - 'B': [ - 'BA', - 'BABY', - 'BAIDU', - 'BANAMEX', - 'BANANAREPUBLIC', - 'BAND', - 'BANK', - 'BAR', - 'BARCELONA', - 'BARCLAYCARD', - 'BARCLAYS', - 'BAREFOOT', - 'BARGAINS', - 'BASEBALL', - 'BASKETBALL', - 'BAUHAUS', - 'BAYERN', - 'BB', - 'BBC', - 'BBT', - 'BBVA', - 'BCG', - 'BCN', - 'BD', - 'BE', - 'BEATS', - 'BEAUTY', - 'BEER', - 'BENTLEY', - 'BERLIN', - 'BEST', - 'BESTBUY', - 'BET', - 'BF', - 'BG', - 'BH', - 'BHARTI', - 'BI', - 'BIBLE', - 'BID', - 'BIKE', - 'BING', - 'BINGO', - 'BIO', - 'BIZ', - 'BJ', - 'BLACK', - 'BLACKFRIDAY', - 'BLOCKBUSTER', - 'BLOG', - 'BLOOMBERG', - 'BLUE', - 'BM', - 'BMS', - 'BMW', - 'BN', - 'BNPPARIBAS', - 'BO', - 'BOATS', - 'BOEHRINGER', - 'BOFA', - 'BOM', - 'BOND', - 'BOO', - 'BOOK', - 'BOOKING', - 'BOSCH', - 'BOSTIK', - 'BOSTON', - 'BOT', - 'BOUTIQUE', - 'BOX', - 'BR', - 'BRADESCO', - 'BRIDGESTONE', - 'BROADWAY', - 'BROKER', - 'BROTHER', - 'BRUSSELS', - 'BS', - 'BT', - 'BUDAPEST', - 'BUGATTI', - 'BUILD', - 'BUILDERS', - 'BUSINESS', - 'BUY', - 'BUZZ', - 'BV', - 'BW', - 'BY', - 'BZ', - 'BZH', - ], - 'C': [ - 'CA', - 'CAB', - 'CAFE', - 'CAL', - 'CALL', - 'CALVINKLEIN', - 'CAM', - 'CAMERA', - 'CAMP', - 'CANCERRESEARCH', - 'CANON', - 'CAPETOWN', - 'CAPITAL', - 'CAPITALONE', - 'CAR', - 'CARAVAN', - 'CARDS', - 'CARE', - 'CAREER', - 'CAREERS', - 'CARS', - 'CASA', - 'CASE', - 'CASH', - 'CASINO', - 'CAT', - 'CATERING', - 'CATHOLIC', - 'CBA', - 'CBN', - 'CBRE', - 'CBS', - 'CC', - 'CD', - 'CENTER', - 'CEO', - 'CERN', - 'CF', - 'CFA', - 'CFD', - 'CG', - 'CH', - 'CHANEL', - 'CHANNEL', - 'CHARITY', - 'CHASE', - 'CHAT', - 'CHEAP', - 'CHINTAI', - 'CHRISTMAS', - 'CHROME', - 'CHURCH', - 'CI', - 'CIPRIANI', - 'CIRCLE', - 'CISCO', - 'CITADEL', - 'CITI', - 'CITIC', - 'CITY', - 'CITYEATS', - 'CK', - 'CL', - 'CLAIMS', - 'CLEANING', - 'CLICK', - 'CLINIC', - 'CLINIQUE', - 'CLOTHING', - 'CLOUD', - 'CLUB', - 'CLUBMED', - 'CM', - 'CN', - 'CO', - 'COACH', - 'CODES', - 'COFFEE', - 'COLLEGE', - 'COLOGNE', - 'COM', - 'COMCAST', - 'COMMBANK', - 'COMMUNITY', - 'COMPANY', - 'COMPARE', - 'COMPUTER', - 'COMSEC', - 'CONDOS', - 'CONSTRUCTION', - 'CONSULTING', - 'CONTACT', - 'CONTRACTORS', - 'COOKING', - 'COOKINGCHANNEL', - 'COOL', - 'COOP', - 'CORSICA', - 'COUNTRY', - 'COUPON', - 'COUPONS', - 'COURSES', - 'CPA', - 'CR', - 'CREDIT', - 'CREDITCARD', - 'CREDITUNION', - 'CRICKET', - 'CROWN', - 'CRS', - 'CRUISE', - 'CRUISES', - 'CSC', - 'CU', - 'CUISINELLA', - 'CV', - 'CW', - 'CX', - 'CY', - 'CYMRU', - 'CYOU', - 'CZ', - ], - 'D': [ - 'DABUR', - 'DAD', - 'DANCE', - 'DATA', - 'DATE', - 'DATING', - 'DATSUN', - 'DAY', - 'DCLK', - 'DDS', - 'DE', - 'DEAL', - 'DEALER', - 'DEALS', - 'DEGREE', - 'DELIVERY', - 'DELL', - 'DELOITTE', - 'DELTA', - 'DEMOCRAT', - 'DENTAL', - 'DENTIST', - 'DESI', - 'DESIGN', - 'DEV', - 'DHL', - 'DIAMONDS', - 'DIET', - 'DIGITAL', - 'DIRECT', - 'DIRECTORY', - 'DISCOUNT', - 'DISCOVER', - 'DISH', - 'DIY', - 'DJ', - 'DK', - 'DM', - 'DNP', - 'DO', - 'DOCS', - 'DOCTOR', - 'DOG', - 'DOMAINS', - 'DOT', - 'DOWNLOAD', - 'DRIVE', - 'DTV', - 'DUBAI', - 'DUNLOP', - 'DUPONT', - 'DURBAN', - 'DVAG', - 'DVR', - 'DZ', - ], - 'E': [ - 'EARTH', - 'EAT', - 'EC', - 'ECO', - 'EDEKA', - 'EDU', - 'EDUCATION', - 'EE', - 'EG', - 'EMAIL', - 'EMERCK', - 'ENERGY', - 'ENGINEER', - 'ENGINEERING', - 'ENTERPRISES', - 'EPSON', - 'EQUIPMENT', - 'ER', - 'ERICSSON', - 'ERNI', - 'ES', - 'ESQ', - 'ESTATE', - 'ET', - 'ETISALAT', - 'EU', - 'EUROVISION', - 'EUS', - 'EVENTS', - 'EXCHANGE', - 'EXPERT', - 'EXPOSED', - 'EXPRESS', - 'EXTRASPACE', - ], - 'F': [ - 'FAGE', - 'FAIL', - 'FAIRWINDS', - 'FAITH', - 'FAMILY', - 'FAN', - 'FANS', - 'FARM', - 'FARMERS', - 'FASHION', - 'FAST', - 'FEDEX', - 'FEEDBACK', - 'FERRARI', - 'FERRERO', - 'FI', - 'FIAT', - 'FIDELITY', - 'FIDO', - 'FILM', - 'FINAL', - 'FINANCE', - 'FINANCIAL', - 'FIRE', - 'FIRESTONE', - 'FIRMDALE', - 'FISH', - 'FISHING', - 'FIT', - 'FITNESS', - 'FJ', - 'FK', - 'FLICKR', - 'FLIGHTS', - 'FLIR', - 'FLORIST', - 'FLOWERS', - 'FLY', - 'FM', - 'FO', - 'FOO', - 'FOOD', - 'FOODNETWORK', - 'FOOTBALL', - 'FORD', - 'FOREX', - 'FORSALE', - 'FORUM', - 'FOUNDATION', - 'FOX', - 'FR', - 'FREE', - 'FRESENIUS', - 'FRL', - 'FROGANS', - 'FRONTDOOR', - 'FRONTIER', - 'FTR', - 'FUJITSU', - 'FUN', - 'FUND', - 'FURNITURE', - 'FUTBOL', - 'FYI', - ], - 'G': [ - 'GA', - 'GAL', - 'GALLERY', - 'GALLO', - 'GALLUP', - 'GAME', - 'GAMES', - 'GAP', - 'GARDEN', - 'GAY', - 'GB', - 'GBIZ', - 'GD', - 'GDN', - 'GE', - 'GEA', - 'GENT', - 'GENTING', - 'GEORGE', - 'GF', - 'GG', - 'GGEE', - 'GH', - 'GI', - 'GIFT', - 'GIFTS', - 'GIVES', - 'GIVING', - 'GL', - 'GLASS', - 'GLE', - 'GLOBAL', - 'GLOBO', - 'GM', - 'GMAIL', - 'GMBH', - 'GMO', - 'GMX', - 'GN', - 'GODADDY', - 'GOLD', - 'GOLDPOINT', - 'GOLF', - 'GOO', - 'GOODYEAR', - 'GOOG', - 'GOOGLE', - 'GOP', - 'GOT', - 'GOV', - 'GP', - 'GQ', - 'GR', - 'GRAINGER', - 'GRAPHICS', - 'GRATIS', - 'GREEN', - 'GRIPE', - 'GROCERY', - 'GROUP', - 'GS', - 'GT', - 'GU', - 'GUARDIAN', - 'GUCCI', - 'GUGE', - 'GUIDE', - 'GUITARS', - 'GURU', - 'GW', - 'GY', - ], - 'H': [ - 'HAIR', - 'HAMBURG', - 'HANGOUT', - 'HAUS', - 'HBO', - 'HDFC', - 'HDFCBANK', - 'HEALTH', - 'HEALTHCARE', - 'HELP', - 'HELSINKI', - 'HERE', - 'HERMES', - 'HGTV', - 'HIPHOP', - 'HISAMITSU', - 'HITACHI', - 'HIV', - 'HK', - 'HKT', - 'HM', - 'HN', - 'HOCKEY', - 'HOLDINGS', - 'HOLIDAY', - 'HOMEDEPOT', - 'HOMEGOODS', - 'HOMES', - 'HOMESENSE', - 'HONDA', - 'HORSE', - 'HOSPITAL', - 'HOST', - 'HOSTING', - 'HOT', - 'HOTELES', - 'HOTELS', - 'HOTMAIL', - 'HOUSE', - 'HOW', - 'HR', - 'HSBC', - 'HT', - 'HU', - 'HUGHES', - 'HYATT', - 'HYUNDAI', - ], - 'I': [ - 'IBM', - 'ICBC', - 'ICE', - 'ICU', - 'ID', - 'IE', - 'IEEE', - 'IFM', - 'IKANO', - 'IL', - 'IM', - 'IMAMAT', - 'IMDB', - 'IMMO', - 'IMMOBILIEN', - 'IN', - 'INC', - 'INDUSTRIES', - 'INFINITI', - 'INFO', - 'ING', - 'INK', - 'INSTITUTE', - 'INSURANCE', - 'INSURE', - 'INT', - 'INTERNATIONAL', - 'INTUIT', - 'INVESTMENTS', - 'IO', - 'IPIRANGA', - 'IQ', - 'IR', - 'IRISH', - 'IS', - 'ISMAILI', - 'IST', - 'ISTANBUL', - 'IT', - 'ITAU', - 'ITV', - ], - 'J': [ - 'JAGUAR', - 'JAVA', - 'JCB', - 'JE', - 'JEEP', - 'JETZT', - 'JEWELRY', - 'JIO', - 'JLL', - 'JM', - 'JMP', - 'JNJ', - 'JO', - 'JOBS', - 'JOBURG', - 'JOT', - 'JOY', - 'JP', - 'JPMORGAN', - 'JPRS', - 'JUEGOS', - 'JUNIPER', - ], - 'K': [ - 'KAUFEN', - 'KDDI', - 'KE', - 'KERRYHOTELS', - 'KERRYLOGISTICS', - 'KERRYPROPERTIES', - 'KFH', - 'KG', - 'KH', - 'KI', - 'KIA', - 'KIM', - 'KINDER', - 'KINDLE', - 'KITCHEN', - 'KIWI', - 'KM', - 'KN', - 'KOELN', - 'KOMATSU', - 'KOSHER', - 'KP', - 'KPMG', - 'KPN', - 'KR', - 'KRD', - 'KRED', - 'KUOKGROUP', - 'KW', - 'KY', - 'KYOTO', - 'KZ', - ], - 'L': [ - 'LA', - 'LACAIXA', - 'LAMBORGHINI', - 'LAMER', - 'LANCASTER', - 'LANCIA', - 'LAND', - 'LANDROVER', - 'LANXESS', - 'LASALLE', - 'LAT', - 'LATINO', - 'LATROBE', - 'LAW', - 'LAWYER', - 'LB', - 'LC', - 'LDS', - 'LEASE', - 'LECLERC', - 'LEFRAK', - 'LEGAL', - 'LEGO', - 'LEXUS', - 'LGBT', - 'LI', - 'LIDL', - 'LIFE', - 'LIFEINSURANCE', - 'LIFESTYLE', - 'LIGHTING', - 'LIKE', - 'LILLY', - 'LIMITED', - 'LIMO', - 'LINCOLN', - 'LINDE', - 'LINK', - 'LIPSY', - 'LIVE', - 'LIVING', - 'LK', - 'LLC', - 'LLP', - 'LOAN', - 'LOANS', - 'LOCKER', - 'LOCUS', - 'LOFT', - 'LOL', - 'LONDON', - 'LOTTE', - 'LOTTO', - 'LOVE', - 'LPL', - 'LPLFINANCIAL', - 'LR', - 'LS', - 'LT', - 'LTD', - 'LTDA', - 'LU', - 'LUNDBECK', - 'LUXE', - 'LUXURY', - 'LV', - 'LY', - ], - 'M': [ - 'MA', - 'MACYS', - 'MADRID', - 'MAIF', - 'MAISON', - 'MAKEUP', - 'MAN', - 'MANAGEMENT', - 'MANGO', - 'MAP', - 'MARKET', - 'MARKETING', - 'MARKETS', - 'MARRIOTT', - 'MARSHALLS', - 'MASERATI', - 'MATTEL', - 'MBA', - 'MC', - 'MCKINSEY', - 'MD', - 'ME', - 'MED', - 'MEDIA', - 'MEET', - 'MELBOURNE', - 'MEME', - 'MEMORIAL', - 'MEN', - 'MENU', - 'MERCKMSD', - 'MG', - 'MH', - 'MIAMI', - 'MICROSOFT', - 'MIL', - 'MINI', - 'MINT', - 'MIT', - 'MITSUBISHI', - 'MK', - 'ML', - 'MLB', - 'MLS', - 'MM', - 'MMA', - 'MN', - 'MO', - 'MOBI', - 'MOBILE', - 'MODA', - 'MOE', - 'MOI', - 'MOM', - 'MONASH', - 'MONEY', - 'MONSTER', - 'MORMON', - 'MORTGAGE', - 'MOSCOW', - 'MOTO', - 'MOTORCYCLES', - 'MOV', - 'MOVIE', - 'MP', - 'MQ', - 'MR', - 'MS', - 'MSD', - 'MT', - 'MTN', - 'MTR', - 'MU', - 'MUSEUM', - 'MUSIC', - 'MUTUAL', - 'MV', - 'MW', - 'MX', - 'MY', - 'MZ', - ], - 'N': [ - 'NA', - 'NAB', - 'NAGOYA', - 'NAME', - 'NATURA', - 'NAVY', - 'NBA', - 'NC', - 'NE', - 'NEC', - 'NET', - 'NETBANK', - 'NETFLIX', - 'NETWORK', - 'NEUSTAR', - 'NEW', - 'NEWS', - 'NEXT', - 'NEXTDIRECT', - 'NEXUS', - 'NF', - 'NFL', - 'NG', - 'NGO', - 'NHK', - 'NI', - 'NICO', - 'NIKE', - 'NIKON', - 'NINJA', - 'NISSAN', - 'NISSAY', - 'NL', - 'NO', - 'NOKIA', - 'NORTHWESTERNMUTUAL', - 'NORTON', - 'NOW', - 'NOWRUZ', - 'NOWTV', - 'NP', - 'NR', - 'NRA', - 'NRW', - 'NTT', - 'NU', - 'NYC', - 'NZ', - ], - 'O': [ - 'OBI', - 'OBSERVER', - 'OFFICE', - 'OKINAWA', - 'OLAYAN', - 'OLAYANGROUP', - 'OLDNAVY', - 'OLLO', - 'OM', - 'OMEGA', - 'ONE', - 'ONG', - 'ONL', - 'ONLINE', - 'OOO', - 'OPEN', - 'ORACLE', - 'ORANGE', - 'ORG', - 'ORGANIC', - 'ORIGINS', - 'OSAKA', - 'OTSUKA', - 'OTT', - 'OVH', - ], - 'P': [ - 'PA', - 'PAGE', - 'PANASONIC', - 'PARIS', - 'PARS', - 'PARTNERS', - 'PARTS', - 'PARTY', - 'PASSAGENS', - 'PAY', - 'PCCW', - 'PE', - 'PET', - 'PF', - 'PFIZER', - 'PG', - 'PH', - 'PHARMACY', - 'PHD', - 'PHILIPS', - 'PHONE', - 'PHOTO', - 'PHOTOGRAPHY', - 'PHOTOS', - 'PHYSIO', - 'PICS', - 'PICTET', - 'PICTURES', - 'PID', - 'PIN', - 'PING', - 'PINK', - 'PIONEER', - 'PIZZA', - 'PK', - 'PL', - 'PLACE', - 'PLAY', - 'PLAYSTATION', - 'PLUMBING', - 'PLUS', - 'PM', - 'PN', - 'PNC', - 'POHL', - 'POKER', - 'POLITIE', - 'PORN', - 'POST', - 'PR', - 'PRAMERICA', - 'PRAXI', - 'PRESS', - 'PRIME', - 'PRO', - 'PROD', - 'PRODUCTIONS', - 'PROF', - 'PROGRESSIVE', - 'PROMO', - 'PROPERTIES', - 'PROPERTY', - 'PROTECTION', - 'PRU', - 'PRUDENTIAL', - 'PS', - 'PT', - 'PUB', - 'PW', - 'PWC', - 'PY', - ], - 'Q': [ - 'QA', - 'QPON', - 'QUEBEC', - 'QUEST', - ], - 'R': [ - 'RACING', - 'RADIO', - 'RE', - 'READ', - 'REALESTATE', - 'REALTOR', - 'REALTY', - 'RECIPES', - 'RED', - 'REDSTONE', - 'REDUMBRELLA', - 'REHAB', - 'REISE', - 'REISEN', - 'REIT', - 'RELIANCE', - 'REN', - 'RENT', - 'RENTALS', - 'REPAIR', - 'REPORT', - 'REPUBLICAN', - 'REST', - 'RESTAURANT', - 'REVIEW', - 'REVIEWS', - 'REXROTH', - 'RICH', - 'RICHARDLI', - 'RICOH', - 'RIL', - 'RIO', - 'RIP', - 'RO', - 'ROCHER', - 'ROCKS', - 'RODEO', - 'ROGERS', - 'ROOM', - 'RS', - 'RSVP', - 'RU', - 'RUGBY', - 'RUHR', - 'RUN', - 'RW', - 'RWE', - 'RYUKYU', - ], - 'S': [ - 'SA', - 'SAARLAND', - 'SAFE', - 'SAFETY', - 'SAKURA', - 'SALE', - 'SALON', - 'SAMSCLUB', - 'SAMSUNG', - 'SANDVIK', - 'SANDVIKCOROMANT', - 'SANOFI', - 'SAP', - 'SARL', - 'SAS', - 'SAVE', - 'SAXO', - 'SB', - 'SBI', - 'SBS', - 'SC', - 'SCA', - 'SCB', - 'SCHAEFFLER', - 'SCHMIDT', - 'SCHOLARSHIPS', - 'SCHOOL', - 'SCHULE', - 'SCHWARZ', - 'SCIENCE', - 'SCOT', - 'SD', - 'SE', - 'SEARCH', - 'SEAT', - 'SECURE', - 'SECURITY', - 'SEEK', - 'SELECT', - 'SENER', - 'SERVICES', - 'SES', - 'SEVEN', - 'SEW', - 'SEX', - 'SEXY', - 'SFR', - 'SG', - 'SH', - 'SHANGRILA', - 'SHARP', - 'SHAW', - 'SHELL', - 'SHIA', - 'SHIKSHA', - 'SHOES', - 'SHOP', - 'SHOPPING', - 'SHOUJI', - 'SHOW', - 'SHOWTIME', - 'SI', - 'SILK', - 'SINA', - 'SINGLES', - 'SITE', - 'SJ', - 'SK', - 'SKI', - 'SKIN', - 'SKY', - 'SKYPE', - 'SL', - 'SLING', - 'SM', - 'SMART', - 'SMILE', - 'SN', - 'SNCF', - 'SO', - 'SOCCER', - 'SOCIAL', - 'SOFTBANK', - 'SOFTWARE', - 'SOHU', - 'SOLAR', - 'SOLUTIONS', - 'SONG', - 'SONY', - 'SOY', - 'SPA', - 'SPACE', - 'SPORT', - 'SPOT', - 'SR', - 'SRL', - 'SS', - 'ST', - 'STADA', - 'STAPLES', - 'STAR', - 'STATEBANK', - 'STATEFARM', - 'STC', - 'STCGROUP', - 'STOCKHOLM', - 'STORAGE', - 'STORE', - 'STREAM', - 'STUDIO', - 'STUDY', - 'STYLE', - 'SU', - 'SUCKS', - 'SUPPLIES', - 'SUPPLY', - 'SUPPORT', - 'SURF', - 'SURGERY', - 'SUZUKI', - 'SV', - 'SWATCH', - 'SWISS', - 'SX', - 'SY', - 'SYDNEY', - 'SYSTEMS', - 'SZ', - ], - 'T': [ - 'TAB', - 'TAIPEI', - 'TALK', - 'TAOBAO', - 'TARGET', - 'TATAMOTORS', - 'TATAR', - 'TATTOO', - 'TAX', - 'TAXI', - 'TC', - 'TCI', - 'TD', - 'TDK', - 'TEAM', - 'TECH', - 'TECHNOLOGY', - 'TEL', - 'TEMASEK', - 'TENNIS', - 'TEVA', - 'TF', - 'TG', - 'TH', - 'THD', - 'THEATER', - 'THEATRE', - 'TIAA', - 'TICKETS', - 'TIENDA', - 'TIFFANY', - 'TIPS', - 'TIRES', - 'TIROL', - 'TJ', - 'TJMAXX', - 'TJX', - 'TK', - 'TKMAXX', - 'TL', - 'TM', - 'TMALL', - 'TN', - 'TO', - 'TODAY', - 'TOKYO', - 'TOOLS', - 'TOP', - 'TORAY', - 'TOSHIBA', - 'TOTAL', - 'TOURS', - 'TOWN', - 'TOYOTA', - 'TOYS', - 'TR', - 'TRADE', - 'TRADING', - 'TRAINING', - 'TRAVEL', - 'TRAVELCHANNEL', - 'TRAVELERS', - 'TRAVELERSINSURANCE', - 'TRUST', - 'TRV', - 'TT', - 'TUBE', - 'TUI', - 'TUNES', - 'TUSHU', - 'TV', - 'TVS', - 'TW', - 'TZ', - ], - 'U': [ - 'UA', - 'UBANK', - 'UBS', - 'UG', - 'UK', - 'UNICOM', - 'UNIVERSITY', - 'UNO', - 'UOL', - 'UPS', - 'US', - 'UY', - 'UZ', - ], - 'V': [ - 'VA', - 'VACATIONS', - 'VANA', - 'VANGUARD', - 'VC', - 'VE', - 'VEGAS', - 'VENTURES', - 'VERISIGN', - 'VERSICHERUNG', - 'VET', - 'VG', - 'VI', - 'VIAJES', - 'VIDEO', - 'VIG', - 'VIKING', - 'VILLAS', - 'VIN', - 'VIP', - 'VIRGIN', - 'VISA', - 'VISION', - 'VIVA', - 'VIVO', - 'VLAANDEREN', - 'VN', - 'VODKA', - 'VOLKSWAGEN', - 'VOLVO', - 'VOTE', - 'VOTING', - 'VOTO', - 'VOYAGE', - 'VU', - 'VUELOS', - ], - 'W': [ - 'WALES', - 'WALMART', - 'WALTER', - 'WANG', - 'WANGGOU', - 'WATCH', - 'WATCHES', - 'WEATHER', - 'WEATHERCHANNEL', - 'WEBCAM', - 'WEBER', - 'WEBSITE', - 'WED', - 'WEDDING', - 'WEIBO', - 'WEIR', - 'WF', - 'WHOSWHO', - 'WIEN', - 'WIKI', - 'WILLIAMHILL', - 'WIN', - 'WINDOWS', - 'WINE', - 'WINNERS', - 'WME', - 'WOLTERSKLUWER', - 'WOODSIDE', - 'WORK', - 'WORKS', - 'WORLD', - 'WOW', - 'WS', - 'WTC', - 'WTF', - ], - 'X': [ - 'XBOX', - 'XEROX', - 'XFINITY', - 'XIHUAN', - 'XIN', - 'XN--11B4C3D', - 'XN--1CK2E1B', - 'XN--1QQW23A', - 'XN--2SCRJ9C', - 'XN--30RR7Y', - 'XN--3BST00M', - 'XN--3DS443G', - 'XN--3E0B707E', - 'XN--3HCRJ9C', - 'XN--3PXU8K', - 'XN--42C2D9A', - 'XN--45BR5CYL', - 'XN--45BRJ9C', - 'XN--45Q11C', - 'XN--4DBRK0CE', - 'XN--4GBRIM', - 'XN--54B7FTA0CC', - 'XN--55QW42G', - 'XN--55QX5D', - 'XN--5SU34J936BGSG', - 'XN--5TZM5G', - 'XN--6FRZ82G', - 'XN--6QQ986B3XL', - 'XN--80ADXHKS', - 'XN--80AO21A', - 'XN--80AQECDR1A', - 'XN--80ASEHDB', - 'XN--80ASWG', - 'XN--8Y0A063A', - 'XN--90A3AC', - 'XN--90AE', - 'XN--90AIS', - 'XN--9DBQ2A', - 'XN--9ET52U', - 'XN--9KRT00A', - 'XN--B4W605FERD', - 'XN--BCK1B9A5DRE4C', - 'XN--C1AVG', - 'XN--C2BR7G', - 'XN--CCK2B3B', - 'XN--CCKWCXETD', - 'XN--CG4BKI', - 'XN--CLCHC0EA0B2G2A9GCD', - 'XN--CZR694B', - 'XN--CZRS0T', - 'XN--CZRU2D', - 'XN--D1ACJ3B', - 'XN--D1ALF', - 'XN--E1A4C', - 'XN--ECKVDTC9D', - 'XN--EFVY88H', - 'XN--FCT429K', - 'XN--FHBEI', - 'XN--FIQ228C5HS', - 'XN--FIQ64B', - 'XN--FIQS8S', - 'XN--FIQZ9S', - 'XN--FJQ720A', - 'XN--FLW351E', - 'XN--FPCRJ9C3D', - 'XN--FZC2C9E2C', - 'XN--FZYS8D69UVGM', - 'XN--G2XX48C', - 'XN--GCKR3F0F', - 'XN--GECRJ9C', - 'XN--GK3AT1E', - 'XN--H2BREG3EVE', - 'XN--H2BRJ9C', - 'XN--H2BRJ9C8C', - 'XN--HXT814E', - 'XN--I1B6B1A6A2E', - 'XN--IMR513N', - 'XN--IO0A7I', - 'XN--J1AEF', - 'XN--J1AMH', - 'XN--J6W193G', - 'XN--JLQ480N2RG', - 'XN--JLQ61U9W7B', - 'XN--JVR189M', - 'XN--KCRX77D1X4A', - 'XN--KPRW13D', - 'XN--KPRY57D', - 'XN--KPUT3I', - 'XN--L1ACC', - 'XN--LGBBAT1AD8J', - 'XN--MGB9AWBF', - 'XN--MGBA3A3EJT', - 'XN--MGBA3A4F16A', - 'XN--MGBA7C0BBN0A', - 'XN--MGBAAKC7DVF', - 'XN--MGBAAM7A8H', - 'XN--MGBAB2BD', - 'XN--MGBAH1A3HJKRD', - 'XN--MGBAI9AZGQP6J', - 'XN--MGBAYH7GPA', - 'XN--MGBBH1A', - 'XN--MGBBH1A71E', - 'XN--MGBC0A9AZCG', - 'XN--MGBCA7DZDO', - 'XN--MGBCPQ6GPA1A', - 'XN--MGBERP4A5D4AR', - 'XN--MGBGU82A', - 'XN--MGBI4ECEXP', - 'XN--MGBPL2FH', - 'XN--MGBT3DHD', - 'XN--MGBTX2B', - 'XN--MGBX4CD0AB', - 'XN--MIX891F', - 'XN--MK1BU44C', - 'XN--MXTQ1M', - 'XN--NGBC5AZD', - 'XN--NGBE9E0A', - 'XN--NGBRX', - 'XN--NODE', - 'XN--NQV7F', - 'XN--NQV7FS00EMA', - 'XN--NYQY26A', - 'XN--O3CW4H', - 'XN--OGBPF8FL', - 'XN--OTU796D', - 'XN--P1ACF', - 'XN--P1AI', - 'XN--PGBS0DH', - 'XN--PSSY2U', - 'XN--Q7CE6A', - 'XN--Q9JYB4C', - 'XN--QCKA1PMC', - 'XN--QXA6A', - 'XN--QXAM', - 'XN--RHQV96G', - 'XN--ROVU88B', - 'XN--RVC1E0AM3E', - 'XN--S9BRJ9C', - 'XN--SES554G', - 'XN--T60B56A', - 'XN--TCKWE', - 'XN--TIQ49XQYJ', - 'XN--UNUP4Y', - 'XN--VERMGENSBERATER-CTB', - 'XN--VERMGENSBERATUNG-PWB', - 'XN--VHQUV', - 'XN--VUQ861B', - 'XN--W4R85EL8FHU5DNRA', - 'XN--W4RS40L', - 'XN--WGBH1C', - 'XN--WGBL6A', - 'XN--XHQ521B', - 'XN--XKC2AL3HYE2A', - 'XN--XKC2DL3A5EE0H', - 'XN--Y9A3AQ', - 'XN--YFRO4I67O', - 'XN--YGBI2AMMX', - 'XN--ZFR164B', - 'XXX', - 'XYZ', - ], - 'Y': [ - 'YACHTS', - 'YAHOO', - 'YAMAXUN', - 'YANDEX', - 'YE', - 'YODOBASHI', - 'YOGA', - 'YOKOHAMA', - 'YOU', - 'YOUTUBE', - 'YT', - 'YUN', - ], - 'Z': [ - 'ZA', - 'ZAPPOS', - 'ZARA', - 'ZERO', - 'ZIP', - 'ZM', - 'ZONE', - 'ZUERICH', - 'ZW', - ], -}; diff --git a/packages/stream_chat_flutter/lib/src/message_list_view/floating_date_divider.dart b/packages/stream_chat_flutter/lib/src/message_list_view/floating_date_divider.dart deleted file mode 100644 index 49d8381c5b..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_list_view/floating_date_divider.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart'; -import 'package:stream_chat_flutter/src/message_list_view/mlv_utils.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template floatingDateDivider} -/// Not intended for use outside of [MessageListView]. -/// {@endtemplate} -class FloatingDateDivider extends StatelessWidget { - /// {@macro floatingDateDivider} - const FloatingDateDivider({ - super.key, - required this.itemPositionListener, - required this.reverse, - required this.messages, - required this.itemCount, - this.isThreadConversation = false, - this.dateDividerBuilder, - }); - - /// true if this is a thread conversation - final bool isThreadConversation; - - // ignore: public_member_api_docs - final ValueListenable> itemPositionListener; - - // ignore: public_member_api_docs - final bool reverse; - - // ignore: public_member_api_docs - final List messages; - - // ignore: public_member_api_docs - final int itemCount; - - // ignore: public_member_api_docs - final Widget Function(DateTime)? dateDividerBuilder; - - @override - Widget build(BuildContext context) { - return ValueListenableBuilder( - valueListenable: itemPositionListener, - builder: (context, positions, child) { - if (positions.isEmpty || messages.isEmpty) { - return const Offstage(); - } - - var index = switch (reverse) { - true => getBottomElementIndex(positions), - false => getTopElementIndex(positions), - }; - - if ((index == null) || - (!isThreadConversation && index == itemCount - 2) || - (isThreadConversation && index == itemCount - 1)) { - return const Offstage(); - } - - if (index <= 2 || index >= itemCount - 3) { - if (reverse) { - index = itemCount - 4; - } else { - index = 2; - } - } - - final message = messages[index - 2]; - return dateDividerBuilder?.call(message.createdAt.toLocal()) ?? - StreamDateDivider(dateTime: message.createdAt.toLocal()); - }, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_list_view/loading_indicator.dart b/packages/stream_chat_flutter/lib/src/message_list_view/loading_indicator.dart deleted file mode 100644 index c730efb008..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_list_view/loading_indicator.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template loadingIndicatorMLV} -/// A loading indicator for [MessageListView]. Not intended for use outside of -/// [MessageListView]. -/// {@endtemplate} -class LoadingIndicator extends StatelessWidget { - /// {@macro loadingIndicatorMLV} - const LoadingIndicator({ - super.key, - required this.streamTheme, - required this.isThreadConversation, - required this.direction, - required this.streamChannelState, - this.indicatorBuilder, - }); - - // ignore: public_member_api_docs - final StreamChatThemeData streamTheme; - - // ignore: public_member_api_docs - final bool isThreadConversation; - - // ignore: public_member_api_docs - final QueryDirection direction; - - // ignore: public_member_api_docs - final StreamChannelState streamChannelState; - - // ignore: public_member_api_docs - final WidgetBuilder? indicatorBuilder; - - @override - Widget build(BuildContext context) { - final stream = direction == QueryDirection.top - ? streamChannelState.queryTopMessages - : streamChannelState.queryBottomMessages; - return BetterStreamBuilder( - key: Key('LOADING-INDICATOR $direction'), - stream: stream, - initialData: false, - errorBuilder: (context, error) => ColoredBox( - // ignore: deprecated_member_use - color: streamTheme.colorTheme.accentError.withOpacity(0.2), - child: Center( - child: Text(context.translations.loadingMessagesError), - ), - ), - builder: (context, data) { - if (!data) return const Offstage(); - return indicatorBuilder?.call(context) ?? - const Center( - child: Padding( - padding: EdgeInsets.all(8), - child: CircularProgressIndicator.adaptive(), - ), - ); - }, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_list_view/message_details.dart b/packages/stream_chat_flutter/lib/src/message_list_view/message_details.dart deleted file mode 100644 index 36ffd887bf..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_list_view/message_details.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template messageDetails} -/// Class for message details -/// {@endtemplate} -// ignore: prefer-match-file-name -class MessageDetails { - /// {@macro messageDetails} - MessageDetails( - String currentUserId, - this.message, - List messages, - this.index, - ) { - isMyMessage = message.user?.id == currentUserId; - isLastUser = index + 1 < messages.length && - message.user?.id == messages[index + 1].user?.id; - isNextUser = - index - 1 >= 0 && message.user!.id == messages[index - 1].user?.id; - } - - /// True if the message belongs to the current user - late final bool isMyMessage; - - /// True if the user message is the same of the previous message - late final bool isLastUser; - - /// True if the user message is the same of the next message - late final bool isNextUser; - - /// The message - final Message message; - - /// The index of the message - final int index; -} diff --git a/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart b/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart deleted file mode 100644 index 9f86679faf..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart +++ /dev/null @@ -1,1579 +0,0 @@ -// ignore_for_file: lines_longer_than_80_chars -import 'dart:async'; -import 'dart:math'; - -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_portal/flutter_portal.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart'; -import 'package:stream_chat_flutter/src/message_list_view/floating_date_divider.dart'; -import 'package:stream_chat_flutter/src/message_list_view/loading_indicator.dart'; -import 'package:stream_chat_flutter/src/message_list_view/mlv_utils.dart'; -import 'package:stream_chat_flutter/src/message_list_view/thread_separator.dart'; -import 'package:stream_chat_flutter/src/message_list_view/unread_messages_separator.dart'; -import 'package:stream_chat_flutter/src/message_widget/ephemeral_message.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Spacing Types (These are properties of a message to help inform the decision -/// of how much space / which widget to build after it) -enum SpacingType { - /// Message is a thread - thread, - - /// There is a >1s time diff between current and last message - timeDiff, - - /// Next message is by a different user - otherUser, - - /// Message is deleted - deleted, - - /// No other conditions are valid, default spacing (This will likely be the - /// only rule in the list provided) - defaultSpacing, -} - -/// {@template streamMessageListView} -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/message_listview.png) -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/message_listview_paint.png) -/// -/// Shows the list of messages in the current channel. -/// -/// ```dart -/// class ChannelPage extends StatelessWidget { -/// const ChannelPage({ -/// Key? key, -/// }) : super(key: key); -/// -/// @override -/// Widget build(BuildContext context) { -/// return Scaffold( -/// appBar: StreamChannelHeader(), -/// body: Column( -/// children: [ -/// Expanded( -/// child: StreamMessageListView( -/// threadBuilder: (_, parentMessage) { -/// return ThreadPage( -/// parent: parentMessage, -/// ); -/// }, -/// ), -/// ), -/// StreamMessageInput(), -/// ], -/// ), -/// ); -/// } -/// } -/// ``` -/// -/// A [StreamChannel] ancestor widget is required in order to provide the -/// information about the channels. -/// -/// Uses a [ScrollablePositionedList] to render the list of channels. -/// -/// The UI is rendered based on the first ancestor of type [StreamChatTheme]. -/// Modify it to change the widget's appearance. -/// {@endtemplate} -class StreamMessageListView extends StatefulWidget { - /// {@macro streamMessageListView} - const StreamMessageListView({ - super.key, - this.showScrollToBottom = true, - this.showUnreadCountOnScrollToBottom = false, - this.scrollToBottomBuilder, - this.showUnreadIndicator = true, - this.unreadIndicatorBuilder, - this.markReadWhenAtTheBottom = false, - this.messageBuilder, - this.parentMessageBuilder, - this.parentMessage, - this.threadBuilder, - this.onThreadTap, - this.dateDividerBuilder, - // we need to use ClampingScrollPhysics to avoid the list view to bounce - // when we are at the either end of the list view and try to use 'animateTo' - // to animate in the same direction. - this.scrollPhysics = const ClampingScrollPhysics(), - this.initialScrollIndex, - this.initialAlignment, - this.scrollController, - this.itemPositionListener, - this.highlightInitialMessage = false, - this.messageHighlightColor, - this.showConnectionStateTile = false, - this.headerBuilder, - this.footerBuilder, - this.loadingBuilder, - this.emptyBuilder, - this.systemMessageBuilder, - this.ephemeralMessageBuilder, - this.messageListBuilder, - this.errorBuilder, - this.messageFilter, - this.onMessageTap, - this.onSystemMessageTap, - this.showFloatingDateDivider = true, - this.threadSeparatorBuilder, - this.unreadMessagesSeparatorBuilder, - this.messageListController, - this.reverse = true, - this.shrinkWrap = false, - this.paginationLimit = 20, - this.paginationLoadingIndicatorBuilder, - this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.onDrag, - this.spacingWidgetBuilder = _defaultSpacingWidgetBuilder, - }); - - /// [ScrollViewKeyboardDismissBehavior] the defines how this [PositionedList] will - /// dismiss the keyboard automatically. - final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior; - - /// {@macro messageBuilder} - final MessageBuilder? messageBuilder; - - /// Whether the view scrolls in the reading direction. - /// - /// Defaults to true. - /// - /// See [ScrollView.reverse]. - final bool reverse; - - /// Whether the extent of the scroll view in the [scrollDirection] should be - /// determined by the contents being viewed. - /// - /// Defaults to false. - /// - /// See [ScrollView.shrinkWrap]. - final bool shrinkWrap; - - /// Limit used during pagination - final int paginationLimit; - - /// {@macro systemMessageBuilder} - final SystemMessageBuilder? systemMessageBuilder; - - /// {@macro ephemeralMessageBuilder} - final EphemeralMessageBuilder? ephemeralMessageBuilder; - - /// {@macro parentMessageBuilder} - final ParentMessageBuilder? parentMessageBuilder; - - /// {@macro threadBuilder} - final ThreadBuilder? threadBuilder; - - /// {@macro threadTapCallback} - /// - /// By default it calls [Navigator.push] using the widget - /// built using [threadBuilder] - final ThreadTapCallback? onThreadTap; - - /// If true will show a scroll to bottom button when - /// the scroll offset is not zero - final bool showScrollToBottom; - - /// If true will show an indicator with number of unread messages - /// on scroll to bottom button - final bool showUnreadCountOnScrollToBottom; - - /// Function used to build a custom scroll to bottom widget - /// - /// Provides the current unread messages count and a reference - /// to the function that is executed on tap of this widget by default - /// - /// As an example: - /// ``` - /// MessageListView( - /// scrollToBottomBuilder: (unreadCount, defaultTapAction) { - /// return InkWell( - /// onTap: () => defaultTapAction(unreadCount), - /// child: Text('Scroll To Bottom'), - /// ); - /// }, - /// ), - /// ``` - final Widget Function( - int unreadCount, - Future Function(int) scrollToBottomDefaultTapAction, - )? scrollToBottomBuilder; - - /// If true will show an indicator with number of unread messages - /// that will scroll to latest read message when tapped and mark - /// channel as read when dismissed - final bool showUnreadIndicator; - - /// Function used to build a custom unread indicator widget - /// - /// Provides the current unread messages count and a reference - /// to the function that is executed on tap to scroll to latest - /// read message by default - /// - /// As an example: - /// ``` - /// MessageListView( - /// unreadIndicatorBuilder: (unreadCount, defaultTapAction, dismissAction) { - /// return InkWell( - /// onTap: () => defaultTapAction(unreadCount), - /// child: Text('Scroll To Unread'), - /// ); - /// }, - /// ), - /// ``` - final Widget Function( - int unreadCount, - Future Function(String) scrollToUnreadDefaultTapAction, - Future Function() dismissIndicatorDefaultTapAction, - )? unreadIndicatorBuilder; - - /// If true will mark channel as read when the user scrolls to the bottom of the list - final bool markReadWhenAtTheBottom; - - /// Parent message in case of a thread - final Message? parentMessage; - - /// Builder used to render date dividers - final Widget Function(DateTime)? dateDividerBuilder; - - /// Index of an item to initially align within the viewport. - final int? initialScrollIndex; - - /// Determines where the leading edge of the item at [initialScrollIndex] - /// should be placed. - final double? initialAlignment; - - /// Controller for jumping or scrolling to an item. - final ItemScrollController? scrollController; - - /// Provides a listenable iterable of [itemPositions] of items that are on - /// screen and their locations. - final ItemPositionsListener? itemPositionListener; - - /// The ScrollPhysics used by the ListView - final ScrollPhysics? scrollPhysics; - - /// If true the list will highlight the initialMessage if there is any. - /// - /// Also See [StreamChannel] - final bool highlightInitialMessage; - - /// Color used while highlighting initial message - final Color? messageHighlightColor; - - /// Flag for showing tile on header - final bool showConnectionStateTile; - - /// Flag for showing the floating date divider - final bool showFloatingDateDivider; - - /// Function called when messages are fetched - final Widget Function(BuildContext, List)? messageListBuilder; - - /// Function used to build a header widget - final WidgetBuilder? headerBuilder; - - /// Function used to build a footer widget - final WidgetBuilder? footerBuilder; - - /// Function used to build a loading widget - final WidgetBuilder? loadingBuilder; - - /// Function used to build an empty widget - final WidgetBuilder? emptyBuilder; - - /// Callback triggered when an error occurs while performing the - /// given request. - /// - /// This parameter can be used to display an error message to - /// users in the event of a connection failure. - final ErrorBuilder? errorBuilder; - - /// Predicate used to filter messages - final bool Function(Message)? messageFilter; - - /// Called when any message is tapped except a system message - /// (use [onSystemMessageTap] instead) - final OnMessageTap? onMessageTap; - - /// Called when system message is tapped - final OnMessageTap? onSystemMessageTap; - - /// Builder used to build the thread separator in case it's a thread view - final Function(BuildContext context, Message parentMessage)? - threadSeparatorBuilder; - - /// Builder used to build the unread message separator - final Widget Function(BuildContext context, int unreadCount)? - unreadMessagesSeparatorBuilder; - - /// A [MessageListController] allows pagination. - /// - /// Use [ChannelListController.paginateData] pagination. - final MessageListController? messageListController; - - /// Builder used to build the loading indicator shown while paginating. - final WidgetBuilder? paginationLoadingIndicatorBuilder; - - /// {@macro spacingWidgetBuilder} - final SpacingWidgetBuilder spacingWidgetBuilder; - - static Widget _defaultSpacingWidgetBuilder( - BuildContext context, - List spacingTypes, - ) { - if (spacingTypes.contains(SpacingType.otherUser)) { - return const SizedBox(height: 8); - } else if (spacingTypes.contains(SpacingType.thread)) { - return const SizedBox(height: 8); - } else if (spacingTypes.contains(SpacingType.timeDiff)) { - return const SizedBox(height: 8); - } - - return const SizedBox(height: 2); - } - - @override - _StreamMessageListViewState createState() => _StreamMessageListViewState(); -} - -class _StreamMessageListViewState extends State { - ItemScrollController? _scrollController; - void Function(Message)? _onThreadTap; - final ValueNotifier _showScrollToBottom = ValueNotifier(false); - late final ItemPositionsListener _itemPositionListener; - int? _messageListLength; - StreamChannelState? streamChannel; - late StreamChatThemeData _streamTheme; - late List _userPermissions; - late int unreadCount; - - double get _initialAlignment { - final initialAlignment = widget.initialAlignment; - if (initialAlignment != null) return initialAlignment; - return initialIndex == 0 ? 0 : 0.1; - } - - bool get _upToDate => streamChannel!.channel.state!.isUpToDate; - - bool get _isThreadConversation => widget.parentMessage != null; - - bool _bottomPaginationActive = false; - - int initialIndex = 0; - double initialAlignment = 0; - - List messages = []; - Map messagesIndex = {}; - - bool initialMessageHighlightComplete = false; - - bool _inBetweenList = false; - - late final _defaultController = MessageListController(); - - MessageListController get _messageListController => - widget.messageListController ?? _defaultController; - - StreamSubscription? _messageNewListener; - StreamSubscription? _userReadListener; - - Read? _userRead; - Message? _oldestUnreadMessage; - - @override - void initState() { - super.initState(); - - _scrollController = widget.scrollController ?? ItemScrollController(); - _itemPositionListener = - widget.itemPositionListener ?? ItemPositionsListener.create(); - _itemPositionListener.itemPositions - .addListener(_handleItemPositionsChanged); - - _getOnThreadTap(); - } - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - final newStreamChannel = StreamChannel.of(context); - _streamTheme = StreamChatTheme.of(context); - _userPermissions = newStreamChannel.channel.ownCapabilities; - - if (newStreamChannel != streamChannel) { - streamChannel = newStreamChannel; - - _userRead = streamChannel?.channel.state!.currentUserRead; - - _messageNewListener?.cancel(); - _userReadListener?.cancel(); - - unreadCount = streamChannel?.channel.state?.unreadCount ?? 0; - initialIndex = getInitialIndex( - widget.initialScrollIndex, - streamChannel!, - widget.messageFilter, - _userRead, - ); - - initialAlignment = _initialAlignment; - - if (_scrollController?.isAttached == true) { - _scrollController?.jumpTo( - index: initialIndex, - alignment: initialAlignment, - ); - } - - _messageNewListener = - streamChannel!.channel.on(EventType.messageNew).listen((event) { - if (_upToDate) { - _bottomPaginationActive = false; - } - if (event.message?.parentId == widget.parentMessage?.id && - event.message!.user!.id == - streamChannel!.channel.client.state.currentUser!.id) { - setState(() => unreadCount = 0); - - WidgetsBinding.instance.addPostFrameCallback((_) { - _scrollController?.jumpTo( - index: 0, - ); - }); - } - }); - - _userReadListener = - streamChannel!.channel.state?.readStream.listen((event) { - setState(() { - unreadCount = streamChannel!.channel.state?.unreadCount ?? 0; - _userRead = streamChannel!.channel.state?.currentUserRead; - }); - }); - - if (_isThreadConversation) { - streamChannel!.getReplies(widget.parentMessage!.id); - } - - unreadCount = streamChannel?.channel.state?.unreadCount ?? 0; - } - } - - @override - void dispose() { - if (!_upToDate) { - streamChannel!.reloadChannel(); - } - debouncedMarkRead?.cancel(); - debouncedMarkThreadRead?.cancel(); - _messageNewListener?.cancel(); - _userReadListener?.cancel(); - _itemPositionListener.itemPositions - .removeListener(_handleItemPositionsChanged); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Portal( - labels: const [kPortalMessageListViewLabel], - child: ScaffoldMessenger( - child: MessageListCore( - paginationLimit: widget.paginationLimit, - messageFilter: widget.messageFilter, - loadingBuilder: widget.loadingBuilder ?? - (context) => const Center( - child: CircularProgressIndicator.adaptive(), - ), - emptyBuilder: widget.emptyBuilder ?? - (context) => Center( - child: Text( - context.translations.emptyChatMessagesText, - style: _streamTheme.textTheme.footnote.copyWith( - color: _streamTheme.colorTheme.textHighEmphasis - // ignore: deprecated_member_use - .withOpacity(0.5), - ), - ), - ), - messageListBuilder: widget.messageListBuilder ?? - (context, list) => _buildListView(list), - messageListController: _messageListController, - parentMessage: widget.parentMessage, - errorBuilder: widget.errorBuilder ?? - (BuildContext context, Object error) => Center( - child: Text( - context.translations.genericErrorText, - style: _streamTheme.textTheme.footnote.copyWith( - color: _streamTheme.colorTheme.textHighEmphasis - // ignore: deprecated_member_use - .withOpacity(0.5), - ), - ), - ), - ), - ), - ); - } - - Widget _buildListView(List data) { - messages = data; - - _oldestUnreadMessage = messages.lastUnreadMessage(_userRead); - - for (var index = 0; index < messages.length; index++) { - messagesIndex[messages[index].id] = index; - } - final newMessagesListLength = messages.length; - - if (_messageListLength != null) { - if (_bottomPaginationActive || (_inBetweenList && _upToDate)) { - if (_itemPositionListener.itemPositions.value.isNotEmpty) { - final first = _itemPositionListener.itemPositions.value.first; - final diff = newMessagesListLength - _messageListLength!; - if (diff > 0) { - if (messages[0].user?.id != - streamChannel!.channel.client.state.currentUser?.id) { - initialIndex = first.index + diff; - initialAlignment = first.itemLeadingEdge; - } - } - } - } - } - - _messageListLength = newMessagesListLength; - - final itemCount = messages.length + // total messages - 2 + // top + bottom loading indicator - 2 + // header + footer - 1 // parent message - ; - - final child = Stack( - alignment: Alignment.center, - children: [ - StreamConnectionStatusBuilder( - statusBuilder: (context, status) { - var statusString = ''; - var showStatus = true; - switch (status) { - case ConnectionStatus.connected: - statusString = context.translations.connectedLabel; - showStatus = false; - break; - case ConnectionStatus.connecting: - statusString = context.translations.reconnectingLabel; - break; - case ConnectionStatus.disconnected: - statusString = context.translations.disconnectedLabel; - break; - } - - return StreamInfoTile( - showMessage: widget.showConnectionStateTile && showStatus, - tileAnchor: Alignment.topCenter, - childAnchor: Alignment.topCenter, - message: statusString, - child: LazyLoadScrollView( - onStartOfPage: () async { - _inBetweenList = false; - if (!_upToDate) { - _bottomPaginationActive = true; - return _paginateData( - streamChannel, - QueryDirection.bottom, - ); - } - }, - onEndOfPage: () async { - _inBetweenList = false; - _bottomPaginationActive = false; - return _paginateData( - streamChannel, - QueryDirection.top, - ); - }, - onInBetweenOfPage: () { - _inBetweenList = true; - }, - child: ScrollablePositionedList.separated( - key: (initialIndex != 0 && initialAlignment != 0) - ? ValueKey('$initialIndex-$initialAlignment') - : null, - keyboardDismissBehavior: widget.keyboardDismissBehavior, - itemPositionsListener: _itemPositionListener, - initialScrollIndex: initialIndex, - initialAlignment: initialAlignment, - physics: widget.scrollPhysics, - itemScrollController: _scrollController, - reverse: widget.reverse, - shrinkWrap: widget.shrinkWrap, - itemCount: itemCount, - - // Commented out as it is not working as expected. - // The list view gets broken in the following case: - // * The list view is loaded at a particular message (eg: Last Read, or a quoted message) - // and a new message is added to the list view. - // - // Issues faced: - // * https://github.com/GetStream/stream-chat-flutter/issues/1576 - // * https://github.com/GetStream/stream-chat-flutter/issues/1414 - // - // Related issues: https://github.com/flutter/flutter/issues/107123 - // - // findChildIndexCallback: (Key key) { - // final indexedKey = key as IndexedKey; - // final valueKey = indexedKey.key as ValueKey?; - // if (valueKey != null) { - // final index = messagesIndex[valueKey.value]; - // if (index != null) { - // // The calculation is as follows: - // // * Add 2 to the index retrieved to account for the footer and the bottom loader. - // // * Multiply the result by 2 to account for the separators between each pair of items. - // // * Subtract 1 to adjust for the 0-based indexing of the list view. - // return ((index + 2) * 2) - 1; - // } - // } - // return null; - // }, - - // Item Count -> 8 (1 parent, 2 header+footer, 2 top+bottom, 3 messages) - // eg: |Type| rev(|Index(item)|) rev(|Index(separator)|) |Index(item)| |Index(separator)| - // ParentMessage -> 7 (count-1) - // Separator(ThreadSeparator) -> 6 (count-2) - // Header -> 6 (count-2) - // Separator(Header -> 8??T -> 0||52) -> 5 (count-3) - // TopLoader -> 5 (count-3) - // Separator(0) -> 4 (count-4) - // Message -> 4 (count-4) - // Separator(2||8) -> 3 (count-5) - // Message -> 3 (count-5) - // Separator(2||8) -> 2 (count-6) - // Message -> 2 (count-6) - // Separator(0) -> 1 (count-7) - // BottomLoader -> 1 (count-7) - // Separator(Footer -> 8??30) -> 0 (count-8) - // Footer -> 0 (count-8) - - separatorBuilder: (context, i) { - if (i == itemCount - 2) { - if (widget.parentMessage == null) { - return const Offstage(); - } - - if (widget.threadSeparatorBuilder != null) { - return widget.threadSeparatorBuilder! - .call(context, widget.parentMessage!); - } - - return ThreadSeparator( - parentMessage: widget.parentMessage, - ); - } - if (i == itemCount - 3) { - if (widget.reverse - ? widget.headerBuilder == null - : widget.footerBuilder == null) { - if (messages.isNotEmpty) { - return _buildDateDivider(messages.last); - } - if (_isThreadConversation) return const Offstage(); - return const SizedBox(height: 52); - } - return const SizedBox(height: 8); - } - if (i == 0) { - if (widget.reverse - ? widget.footerBuilder == null - : widget.headerBuilder == null) { - return const SizedBox(height: 30); - } - return const SizedBox(height: 8); - } - - if (i == 1 || i == itemCount - 4) return const Offstage(); - - late final Message message, nextMessage; - if (widget.reverse) { - message = messages[i - 1]; - nextMessage = messages[i - 2]; - } else { - message = messages[i - 2]; - nextMessage = messages[i - 1]; - } - - Widget separator; - - final isPartOfThread = message.replyCount! > 0 || - message.showInChannel == true; - - final createdAt = Jiffy.parseFromDateTime( - message.createdAt.toLocal(), - ); - - final nextCreatedAt = Jiffy.parseFromDateTime( - nextMessage.createdAt.toLocal(), - ); - - if (!createdAt.isSame(nextCreatedAt, unit: Unit.day)) { - separator = _buildDateDivider(nextMessage); - } else { - final hasTimeDiff = !createdAt.isSame( - nextCreatedAt, - unit: Unit.minute, - ); - - final isNextUserSame = - message.user!.id == nextMessage.user?.id; - final isDeleted = message.isDeleted; - - final spacingRules = [ - if (hasTimeDiff) SpacingType.timeDiff, - if (!isNextUserSame) SpacingType.otherUser, - if (isPartOfThread) SpacingType.thread, - if (isDeleted) SpacingType.deleted, - ]; - - if (spacingRules.isEmpty) { - spacingRules.add(SpacingType.defaultSpacing); - } - - separator = widget.spacingWidgetBuilder.call( - context, - spacingRules, - ); - } - - if (!isPartOfThread && - unreadCount > 0 && - _oldestUnreadMessage?.id == nextMessage.id) { - final unreadMessagesSeparator = - _buildUnreadMessagesSeparator(unreadCount); - - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - separator, - unreadMessagesSeparator, - ], - ); - } - return separator; - }, - itemBuilder: (context, i) { - if (i == itemCount - 1) { - if (widget.parentMessage == null) { - return const Offstage(); - } - return buildParentMessage(widget.parentMessage!); - } - - if (i == itemCount - 2) { - if (widget.reverse) { - return widget.headerBuilder?.call(context) ?? - const Offstage(); - } else { - return widget.footerBuilder?.call(context) ?? - const Offstage(); - } - } - - final indicatorBuilder = - widget.paginationLoadingIndicatorBuilder; - - if (i == itemCount - 3) { - return LoadingIndicator( - direction: QueryDirection.top, - streamTheme: _streamTheme, - streamChannelState: streamChannel!, - isThreadConversation: _isThreadConversation, - indicatorBuilder: indicatorBuilder, - ); - } - - if (i == 1) { - return LoadingIndicator( - direction: QueryDirection.bottom, - streamTheme: _streamTheme, - streamChannelState: streamChannel!, - isThreadConversation: _isThreadConversation, - indicatorBuilder: indicatorBuilder, - ); - } - - if (i == 0) { - if (widget.reverse) { - return widget.footerBuilder?.call(context) ?? - const Offstage(); - } else { - return widget.headerBuilder?.call(context) ?? - const Offstage(); - } - } - - const bottomMessageIndex = 2; // 1 -> loader // 0 -> footer - - final message = messages[i - 2]; - Widget messageWidget; - - if (i == bottomMessageIndex) { - messageWidget = _buildBottomMessage( - context, - message, - messages, - streamChannel!, - i - 2, - ); - } else { - messageWidget = buildMessage(message, messages, i - 2); - } - return KeyedSubtree( - key: ValueKey(message.id), - child: messageWidget, - ); - }, - ), - ), - ); - }, - ), - if (widget.showFloatingDateDivider) - Positioned( - top: 20, - left: 0, - right: 0, - child: FloatingDateDivider( - itemCount: itemCount, - reverse: widget.reverse, - itemPositionListener: _itemPositionListener.itemPositions, - messages: messages, - dateDividerBuilder: widget.dateDividerBuilder, - isThreadConversation: _isThreadConversation, - ), - ), - if (widget.showScrollToBottom) - BetterStreamBuilder( - stream: streamChannel!.channel.state!.isUpToDateStream, - initialData: streamChannel!.channel.state!.isUpToDate, - builder: (context, snapshot) => ValueListenableBuilder( - valueListenable: _showScrollToBottom, - child: _buildScrollToBottom(), - builder: (context, value, child) { - if (!snapshot || value) { - return child!; - } - return const Offstage(); - }, - ), - ), - if (widget.showUnreadIndicator) _buildShowUnreadBottom(), - ], - ); - - final backgroundColor = - StreamMessageListViewTheme.of(context).backgroundColor; - final backgroundImage = - StreamMessageListViewTheme.of(context).backgroundImage; - - if (backgroundColor != null || backgroundImage != null) { - return DecoratedBox( - decoration: BoxDecoration( - color: backgroundColor, - image: backgroundImage, - ), - child: child, - ); - } - - return child; - } - - Widget _buildUnreadMessagesSeparator(int unreadCount) { - final unreadMessagesSeparator = - widget.unreadMessagesSeparatorBuilder?.call(context, unreadCount) ?? - UnreadMessagesSeparator(unreadCount: unreadCount); - return unreadMessagesSeparator; - } - - Future _paginateData( - StreamChannelState? channel, - QueryDirection direction, - ) => - _messageListController.paginateData!(direction: direction); - - Future scrollToBottomDefaultTapAction(int unreadCount) async { - // If the channel is not up to date, we need to reload it before scrolling - // to the end of the list. - if (!_upToDate) { - // Reset the pagination variables. - initialIndex = 0; - initialAlignment = 0; - _bottomPaginationActive = false; - - // Reload the channel to get the latest messages. - await streamChannel!.reloadChannel(); - - // Wait for the frame to be rendered with the updated channel state. - await WidgetsBinding.instance.endOfFrame; - } - - // Scroll to the end of the list. - if (_scrollController?.isAttached == true) { - _scrollController!.scrollTo( - index: max( - messages.toList().indexWhere((element) => - element.id == - streamChannel! - .channel.state?.currentUserRead?.lastReadMessageId), - 0), - duration: const Duration(seconds: 1), - curve: Curves.easeInOut, - ); - } - } - - Future scrollToUnreadDefaultTapAction(String lastReadMessageId) async { - // If the channel is not up to date, we need to reload it before scrolling - if (!_upToDate) { - // Reset the pagination variables. - initialIndex = 0; - initialAlignment = 0; - _bottomPaginationActive = false; - - // Reload the channel to get the latest messages. - await streamChannel!.reloadChannel(); - - // Wait for the frame to be rendered with the updated channel state. - await WidgetsBinding.instance.endOfFrame; - } - - // Scroll to the end of the list. - if (_scrollController?.isAttached == true) { - _scrollController!.scrollTo( - index: max( - messages - .toList() - .indexWhere((element) => element.id == lastReadMessageId), - 0), - duration: const Duration(seconds: 1), - curve: Curves.easeInOut, - ); - } - } - - late final debouncedMarkRead = switch (streamChannel) { - final streamChannel? => debounce( - streamChannel.channel.markRead, - const Duration(seconds: 1), - ), - _ => null, - }; - - late final debouncedMarkThreadRead = switch (streamChannel) { - final streamChannel? => debounce( - streamChannel.channel.markThreadRead, - const Duration(seconds: 1), - ), - _ => null, - }; - - Future dismissIndicatorDefaultTapAction() async { - // Mark regular messages as read. - debouncedMarkRead?.call(); - - // Mark thread messages as read. - if (widget.parentMessage case final parent?) { - debouncedMarkThreadRead?.call([parent.id]); - } - } - - Widget _buildDateDivider(Message message) { - final divider = widget.dateDividerBuilder != null - ? widget.dateDividerBuilder!( - message.createdAt.toLocal(), - ) - : Padding( - padding: const EdgeInsets.symmetric(vertical: 12), - child: StreamDateDivider( - dateTime: message.createdAt.toLocal(), - ), - ); - return divider; - } - - Widget _buildBottomMessage( - BuildContext context, - Message message, - List messages, - StreamChannelState streamChannel, - int index, - ) { - final messageWidget = buildMessage(message, messages, index); - return messageWidget; - } - - Widget buildParentMessage( - Message message, - ) { - final isMyMessage = - message.user!.id == StreamChat.of(context).currentUser!.id; - final isOnlyEmoji = message.text?.isOnlyEmoji ?? false; - final currentUser = StreamChat.of(context).currentUser; - final members = StreamChannel.of(context).channel.state?.members ?? []; - final currentUserMember = - members.firstWhereOrNull((e) => e.user!.id == currentUser!.id); - - final hasFileAttachment = - message.attachments.any((it) => it.type == AttachmentType.file); - - final hasUrlAttachment = - message.attachments.any((it) => it.type == AttachmentType.urlPreview); - - final attachmentBorderRadius = hasUrlAttachment - ? 8.0 - : hasFileAttachment - ? 12.0 - : 14.0; - - final borderSide = isOnlyEmoji ? BorderSide.none : null; - - final defaultMessageWidget = StreamMessageWidget( - showReplyMessage: false, - showResendMessage: false, - showThreadReplyMessage: false, - showCopyMessage: false, - showDeleteMessage: false, - showEditMessage: false, - showMarkUnreadMessage: false, - message: message, - reverse: isMyMessage, - showUsername: !isMyMessage, - padding: const EdgeInsets.all(8), - showSendingIndicator: false, - attachmentPadding: EdgeInsets.all( - hasUrlAttachment - ? 8 - : hasFileAttachment - ? 4 - : 2, - ), - attachmentShape: RoundedRectangleBorder( - side: BorderSide( - color: _streamTheme.colorTheme.borders, - strokeAlign: BorderSide.strokeAlignOutside, - ), - borderRadius: BorderRadius.only( - topLeft: Radius.circular(attachmentBorderRadius), - bottomLeft: isMyMessage - ? Radius.circular(attachmentBorderRadius) - : Radius.zero, - topRight: Radius.circular(attachmentBorderRadius), - bottomRight: isMyMessage - ? Radius.zero - : Radius.circular(attachmentBorderRadius), - ), - ), - borderRadiusGeometry: BorderRadius.only( - topLeft: const Radius.circular(16), - bottomLeft: isMyMessage ? const Radius.circular(16) : Radius.zero, - topRight: const Radius.circular(16), - bottomRight: isMyMessage ? Radius.zero : const Radius.circular(16), - ), - textPadding: EdgeInsets.symmetric( - vertical: 8, - horizontal: isOnlyEmoji ? 0 : 16.0, - ), - borderSide: borderSide, - showUserAvatar: isMyMessage ? DisplayWidget.gone : DisplayWidget.show, - messageTheme: isMyMessage - ? _streamTheme.ownMessageTheme - : _streamTheme.otherMessageTheme, - onMessageTap: (message) { - widget.onMessageTap?.call(message); - FocusScope.of(context).unfocus(); - }, - showPinButton: currentUserMember != null && - _userPermissions.contains(PermissionType.pinMessage), - ); - - if (widget.parentMessageBuilder != null) { - return widget.parentMessageBuilder!.call( - context, - widget.parentMessage, - defaultMessageWidget, - ); - } - - return defaultMessageWidget; - } - - Widget _buildScrollToBottom() { - return StreamBuilder( - stream: streamChannel!.channel.state!.unreadCountStream, - builder: (_, snapshot) { - if (snapshot.hasError) { - return const Offstage(); - } else if (!snapshot.hasData) { - return const Offstage(); - } - final unreadCount = snapshot.data!; - if (widget.scrollToBottomBuilder != null) { - return widget.scrollToBottomBuilder!( - unreadCount, - scrollToBottomDefaultTapAction, - ); - } - final showUnreadCount = unreadCount > 0 && - streamChannel!.channel.state!.members.any((e) => - e.userId == - streamChannel!.channel.client.state.currentUser!.id); - - return Positioned( - bottom: 8, - right: 8, - width: 40, - height: 40, - child: Stack( - clipBehavior: Clip.none, - children: [ - FloatingActionButton( - backgroundColor: _streamTheme.colorTheme.barsBg, - onPressed: () async { - return scrollToBottomDefaultTapAction(unreadCount); - }, - child: widget.reverse - ? StreamSvgIcon( - icon: StreamSvgIcons.down, - color: _streamTheme.colorTheme.textHighEmphasis, - ) - : StreamSvgIcon( - icon: StreamSvgIcons.up, - color: _streamTheme.colorTheme.textHighEmphasis, - ), - ), - if (showUnreadCount && widget.showUnreadCountOnScrollToBottom) - Positioned( - left: 0, - right: 0, - top: -10, - child: Center( - child: Material( - borderRadius: BorderRadius.circular(8), - color: - StreamChatTheme.of(context).colorTheme.accentPrimary, - child: Padding( - padding: const EdgeInsets.only( - left: 5, - right: 5, - top: 2, - bottom: 2, - ), - child: Text( - '${unreadCount > 99 ? '99+' : unreadCount}', - style: const TextStyle( - fontSize: 11, - color: Colors.white, - ), - ), - ), - ), - ), - ), - ], - ), - ); - }, - ); - } - - Widget _buildShowUnreadBottom() { - return StreamBuilder( - stream: streamChannel!.channel.state!.unreadCountStream, - builder: (_, snapshot) { - if (snapshot.hasError) { - return const Offstage(); - } else if (!snapshot.hasData) { - return const Offstage(); - } - final unreadCount = snapshot.data!; - - if (widget.unreadIndicatorBuilder != null) { - return widget.unreadIndicatorBuilder!( - unreadCount, - scrollToUnreadDefaultTapAction, - dismissIndicatorDefaultTapAction, - ); - } - - final showUnread = unreadCount > 0 && - streamChannel!.channel.state!.members.any((e) => - e.userId == - streamChannel!.channel.client.state.currentUser!.id); - - if (!showUnread) return const Offstage(); - - final lastReadMessageId = - streamChannel!.channel.state!.currentUserRead?.lastReadMessageId; - - return Positioned( - top: 8, - child: GestureDetector( - onTap: lastReadMessageId != null - ? () => scrollToUnreadDefaultTapAction(lastReadMessageId) - : null, - child: Container( - // width: 120, - padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), - height: 40, - decoration: BoxDecoration( - color: _streamTheme.colorTheme.textLowEmphasis, - borderRadius: BorderRadius.circular(18), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Text( - context.translations - .unreadCountIndicatorLabel(unreadCount: unreadCount), - style: TextStyle(color: _streamTheme.colorTheme.barsBg), - ), - const SizedBox(width: 16), - GestureDetector( - onTap: dismissIndicatorDefaultTapAction, - child: Icon( - Icons.close, - color: _streamTheme.colorTheme.barsBg, - ), - ), - ], - ), - ), - ), - ); - }, - ); - } - - Widget buildMessage(Message message, List messages, int index) { - if ((message.isSystem || message.isError) && - message.text?.isNotEmpty == true) { - return widget.systemMessageBuilder?.call(context, message) ?? - StreamSystemMessage( - message: message, - onMessageTap: (message) { - widget.onSystemMessageTap?.call(message); - FocusScope.of(context).unfocus(); - }, - ); - } - - if (message.isEphemeral) { - return widget.ephemeralMessageBuilder?.call(context, message) ?? - StreamEphemeralMessage(message: message); - } - - final userId = StreamChat.of(context).currentUser!.id; - final isMyMessage = message.user?.id == userId; - final nextMessage = index - 1 >= 0 ? messages[index - 1] : null; - final isNextUserSame = - nextMessage != null && message.user!.id == nextMessage.user!.id; - - var hasTimeDiff = false; - if (nextMessage != null) { - final createdAt = Jiffy.parseFromDateTime(message.createdAt.toLocal()); - final nextCreatedAt = Jiffy.parseFromDateTime( - nextMessage.createdAt.toLocal(), - ); - - hasTimeDiff = !createdAt.isSame(nextCreatedAt, unit: Unit.minute); - } - - final hasVoiceRecordingAttachment = message.attachments - .any((it) => it.type == AttachmentType.voiceRecording); - - final hasFileAttachment = - message.attachments.any((it) => it.type == AttachmentType.file); - - final hasUrlAttachment = - message.attachments.any((it) => it.type == AttachmentType.urlPreview); - - final isThreadMessage = - message.parentId != null && message.showInChannel == true; - - final hasReplies = message.replyCount! > 0; - - final attachmentBorderRadius = hasUrlAttachment - ? 8.0 - : hasFileAttachment - ? 12.0 - : 14.0; - - final showTimeStamp = (!isThreadMessage || _isThreadConversation) && - !hasReplies && - (hasTimeDiff || !isNextUserSame); - - final showUsername = !isMyMessage && - (!isThreadMessage || _isThreadConversation) && - !hasReplies && - (hasTimeDiff || !isNextUserSame); - - final showMarkUnread = streamChannel?.channel.config?.readEvents == true && - !isMyMessage && - (!isThreadMessage || _isThreadConversation); - - final showUserAvatar = isMyMessage - ? DisplayWidget.gone - : (hasTimeDiff || !isNextUserSame) - ? DisplayWidget.show - : DisplayWidget.hide; - - final showSendingIndicator = - isMyMessage && (index == 0 || hasTimeDiff || !isNextUserSame); - - final showInChannelIndicator = !_isThreadConversation && isThreadMessage; - final showThreadReplyIndicator = !_isThreadConversation && hasReplies; - final isOnlyEmoji = message.text?.isOnlyEmoji ?? false; - - final borderSide = isOnlyEmoji ? BorderSide.none : null; - - final currentUser = StreamChat.of(context).currentUser; - final members = StreamChannel.of(context).channel.state?.members ?? []; - final currentUserMember = - members.firstWhereOrNull((e) => e.user!.id == currentUser!.id); - - Widget messageWidget = StreamMessageWidget( - message: message, - reverse: isMyMessage, - showReactions: !message.isDeleted, - padding: const EdgeInsets.symmetric(horizontal: 8), - showInChannelIndicator: showInChannelIndicator, - showThreadReplyIndicator: showThreadReplyIndicator, - showUsername: showUsername, - showTimestamp: showTimeStamp, - showSendingIndicator: showSendingIndicator, - showUserAvatar: showUserAvatar, - showMarkUnreadMessage: showMarkUnread, - onQuotedMessageTap: (quotedMessageId) async { - if (messages.map((e) => e.id).contains(quotedMessageId)) { - final index = messages.indexWhere((m) => m.id == quotedMessageId); - _scrollController?.scrollTo( - index: index + 2, // +2 to account for loader and footer - duration: const Duration(seconds: 1), - curve: Curves.easeInOut, - alignment: 0.1, - ); - } else { - await streamChannel! - .loadChannelAtMessage(quotedMessageId) - .then((_) async { - initialIndex = 21; // 19 + 2 | 19 is the index of the message - initialAlignment = 0.1; - }); - } - }, - showEditMessage: isMyMessage, - showDeleteMessage: isMyMessage, - showThreadReplyMessage: !isThreadMessage && - streamChannel?.channel.ownCapabilities - .contains(PermissionType.sendReply) == - true, - showFlagButton: !isMyMessage, - borderSide: borderSide, - onThreadTap: _onThreadTap, - attachmentShape: RoundedRectangleBorder( - side: BorderSide( - color: _streamTheme.colorTheme.borders, - strokeAlign: BorderSide.strokeAlignOutside, - ), - borderRadius: BorderRadius.only( - topLeft: Radius.circular(attachmentBorderRadius), - bottomLeft: isMyMessage - ? Radius.circular(attachmentBorderRadius) - : Radius.circular( - (hasTimeDiff || !isNextUserSame) && - !(hasReplies || - isThreadMessage || - hasFileAttachment || - hasVoiceRecordingAttachment) - ? 0 - : attachmentBorderRadius, - ), - topRight: Radius.circular(attachmentBorderRadius), - bottomRight: isMyMessage - ? Radius.circular( - (hasTimeDiff || !isNextUserSame) && - !(hasReplies || - isThreadMessage || - hasFileAttachment || - hasVoiceRecordingAttachment) - ? 0 - : attachmentBorderRadius, - ) - : Radius.circular(attachmentBorderRadius), - ), - ), - attachmentPadding: EdgeInsets.all( - hasUrlAttachment - ? 8 - : hasFileAttachment || hasVoiceRecordingAttachment - ? 4 - : 2, - ), - borderRadiusGeometry: BorderRadius.only( - topLeft: const Radius.circular(16), - bottomLeft: isMyMessage - ? const Radius.circular(16) - : Radius.circular( - (hasTimeDiff || !isNextUserSame) && - !(hasReplies || isThreadMessage) - ? 0 - : 16, - ), - topRight: const Radius.circular(16), - bottomRight: isMyMessage - ? Radius.circular( - (hasTimeDiff || !isNextUserSame) && - !(hasReplies || isThreadMessage) - ? 0 - : 16, - ) - : const Radius.circular(16), - ), - textPadding: EdgeInsets.symmetric( - vertical: 8, - horizontal: isOnlyEmoji ? 0 : 16.0, - ), - messageTheme: isMyMessage - ? _streamTheme.ownMessageTheme - : _streamTheme.otherMessageTheme, - onMessageTap: (message) { - widget.onMessageTap?.call(message); - FocusScope.of(context).unfocus(); - }, - showPinButton: currentUserMember != null && - _userPermissions.contains(PermissionType.pinMessage), - ); - - if (widget.messageBuilder != null) { - messageWidget = widget.messageBuilder!( - context, - MessageDetails( - userId, - message, - messages, - index, - ), - messages, - messageWidget as StreamMessageWidget, - ); - } - - var child = messageWidget; - if (!initialMessageHighlightComplete && - widget.highlightInitialMessage && - isInitialMessage(message.id, streamChannel)) { - final colorTheme = _streamTheme.colorTheme; - final highlightColor = - widget.messageHighlightColor ?? colorTheme.highlight; - child = TweenAnimationBuilder( - tween: ColorTween( - begin: highlightColor, - // ignore: deprecated_member_use - end: colorTheme.barsBg.withOpacity(0), - ), - duration: const Duration(seconds: 3), - onEnd: () => initialMessageHighlightComplete = true, - builder: (_, color, child) => ColoredBox( - color: color!, - child: child, - ), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 4), - child: child, - ), - ); - } - - return child; - } - - Message? _lastFullyVisibleMessage; - void _handleItemPositionsChanged() { - final itemPositions = _itemPositionListener.itemPositions.value.toList(); - if (itemPositions.isEmpty) return; - - final isLastItemFullyVisible = isElementAtIndexVisible( - itemPositions, - fullyVisible: true, - // Index of the last item in the list view when reversed is 2 as 1 is the - // progress indicator and 0 is the footer. Similarly, when not reversed, - // the index of the last item is messages.length - 1. - index: widget.reverse ? 2 : messages.length - 1, - ); - - if (isLastItemFullyVisible) { - // We are using the first message as the last fully visible message - // because the messages are reversed in the list view. - final newLastFullyVisibleMessage = messages.firstOrNull; - final lastFullyVisibleMessageChanged = switch (_lastFullyVisibleMessage) { - final message? => message.id != newLastFullyVisibleMessage?.id, - null => true, // Allows setting the initial value. - }; - - // If the last fully visible message has changed, update the value and - // mark the messages as read. - if (lastFullyVisibleMessageChanged) { - _lastFullyVisibleMessage = newLastFullyVisibleMessage; - - if (streamChannel?.channel case final channel?) { - final allowMarkRead = channel.config?.readEvents == true; - final canMarkReadAtBottom = widget.markReadWhenAtTheBottom; - - // Only mark read if it is allowed and channel is upToDate. - if (_upToDate && allowMarkRead && canMarkReadAtBottom) { - // Mark regular message as read. - debouncedMarkRead?.call(); - - // Mark thread message as read. - if (widget.parentMessage case final parent?) { - debouncedMarkThreadRead?.call([parent.id]); - } - } - } - } - } - - if (mounted) _showScrollToBottom.value = !isLastItemFullyVisible; - } - - void _getOnThreadTap() { - if (widget.onThreadTap != null) { - _onThreadTap = (Message message) { - final threadBuilder = widget.threadBuilder; - widget.onThreadTap!( - message, - threadBuilder != null ? threadBuilder(context, message) : null, - ); - }; - } else if (widget.threadBuilder != null) { - _onThreadTap = (Message message) { - Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => BetterStreamBuilder( - stream: streamChannel!.channel.state!.messagesStream.map( - (messages) => messages.firstWhere((m) => m.id == message.id), - ), - initialData: message, - builder: (_, data) => StreamChannel( - channel: streamChannel!.channel, - child: widget.threadBuilder!(context, data), - ), - ), - ), - ); - }; - } - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_list_view/mlv_utils.dart b/packages/stream_chat_flutter/lib/src/message_list_view/mlv_utils.dart deleted file mode 100644 index c65a6ffdad..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_list_view/mlv_utils.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Determines at which point in the [MessageListView] the initial index should -/// be. -int getInitialIndex( - int? initialScrollIndex, - StreamChannelState channelState, - bool Function(Message)? messageFilter, - Read? read, -) { - if (initialScrollIndex != null) { - return initialScrollIndex; - } - - final messages = channelState.channel.state!.messages - .where(messageFilter ?? - defaultMessageFilter( - channelState.channel.client.state.currentUser!.id, - )) - .toList(growable: false); - - if (channelState.initialMessageId != null) { - final totalMessages = messages.length; - final messageIndex = - messages.indexWhere((e) => e.id == channelState.initialMessageId); - final index = totalMessages - messageIndex; - if (index != 0) return index + 1; - return index; - } - - if (read != null) { - final oldestUnreadMessage = messages.firstWhereOrNull( - (it) => - it.user?.id != channelState.channel.client.state.currentUser?.id && - it.createdAt.compareTo(read.lastRead) > 0, - ); - - if (oldestUnreadMessage != null) { - final oldestUnreadMessageIndex = messages.indexOf(oldestUnreadMessage); - final index = messages.length - oldestUnreadMessageIndex; - return index + 1; - } - } - - return 0; -} - -/// Gets the index of the top element in the viewport. -int? getTopElementIndex(Iterable values) { - final inView = values.where((position) => position.itemTrailingEdge > 0); - if (inView.isEmpty) return null; - - return inView.reduce((min, position) { - return position.itemTrailingEdge < min.itemTrailingEdge ? position : min; - }).index; -} - -/// Gets the index of the bottom element in the viewport. -int? getBottomElementIndex(Iterable values) { - final inView = values.where((position) => position.itemLeadingEdge < 1); - if (inView.isEmpty) return null; - - return inView.reduce((max, position) { - return position.itemLeadingEdge > max.itemLeadingEdge ? position : max; - }).index; -} - -/// Returns true if the element at [index] is at least partially visible in the -/// viewport. -/// -/// Optionally, pass [fullyVisible] as true to check if the element is fully -/// visible in the viewport. -bool isElementAtIndexVisible( - Iterable values, { - required int index, - bool fullyVisible = false, -}) { - final element = values.firstWhereOrNull((it) => it.index == index); - if (element == null) return false; - - if (fullyVisible) { - return element.itemLeadingEdge >= 0 && element.itemTrailingEdge <= 1; - } - - return element.itemTrailingEdge > 0 && element.itemLeadingEdge < 1; -} - -/// Returns true if the message is the initial message. -bool isInitialMessage(String id, StreamChannelState? channelState) { - return channelState!.initialMessageId == id; -} diff --git a/packages/stream_chat_flutter/lib/src/message_list_view/thread_separator.dart b/packages/stream_chat_flutter/lib/src/message_list_view/thread_separator.dart deleted file mode 100644 index 186c4f75a6..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_list_view/thread_separator.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template threadSeparator} -/// A widget that separates messages in a thread. Not intended for use outside -/// of [StreamMessageWidget]. -/// {@endtemplate} -class ThreadSeparator extends StatelessWidget { - ///{@macro threadSeparator} - const ThreadSeparator({ - super.key, - this.parentMessage, - }); - - // ignore: public_member_api_docs - final Message? parentMessage; - - @override - Widget build(BuildContext context) { - final replyCount = parentMessage!.replyCount!; - return DecoratedBox( - decoration: BoxDecoration( - gradient: StreamChatTheme.of(context).colorTheme.bgGradient, - ), - child: Padding( - padding: const EdgeInsets.all(8), - child: Text( - context.translations.threadSeparatorText(replyCount), - textAlign: TextAlign.center, - style: StreamChannelHeaderTheme.of(context).subtitleStyle, - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_list_view/unread_messages_separator.dart b/packages/stream_chat_flutter/lib/src/message_list_view/unread_messages_separator.dart deleted file mode 100644 index b32c36d8eb..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_list_view/unread_messages_separator.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template unreadMessagesSeparator} -/// {@endtemplate} -class UnreadMessagesSeparator extends StatelessWidget { - /// {@macro unreadMessagesSeparator} - const UnreadMessagesSeparator({ - super.key, - required this.unreadCount, - }); - - /// Number of unread messages. - final int unreadCount; - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: DecoratedBox( - decoration: BoxDecoration( - gradient: StreamChatTheme.of(context).colorTheme.bgGradient, - ), - child: Padding( - padding: const EdgeInsets.all(8), - child: Text( - context.translations.unreadMessagesSeparatorText(), - textAlign: TextAlign.center, - style: StreamChannelHeaderTheme.of(context).subtitleStyle, - ), - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/bottom_row.dart b/packages/stream_chat_flutter/lib/src/message_widget/bottom_row.dart deleted file mode 100644 index f05f08fe6e..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/bottom_row.dart +++ /dev/null @@ -1,290 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/message_widget/sending_indicator_builder.dart'; -import 'package:stream_chat_flutter/src/message_widget/thread_painter.dart'; -import 'package:stream_chat_flutter/src/message_widget/thread_participants.dart'; -import 'package:stream_chat_flutter/src/message_widget/username.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template bottomRow} -/// The bottom row of a [StreamMessageWidget]. -/// -/// Used in [MessageWidgetContent]. Should not be used elsewhere. -/// {@endtemplate} -class BottomRow extends StatelessWidget { - /// {@macro bottomRow} - const BottomRow({ - super.key, - required this.isDeleted, - required this.message, - required this.showThreadReplyIndicator, - required this.showInChannel, - required this.showTimeStamp, - required this.showUsername, - required this.showEditedLabel, - required this.reverse, - required this.showSendingIndicator, - required this.hasUrlAttachments, - required this.isGiphy, - required this.isOnlyEmoji, - required this.messageTheme, - required this.streamChatTheme, - required this.hasNonUrlAttachments, - required this.streamChat, - this.deletedBottomRowBuilder, - this.onThreadTap, - this.usernameBuilder, - this.sendingIndicatorBuilder, - }); - - /// {@macro messageIsDeleted} - final bool isDeleted; - - /// {@macro deletedBottomRowBuilder} - final Widget Function(BuildContext, Message)? deletedBottomRowBuilder; - - /// {@macro message} - final Message message; - - /// {@macro showThreadReplyIndicator} - final bool showThreadReplyIndicator; - - /// {@macro showInChannelIndicator} - final bool showInChannel; - - /// {@macro showTimestamp} - final bool showTimeStamp; - - /// {@macro showUsername} - final bool showUsername; - - /// {@macro showEdited} - final bool showEditedLabel; - - /// {@macro reverse} - final bool reverse; - - /// {@macro showSendingIndicator} - final bool showSendingIndicator; - - /// {@macro hasUrlAttachments} - final bool hasUrlAttachments; - - /// {@macro isGiphy} - final bool isGiphy; - - /// {@macro isOnlyEmoji} - final bool isOnlyEmoji; - - /// {@macro hasNonUrlAttachments} - final bool hasNonUrlAttachments; - - /// {@macro messageTheme} - final StreamMessageThemeData messageTheme; - - /// {@macro onThreadTap} - final void Function(Message)? onThreadTap; - - /// {@macro streamChatThemeData} - final StreamChatThemeData streamChatTheme; - - /// {@macro streamChat} - final StreamChatState streamChat; - - /// {@macro usernameBuilder} - final Widget Function(BuildContext, Message)? usernameBuilder; - - /// {@macro sendingIndicatorBuilder} - final Widget Function(BuildContext, Message)? sendingIndicatorBuilder; - - /// {@template copyWith} - /// Creates a copy of [BottomRow] with specified attributes - /// overridden. - /// {@endtemplate} - BottomRow copyWith({ - Key? key, - bool? isDeleted, - Message? message, - bool? showThreadReplyIndicator, - bool? showInChannel, - bool? showTimeStamp, - bool? showUsername, - bool? showEditedLabel, - bool? reverse, - bool? showSendingIndicator, - bool? hasUrlAttachments, - bool? isGiphy, - bool? isOnlyEmoji, - StreamMessageThemeData? messageTheme, - StreamChatThemeData? streamChatTheme, - bool? hasNonUrlAttachments, - StreamChatState? streamChat, - Widget Function(BuildContext, Message)? deletedBottomRowBuilder, - void Function(Message)? onThreadTap, - Widget Function(BuildContext, Message)? usernameBuilder, - Widget Function(BuildContext, Message)? sendingIndicatorBuilder, - }) => - BottomRow( - key: key ?? this.key, - isDeleted: isDeleted ?? this.isDeleted, - message: message ?? this.message, - showThreadReplyIndicator: - showThreadReplyIndicator ?? this.showThreadReplyIndicator, - showInChannel: showInChannel ?? this.showInChannel, - showTimeStamp: showTimeStamp ?? this.showTimeStamp, - showUsername: showUsername ?? this.showUsername, - showEditedLabel: showEditedLabel ?? this.showEditedLabel, - reverse: reverse ?? this.reverse, - showSendingIndicator: showSendingIndicator ?? this.showSendingIndicator, - hasUrlAttachments: hasUrlAttachments ?? this.hasUrlAttachments, - isGiphy: isGiphy ?? this.isGiphy, - isOnlyEmoji: isOnlyEmoji ?? this.isOnlyEmoji, - messageTheme: messageTheme ?? this.messageTheme, - streamChatTheme: streamChatTheme ?? this.streamChatTheme, - hasNonUrlAttachments: hasNonUrlAttachments ?? this.hasNonUrlAttachments, - streamChat: streamChat ?? this.streamChat, - deletedBottomRowBuilder: - deletedBottomRowBuilder ?? this.deletedBottomRowBuilder, - onThreadTap: onThreadTap ?? this.onThreadTap, - usernameBuilder: usernameBuilder ?? this.usernameBuilder, - sendingIndicatorBuilder: - sendingIndicatorBuilder ?? this.sendingIndicatorBuilder, - ); - - @override - Widget build(BuildContext context) { - if (isDeleted) { - final deletedBottomRowBuilder = this.deletedBottomRowBuilder; - if (deletedBottomRowBuilder != null) { - return deletedBottomRowBuilder(context, message); - } - } - - final threadParticipants = message.threadParticipants?.take(2); - final showThreadParticipants = threadParticipants?.isNotEmpty == true; - final replyCount = message.replyCount; - final isEdited = message.messageTextUpdatedAt != null; - - var msg = context.translations.threadReplyLabel; - if (showThreadReplyIndicator && replyCount! > 1) { - msg = context.translations.threadReplyCountText(replyCount); - } - - Future _onThreadTap() async { - try { - var message = this.message; - if (showInChannel) { - final channel = StreamChannel.of(context); - message = await channel.getMessage(message.parentId!); - } - return onThreadTap?.call(message); - } catch (e, stk) { - debugPrint('Error while fetching message: $e, $stk'); - } - } - - const usernameKey = Key('username'); - - final children = [ - if (showUsername) - usernameBuilder?.call(context, message) ?? - Username( - key: usernameKey, - message: message, - messageTheme: messageTheme, - ), - if (showEditedLabel && isEdited) - Text( - context.translations.editedMessageLabel, - style: messageTheme.createdAtStyle, - ), - if (showTimeStamp) - Text( - Jiffy.parseFromDateTime(message.createdAt.toLocal()).jm, - style: messageTheme.createdAtStyle, - ), - if (showSendingIndicator) - sendingIndicatorBuilder?.call(context, message) ?? - SendingIndicatorBuilder( - messageTheme: messageTheme, - message: message, - hasNonUrlAttachments: hasNonUrlAttachments, - streamChat: streamChat, - streamChatTheme: streamChatTheme, - ), - ]; - - final showThreadTail = - (showThreadReplyIndicator || showInChannel) && !isOnlyEmoji; - - final threadIndicatorWidgets = [ - if (showThreadTail) - // Added builder to use the nearest context to get the right - // textScaleFactor value. - Builder( - builder: (context) { - return Padding( - padding: EdgeInsets.only( - bottom: context.textScaleFactor * - ((messageTheme.repliesStyle?.fontSize ?? 1) / 2), - ), - child: CustomPaint( - size: const Size(16, 32) * context.textScaleFactor, - painter: ThreadReplyPainter( - context: context, - color: messageTheme.messageBorderColor, - reverse: reverse, - ), - ), - ); - }, - ), - if (showInChannel || showThreadReplyIndicator) ...[ - if (showThreadParticipants) - SizedBox.fromSize( - size: Size((threadParticipants!.length * 8.0) + 8, 16), - child: ThreadParticipants( - threadParticipants: threadParticipants, - streamChatTheme: streamChatTheme, - ), - ), - MouseRegion( - cursor: SystemMouseCursors.click, - child: GestureDetector( - onTap: _onThreadTap, - child: Text(msg, style: messageTheme.repliesStyle), - ), - ), - ], - ]; - - if (reverse) { - children.addAll(threadIndicatorWidgets.reversed); - } else { - children.insertAll(0, threadIndicatorWidgets); - } - - return Text.rich( - TextSpan( - children: [ - ...children.insertBetween(const SizedBox(width: 8)).map((child) { - final mediaQueryData = MediaQuery.of(context); - return WidgetSpan( - child: MediaQuery( - // Hardcoding the textScaleFactor to 1 to avoid the multiple - // resizing of the text. This is needed because the - // textScaleFactor is already applied to the textSpan. - // - // issue: https://github.com/GetStream/stream-chat-flutter/issues/1250 - // ignore: deprecated_member_use - data: mediaQueryData.copyWith(textScaleFactor: 1), - child: child, - ), - ); - }), - ], - ), - maxLines: 1, - textAlign: reverse ? TextAlign.right : TextAlign.left, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/deleted_message.dart b/packages/stream_chat_flutter/lib/src/message_widget/deleted_message.dart deleted file mode 100644 index ca82b5b834..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/deleted_message.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamDeletedMessage} -/// Displays that a message was deleted at this position in the message list. -/// {@endtemplate} -class StreamDeletedMessage extends StatelessWidget { - /// {@macro streamDeletedMessage} - const StreamDeletedMessage({ - super.key, - required this.messageTheme, - this.borderRadiusGeometry, - this.shape, - this.borderSide, - this.reverse = false, - }); - - /// The theme of the message - final StreamMessageThemeData messageTheme; - - /// The border radius of the message text - final BorderRadiusGeometry? borderRadiusGeometry; - - /// The shape of the message text - final ShapeBorder? shape; - - /// The [BorderSide] of the message text - final BorderSide? borderSide; - - /// If true the widget will be mirrored - final bool reverse; - - @override - Widget build(BuildContext context) { - return Material( - color: messageTheme.messageBackgroundColor, - shape: shape ?? - RoundedRectangleBorder( - borderRadius: borderRadiusGeometry ?? BorderRadius.zero, - side: borderSide ?? - BorderSide( - color: messageTheme.messageBorderColor ?? Colors.transparent, - ), - ), - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 16, - ), - child: Text( - context.translations.messageDeletedLabel, - style: messageTheme.messageTextStyle?.copyWith( - fontStyle: FontStyle.italic, - color: messageTheme.createdAtStyle?.color, - ), - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/ephemeral_message.dart b/packages/stream_chat_flutter/lib/src/message_widget/ephemeral_message.dart deleted file mode 100644 index 9c76a4fa0b..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/ephemeral_message.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/message_widget/giphy_ephemeral_message.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// {@template streamEphemeralMessage} -/// Shows an ephemeral message in a [MessageWidget]. -/// {@endtemplate} -class StreamEphemeralMessage extends StatelessWidget { - /// {@macro streamEphemeralMessage} - const StreamEphemeralMessage({ - super.key, - required this.message, - }); - - /// The underlying [Message] object which this widget represents. - final Message message; - - @override - Widget build(BuildContext context) { - final streamChannel = StreamChannel.of(context); - - // If the message is a giphy command, we will show the giphy ephemeral - // message instead. - final isGiphy = message.command == 'giphy'; - if (isGiphy) { - return GiphyEphemeralMessage( - message: message, - onActionPressed: (name, value) { - streamChannel.channel.sendAction( - message, - {name: value}, - ); - }, - ); - } - - // Assert if the message is not handled. - assert(true, 'Ephemeral message not handled, Please add a handler'); - - // Show nothing if we don't know how to handle the message. - return const SizedBox.shrink(); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/giphy_ephemeral_message.dart b/packages/stream_chat_flutter/lib/src/message_widget/giphy_ephemeral_message.dart deleted file mode 100644 index 44535d6bc9..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/giphy_ephemeral_message.dart +++ /dev/null @@ -1,233 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:jiffy/jiffy.dart'; -import 'package:stream_chat_flutter/src/attachment/thumbnail/giphy_attachment_thumbnail.dart'; -import 'package:stream_chat_flutter/src/icons/stream_svg_icon.dart'; -import 'package:stream_chat_flutter/src/misc/visible_footnote.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter/src/utils/extensions.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// Signature for the action callback passed to [GiphyEphemeralMessage]. -/// -/// Used by [GiphyEphemeralMessage.onActionPressed]. -typedef GiffyAction = void Function(String name, String value); - -/// {@template giphyEphemeralMessage} -/// Shows an ephemeral message of type giphy in a [MessageWidget]. -/// {@endtemplate} -class GiphyEphemeralMessage extends StatelessWidget { - /// {@macro giphyEphemeralMessage} - const GiphyEphemeralMessage({ - super.key, - required this.message, - this.onActionPressed, - }); - - /// The underlying [Message] object which this widget represents. - final Message message; - - /// Callback called when an action is pressed. - final GiffyAction? onActionPressed; - - @override - Widget build(BuildContext context) { - final giphy = message.attachments.first; - - final actions = giphy.actions; - assert(actions != null && actions.isNotEmpty, 'actions cannot be null'); - - final chatTheme = StreamChatTheme.of(context); - final textTheme = chatTheme.textTheme; - final colorTheme = chatTheme.colorTheme; - - final divider = Divider(thickness: 1, height: 0, color: colorTheme.borders); - - return Padding( - padding: const EdgeInsets.all(8), - child: Align( - alignment: Alignment.centerRight, - child: SizedBox( - width: 304, - height: 343, - child: Column( - children: [ - Expanded( - child: Card( - elevation: 2, - color: colorTheme.barsBg, - margin: EdgeInsets.zero, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topRight: Radius.circular(16), - topLeft: Radius.circular(16), - bottomLeft: Radius.circular(16), - ), - ), - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(8), - child: GiphyHeader(title: giphy.title), - ), - divider, - Expanded( - child: Padding( - padding: const EdgeInsets.all(2), - child: ClipRRect( - borderRadius: BorderRadius.circular(2), - child: StreamGiphyAttachmentThumbnail( - giphy: giphy, - width: double.infinity, - height: double.infinity, - ), - ), - ), - ), - divider, - SizedBox( - height: 48, - child: Padding( - padding: const EdgeInsets.all(2), - child: GiphyActions( - giphy: giphy, - onActionPressed: onActionPressed, - ), - ), - ), - ], - ), - ), - ), - const SizedBox(height: 4), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - const StreamVisibleFootnote(), - const SizedBox(width: 4), - Text( - Jiffy.parseFromDateTime(message.createdAt.toLocal()).jm, - style: textTheme.footnote.copyWith( - color: colorTheme.textLowEmphasis, - ), - ), - ], - ), - ], - ), - ), - ), - ); - } -} - -/// {@template giphyActions} -/// Shows the actions for a giphy ephemeral message. -/// {@endtemplate} -class GiphyActions extends StatelessWidget { - /// {@macro giphyActions} - const GiphyActions({ - super.key, - required this.giphy, - required this.onActionPressed, - }); - - /// The underlying [Attachment] object which this widget represents. - final Attachment giphy; - - /// Callback called when an action is pressed. - final GiffyAction? onActionPressed; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - final textTheme = theme.textTheme; - final colorTheme = theme.colorTheme; - - return Row( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Expanded( - child: TextButton( - onPressed: () { - onActionPressed?.call('image_action', 'cancel'); - }, - child: Text( - context.translations.cancelLabel.capitalize(), - style: textTheme.bodyBold.copyWith( - color: colorTheme.textLowEmphasis, - ), - ), - ), - ), - VerticalDivider(thickness: 1, width: 4, color: colorTheme.borders), - Expanded( - child: TextButton( - onPressed: () { - onActionPressed?.call('image_action', 'shuffle'); - }, - child: Text( - context.translations.shuffleLabel.capitalize(), - style: textTheme.bodyBold.copyWith( - color: colorTheme.textLowEmphasis, - ), - ), - ), - ), - VerticalDivider(thickness: 1, width: 4, color: colorTheme.borders), - Expanded( - child: TextButton( - onPressed: () { - onActionPressed?.call('image_action', 'send'); - }, - child: Text( - context.translations.sendLabel.capitalize(), - style: textTheme.bodyBold.copyWith( - color: colorTheme.accentPrimary, - ), - ), - ), - ), - ], - ); - } -} - -/// {@template giphyHeader} -/// Shows the header for a giphy ephemeral message. -/// {@endtemplate} -class GiphyHeader extends StatelessWidget { - /// {@macro giphyHeader} - const GiphyHeader({super.key, this.title}); - - /// The title of the giphy. - final String? title; - - @override - Widget build(BuildContext context) { - final colorTheme = StreamChatTheme.of(context).colorTheme; - return Row( - children: [ - const StreamSvgIcon(icon: StreamSvgIcons.giphy), - const SizedBox(width: 8), - Text( - context.translations.giphyLabel, - style: const TextStyle(fontWeight: FontWeight.bold), - ), - const SizedBox(width: 8), - if (title != null) - Expanded( - child: Text( - title!, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - // ignore: deprecated_member_use - color: colorTheme.textHighEmphasis.withOpacity(0.5), - ), - ), - ), - ], - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/message_card.dart b/packages/stream_chat_flutter/lib/src/message_widget/message_card.dart deleted file mode 100644 index a5687b67b1..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/message_card.dart +++ /dev/null @@ -1,255 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template messageCard} -/// The widget containing a quoted message. -/// -/// Used in [MessageWidgetContent]. Should not be used elsewhere. -/// {@endtemplate} -class MessageCard extends StatefulWidget { - /// {@macro messageCard} - const MessageCard({ - super.key, - required this.message, - required this.isFailedState, - required this.showUserAvatar, - required this.messageTheme, - required this.hasQuotedMessage, - required this.hasUrlAttachments, - required this.hasNonUrlAttachments, - required this.hasPoll, - required this.isOnlyEmoji, - required this.isGiphy, - required this.attachmentBuilders, - required this.attachmentPadding, - required this.attachmentShape, - required this.onAttachmentTap, - required this.onShowMessage, - required this.onReplyTap, - required this.attachmentActionsModalBuilder, - required this.textPadding, - required this.reverse, - this.shape, - this.borderSide, - this.borderRadiusGeometry, - this.textBuilder, - this.quotedMessageBuilder, - this.onLinkTap, - this.onMentionTap, - this.onQuotedMessageTap, - }); - - /// {@macro isFailedState} - final bool isFailedState; - - /// {@macro showUserAvatar} - final DisplayWidget showUserAvatar; - - /// {@macro shape} - final ShapeBorder? shape; - - /// {@macro borderSide} - final BorderSide? borderSide; - - /// {@macro messageTheme} - final StreamMessageThemeData messageTheme; - - /// {@macro borderRadiusGeometry} - final BorderRadiusGeometry? borderRadiusGeometry; - - /// {@macro hasQuotedMessage} - final bool hasQuotedMessage; - - /// {@macro hasUrlAttachments} - final bool hasUrlAttachments; - - /// {@macro hasNonUrlAttachments} - final bool hasNonUrlAttachments; - - /// {@macro hasPoll} - final bool hasPoll; - - /// {@macro isOnlyEmoji} - final bool isOnlyEmoji; - - /// {@macro isGiphy} - final bool isGiphy; - - /// {@macro message} - final Message message; - - /// {@macro attachmentBuilders} - final List? attachmentBuilders; - - /// {@macro attachmentPadding} - final EdgeInsetsGeometry attachmentPadding; - - /// {@macro attachmentShape} - final ShapeBorder? attachmentShape; - - /// {@macro onAttachmentTap} - final StreamAttachmentWidgetTapCallback? onAttachmentTap; - - /// {@macro onShowMessage} - final ShowMessageCallback? onShowMessage; - - /// {@macro onReplyTap} - final void Function(Message)? onReplyTap; - - /// {@macro attachmentActionsBuilder} - final AttachmentActionsBuilder? attachmentActionsModalBuilder; - - /// {@macro textPadding} - final EdgeInsets textPadding; - - /// {@macro textBuilder} - final Widget Function(BuildContext, Message)? textBuilder; - - /// {@macro quotedMessageBuilder} - final Widget Function(BuildContext, Message)? quotedMessageBuilder; - - /// {@macro onLinkTap} - final void Function(String)? onLinkTap; - - /// {@macro onMentionTap} - final void Function(User)? onMentionTap; - - /// {@macro onQuotedMessageTap} - final OnQuotedMessageTap? onQuotedMessageTap; - - /// {@macro reverse} - final bool reverse; - - @override - State createState() => _MessageCardState(); -} - -class _MessageCardState extends State { - final attachmentsKey = GlobalKey(); - double? widthLimit; - - bool get hasAttachments { - return widget.hasUrlAttachments || widget.hasNonUrlAttachments; - } - - void _updateWidthLimit() { - final attachmentContext = attachmentsKey.currentContext; - final renderBox = attachmentContext?.findRenderObject() as RenderBox?; - final attachmentsWidth = renderBox?.size.width; - - if (attachmentsWidth == null || attachmentsWidth == 0) return; - - if (mounted) { - setState(() => widthLimit = attachmentsWidth); - } - } - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - // If there is an attachment, we need to wait for the attachment to be - // rendered to get the width of the attachment and set it as the width - // limit of the message card. - if (hasAttachments) { - WidgetsBinding.instance.addPostFrameCallback((_) { - _updateWidthLimit(); - }); - } - } - - @override - Widget build(BuildContext context) { - final onQuotedMessageTap = widget.onQuotedMessageTap; - final quotedMessageBuilder = widget.quotedMessageBuilder; - - return Container( - constraints: const BoxConstraints().copyWith(maxWidth: widthLimit), - margin: EdgeInsets.symmetric( - horizontal: (widget.isFailedState ? 15.0 : 0.0) + - (widget.showUserAvatar == DisplayWidget.gone ? 0 : 4.0), - ), - clipBehavior: Clip.hardEdge, - decoration: ShapeDecoration( - color: _getBackgroundColor(), - shape: widget.shape ?? - RoundedRectangleBorder( - side: widget.borderSide ?? - BorderSide( - color: widget.messageTheme.messageBorderColor ?? - Colors.transparent, - ), - borderRadius: widget.borderRadiusGeometry ?? BorderRadius.zero, - ), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (widget.hasQuotedMessage) - InkWell( - onTap: !widget.message.quotedMessage!.isDeleted && - onQuotedMessageTap != null - ? () => onQuotedMessageTap(widget.message.quotedMessageId) - : null, - child: quotedMessageBuilder?.call( - context, - widget.message.quotedMessage!, - ) ?? - QuotedMessage( - message: widget.message, - textBuilder: widget.textBuilder, - hasNonUrlAttachments: widget.hasNonUrlAttachments, - ), - ), - if (hasAttachments) - ParseAttachments( - key: attachmentsKey, - message: widget.message, - attachmentBuilders: widget.attachmentBuilders, - attachmentPadding: widget.attachmentPadding, - attachmentShape: widget.attachmentShape, - onAttachmentTap: widget.onAttachmentTap, - onShowMessage: widget.onShowMessage, - onReplyTap: widget.onReplyTap, - attachmentActionsModalBuilder: - widget.attachmentActionsModalBuilder, - ), - if (widget.hasPoll) - PollMessage( - message: widget.message, - ), - TextBubble( - messageTheme: widget.messageTheme, - message: widget.message, - textPadding: widget.textPadding, - textBuilder: widget.textBuilder, - isOnlyEmoji: widget.isOnlyEmoji, - hasQuotedMessage: widget.hasQuotedMessage, - hasUrlAttachments: widget.hasUrlAttachments, - onLinkTap: widget.onLinkTap, - onMentionTap: widget.onMentionTap, - ), - ], - ), - ); - } - - Color? _getBackgroundColor() { - if (widget.hasQuotedMessage) { - return widget.messageTheme.messageBackgroundColor; - } - - final containsOnlyUrlAttachment = - widget.hasUrlAttachments && !widget.hasNonUrlAttachments; - - if (containsOnlyUrlAttachment) { - return widget.messageTheme.urlAttachmentBackgroundColor; - } - - if (widget.isOnlyEmoji) { - return Colors.transparent; - } - - return widget.messageTheme.messageBackgroundColor; - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/message_text.dart b/packages/stream_chat_flutter/lib/src/message_widget/message_text.dart deleted file mode 100644 index a092f34f71..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/message_text.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:collection/collection.dart' show IterableExtension; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamMessageText} -/// The text content of a message. -/// {@endtemplate} -class StreamMessageText extends StatelessWidget { - /// {@macro streamMessageText} - const StreamMessageText({ - super.key, - required this.message, - required this.messageTheme, - this.onMentionTap, - this.onLinkTap, - }); - - /// Message whose text is to be displayed - final Message message; - - /// The action to perform when a mention is tapped - final void Function(User)? onMentionTap; - - /// The action to perform when a link is tapped - final void Function(String)? onLinkTap; - - /// [StreamMessageThemeData] whose text theme is to be applied - final StreamMessageThemeData messageTheme; - - @override - Widget build(BuildContext context) { - final streamChat = StreamChat.of(context); - assert(streamChat.currentUser != null, ''); - return BetterStreamBuilder( - stream: streamChat.currentUserStream.map((it) => it!.language ?? 'en'), - initialData: streamChat.currentUser!.language ?? 'en', - builder: (context, language) { - final messageText = message - .translate(language) - .replaceMentions() - .text - ?.replaceAll('\n', '\n\n') - .trim(); - - return StreamMarkdownMessage( - data: messageText ?? '', - messageTheme: messageTheme, - selectable: isDesktopDeviceOrWeb, - onTapLink: ( - String link, - String? href, - String title, - ) { - if (link.startsWith('@')) { - final mentionedUser = message.mentionedUsers.firstWhereOrNull( - (u) => '@${u.name}' == link, - ); - - if (mentionedUser == null) return; - - onMentionTap?.call(mentionedUser); - } else { - if (onLinkTap != null) { - onLinkTap!(link); - } else { - launchURL(context, link); - } - } - }, - ); - }, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart b/packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart deleted file mode 100644 index 44c003a065..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart +++ /dev/null @@ -1,1132 +0,0 @@ -import 'package:contextmenu/contextmenu.dart'; -import 'package:flutter/material.dart' hide ButtonStyle; -import 'package:flutter/services.dart'; -import 'package:flutter_portal/flutter_portal.dart'; -import 'package:stream_chat_flutter/conditional_parent_builder/conditional_parent_builder.dart'; -import 'package:stream_chat_flutter/platform_widget_builder/platform_widget_builder.dart'; -import 'package:stream_chat_flutter/src/context_menu_items/context_menu_reaction_picker.dart'; -import 'package:stream_chat_flutter/src/context_menu_items/stream_chat_context_menu_item.dart'; -import 'package:stream_chat_flutter/src/dialogs/dialogs.dart'; -import 'package:stream_chat_flutter/src/message_actions_modal/message_actions_modal.dart'; -import 'package:stream_chat_flutter/src/message_widget/message_widget_content.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// The display behaviour of a widget -enum DisplayWidget { - /// Hides the widget replacing its space with a spacer - hide, - - /// Hides the widget not replacing its space - gone, - - /// Shows the widget normally - show, -} - -/// {@template messageWidget} -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/message_widget.png) -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/message_widget_paint.png) -/// -/// Shows a message with reactions, replies and user avatar. -/// -/// Usually you don't use this widget as it's the default message widget used by -/// [MessageListView]. -/// -/// The widget components render the ui based on the first ancestor of type -/// [StreamChatTheme]. -/// Modify it to change the widget appearance. -/// {@endtemplate} -class StreamMessageWidget extends StatefulWidget { - /// {@macro messageWidget} - const StreamMessageWidget({ - super.key, - required this.message, - required this.messageTheme, - this.reverse = false, - this.translateUserAvatar = true, - this.shape, - this.borderSide, - this.borderRadiusGeometry, - this.attachmentShape, - this.onMentionTap, - this.onMessageTap, - this.onReactionsTap, - this.onReactionsHover, - this.showReactionPicker = true, - this.showReactionTail, - this.showUserAvatar = DisplayWidget.show, - this.showSendingIndicator = true, - this.showThreadReplyIndicator = false, - this.showInChannelIndicator = false, - this.onReplyTap, - this.onThreadTap, - this.onConfirmDeleteTap, - this.showUsername = true, - this.showTimestamp = true, - this.showEditedLabel = true, - this.showReactions = true, - this.showDeleteMessage = true, - this.showEditMessage = true, - this.showReplyMessage = true, - this.showThreadReplyMessage = true, - this.showMarkUnreadMessage = true, - this.showResendMessage = true, - this.showCopyMessage = true, - this.showFlagButton = true, - this.showPinButton = true, - this.showPinHighlight = true, - this.onUserAvatarTap, - this.onLinkTap, - this.onMessageActions, - this.onShowMessage, - this.userAvatarBuilder, - this.quotedMessageBuilder, - this.editMessageInputBuilder, - this.textBuilder, - this.bottomRowBuilderWithDefaultWidget, - this.attachmentBuilders, - this.padding, - this.textPadding = const EdgeInsets.symmetric( - horizontal: 16, - vertical: 8, - ), - this.attachmentPadding = EdgeInsets.zero, - this.widthFactor = 0.78, - this.onQuotedMessageTap, - this.customActions = const [], - this.onAttachmentTap, - this.imageAttachmentThumbnailSize = const Size(400, 400), - this.imageAttachmentThumbnailResizeType = 'clip', - this.imageAttachmentThumbnailCropType = 'center', - this.attachmentActionsModalBuilder, - }); - - /// {@template onMentionTap} - /// Function called on mention tap - /// {@endtemplate} - final void Function(User)? onMentionTap; - - /// {@template onThreadTap} - /// The function called when tapping on threads - /// {@endtemplate} - final void Function(Message)? onThreadTap; - - /// {@template onReplyTap} - /// The function called when tapping on replies - /// {@endtemplate} - final void Function(Message)? onReplyTap; - - /// {@template onDeleteTap} - /// The function called when delete confirmation button is tapped. - /// {@endtemplate} - final Future Function(Message)? onConfirmDeleteTap; - - /// {@template editMessageInputBuilder} - /// Widget builder for edit message layout - /// {@endtemplate} - final Widget Function(BuildContext, Message)? editMessageInputBuilder; - - /// {@template textBuilder} - /// Widget builder for building text - /// {@endtemplate} - final Widget Function(BuildContext, Message)? textBuilder; - - /// {@template onMessageActions} - /// Function called on long press - /// {@endtemplate} - final void Function(BuildContext, Message)? onMessageActions; - - /// {@template bottomRowBuilderWithDefaultWidget} - /// Widget builder for building a bottom row below the message. - /// Also contains the default bottom row widget. - /// {@endtemplate} - final BottomRowBuilderWithDefaultWidget? bottomRowBuilderWithDefaultWidget; - - /// {@template userAvatarBuilder} - /// Widget builder for building user avatar - /// {@endtemplate} - final Widget Function(BuildContext, User)? userAvatarBuilder; - - /// {@template quotedMessageBuilder} - /// Widget builder for building quoted message - /// {@endtemplate} - final Widget Function(BuildContext, Message)? quotedMessageBuilder; - - /// {@template message} - /// The message to display. - /// {@endtemplate} - final Message message; - - /// {@template messageTheme} - /// The message theme - /// {@endtemplate} - final StreamMessageThemeData messageTheme; - - /// {@template reverse} - /// If true the widget will be mirrored - /// {@endtemplate} - final bool reverse; - - /// {@template shape} - /// The shape of the message text - /// {@endtemplate} - final ShapeBorder? shape; - - /// {@template attachmentShape} - /// The shape of an attachment - /// {@endtemplate} - final ShapeBorder? attachmentShape; - - /// {@template borderSide} - /// The borderSide of the message text - /// {@endtemplate} - final BorderSide? borderSide; - - /// {@template borderRadiusGeometry} - /// The border radius of the message text - /// {@endtemplate} - final BorderRadiusGeometry? borderRadiusGeometry; - - /// {@template padding} - /// The padding of the widget - /// {@endtemplate} - final EdgeInsetsGeometry? padding; - - /// {@template textPadding} - /// The internal padding of the message text - /// {@endtemplate} - final EdgeInsets textPadding; - - /// {@template attachmentPadding} - /// The internal padding of an attachment - /// {@endtemplate} - final EdgeInsetsGeometry attachmentPadding; - - /// {@template widthFactor} - /// The percentage of the available width the message content should take - /// {@endtemplate} - final double widthFactor; - - /// {@template showUserAvatar} - /// It controls the display behaviour of the user avatar - /// {@endtemplate} - final DisplayWidget showUserAvatar; - - /// {@template showSendingIndicator} - /// It controls the display behaviour of the sending indicator - /// {@endtemplate} - final bool showSendingIndicator; - - /// {@template showReactions} - /// If `true` the message's reactions will be shown. - /// {@endtemplate} - final bool showReactions; - - /// {@template showThreadReplyIndicator} - /// If true the widget will show the thread reply indicator - /// {@endtemplate} - final bool showThreadReplyIndicator; - - /// {@template showInChannelIndicator} - /// If true the widget will show the show in channel indicator - /// {@endtemplate} - final bool showInChannelIndicator; - - /// {@template onUserAvatarTap} - /// The function called when tapping on UserAvatar - /// {@endtemplate} - final void Function(User)? onUserAvatarTap; - - /// {@template onLinkTap} - /// The function called when tapping on a link - /// {@endtemplate} - final void Function(String)? onLinkTap; - - /// {@template showReactionPicker} - /// Whether or not to show the reaction picker. - /// Used in [StreamMessageReactionsModal] and [MessageActionsModal]. - /// {@endtemplate} - final bool showReactionPicker; - - /// {@template showReactionPickerTail} - /// Whether or not to show the reaction picker tail. - /// This is calculated internally in most cases and does not need to be set. - /// {@endtemplate} - final bool? showReactionTail; - - /// {@template onShowMessage} - /// Callback when show message is tapped - /// {@endtemplate} - final ShowMessageCallback? onShowMessage; - - /// {@template showUsername} - /// If true show the users username next to the timestamp of the message - /// {@endtemplate} - final bool showUsername; - - /// {@template showTimestamp} - /// Show message timestamp - /// {@endtemplate} - final bool showTimestamp; - - /// {@template showTimestamp} - /// Show edited label if message is edited - /// {@endtemplate} - final bool showEditedLabel; - - /// {@template showReplyMessage} - /// Show reply action - /// {@endtemplate} - final bool showReplyMessage; - - /// {@template showThreadReplyMessage} - /// Show thread reply action - /// {@endtemplate} - final bool showThreadReplyMessage; - - /// {@template showMarkUnreadMessage} - /// Show mark unread action - /// {@endtemplate} - final bool showMarkUnreadMessage; - - /// {@template showEditMessage} - /// Show edit action - /// {@endtemplate} - final bool showEditMessage; - - /// {@template showCopyMessage} - /// Show copy action - /// {@endtemplate} - final bool showCopyMessage; - - /// {@template showDeleteMessage} - /// Show delete action - /// {@endtemplate} - final bool showDeleteMessage; - - /// {@template showResendMessage} - /// Show resend action - /// {@endtemplate} - final bool showResendMessage; - - /// {@template showFlagButton} - /// Show flag action - /// {@endtemplate} - final bool showFlagButton; - - /// {@template showPinButton} - /// Show pin action - /// {@endtemplate} - final bool showPinButton; - - /// {@template showPinHighlight} - /// Display Pin Highlight - /// {@endtemplate} - final bool showPinHighlight; - - /// {@template attachmentBuilders} - /// List of attachment builders for rendering attachment widgets pre-defined - /// and custom attachment types. - /// - /// If null, the widget will create a default list of attachment builders - /// based on the [Attachment.type] of the attachment. - /// {@endtemplate} - final List? attachmentBuilders; - - /// {@template translateUserAvatar} - /// Center user avatar with bottom of the message - /// {@endtemplate} - final bool translateUserAvatar; - - /// {@macro onQuotedMessageTap} - final OnQuotedMessageTap? onQuotedMessageTap; - - /// {@macro onMessageTap} - final void Function(Message)? onMessageTap; - - /// {@macro onReactionsTap} - /// - /// Note: Only used in mobile devices (iOS and Android). Do not confuse this - /// with the tap action on the reactions picker. - final OnReactionsTap? onReactionsTap; - - /// {@macro onReactionsHover} - /// - /// Note: Only used in desktop devices (web and desktop). - final OnReactionsHover? onReactionsHover; - - /// {@template customActions} - /// List of custom actions shown on message long tap - /// {@endtemplate} - final List customActions; - - /// {@macro onMessageWidgetAttachmentTap} - final StreamAttachmentWidgetTapCallback? onAttachmentTap; - - /// {@macro attachmentActionsBuilder} - final AttachmentActionsBuilder? attachmentActionsModalBuilder; - - /// Size of the image attachment thumbnail. - final Size imageAttachmentThumbnailSize; - - /// Resize type of the image attachment thumbnail. - /// - /// Defaults to [crop] - final String /*clip|crop|scale|fill*/ imageAttachmentThumbnailResizeType; - - /// Crop type of the image attachment thumbnail. - /// - /// Defaults to [center] - final String /*center|top|bottom|left|right*/ - imageAttachmentThumbnailCropType; - - /// {@template copyWith} - /// Creates a copy of [StreamMessageWidget] with specified attributes - /// overridden. - /// {@endtemplate} - StreamMessageWidget copyWith({ - Key? key, - void Function(User)? onMentionTap, - void Function(Message)? onThreadTap, - void Function(Message)? onReplyTap, - Future Function(Message)? onConfirmDeleteTap, - Widget Function(BuildContext, Message)? editMessageInputBuilder, - Widget Function(BuildContext, Message)? textBuilder, - Widget Function(BuildContext, Message)? quotedMessageBuilder, - BottomRowBuilderWithDefaultWidget? bottomRowBuilderWithDefaultWidget, - void Function(BuildContext, Message)? onMessageActions, - Message? message, - StreamMessageThemeData? messageTheme, - bool? reverse, - ShapeBorder? shape, - ShapeBorder? attachmentShape, - BorderSide? borderSide, - BorderRadiusGeometry? borderRadiusGeometry, - EdgeInsetsGeometry? padding, - EdgeInsets? textPadding, - EdgeInsetsGeometry? attachmentPadding, - double? widthFactor, - DisplayWidget? showUserAvatar, - bool? showSendingIndicator, - bool? showReactions, - bool? allRead, - bool? showThreadReplyIndicator, - bool? showInChannelIndicator, - void Function(User)? onUserAvatarTap, - void Function(String)? onLinkTap, - bool? showReactionBrowser, - bool? showReactionPicker, - bool? showReactionTail, - List? readList, - ShowMessageCallback? onShowMessage, - bool? showUsername, - bool? showTimestamp, - bool? showEditedLabel, - bool? showReplyMessage, - bool? showThreadReplyMessage, - bool? showEditMessage, - bool? showCopyMessage, - bool? showDeleteMessage, - bool? showResendMessage, - bool? showFlagButton, - bool? showPinButton, - bool? showPinHighlight, - bool? showMarkUnreadMessage, - List? attachmentBuilders, - bool? translateUserAvatar, - OnQuotedMessageTap? onQuotedMessageTap, - void Function(Message)? onMessageTap, - OnReactionsTap? onReactionsTap, - OnReactionsHover? onReactionsHover, - List? customActions, - void Function(Message message, Attachment attachment)? onAttachmentTap, - Widget Function(BuildContext, User)? userAvatarBuilder, - Size? imageAttachmentThumbnailSize, - String? imageAttachmentThumbnailResizeType, - String? imageAttachmentThumbnailCropType, - AttachmentActionsBuilder? attachmentActionsModalBuilder, - }) { - return StreamMessageWidget( - key: key ?? this.key, - onMentionTap: onMentionTap ?? this.onMentionTap, - onThreadTap: onThreadTap ?? this.onThreadTap, - onReplyTap: onReplyTap ?? this.onReplyTap, - onConfirmDeleteTap: onConfirmDeleteTap ?? this.onConfirmDeleteTap, - editMessageInputBuilder: - editMessageInputBuilder ?? this.editMessageInputBuilder, - textBuilder: textBuilder ?? this.textBuilder, - quotedMessageBuilder: quotedMessageBuilder ?? this.quotedMessageBuilder, - bottomRowBuilderWithDefaultWidget: bottomRowBuilderWithDefaultWidget ?? - this.bottomRowBuilderWithDefaultWidget, - onMessageActions: onMessageActions ?? this.onMessageActions, - message: message ?? this.message, - messageTheme: messageTheme ?? this.messageTheme, - reverse: reverse ?? this.reverse, - shape: shape ?? this.shape, - attachmentShape: attachmentShape ?? this.attachmentShape, - borderSide: borderSide ?? this.borderSide, - borderRadiusGeometry: borderRadiusGeometry ?? this.borderRadiusGeometry, - padding: padding ?? this.padding, - textPadding: textPadding ?? this.textPadding, - attachmentPadding: attachmentPadding ?? this.attachmentPadding, - widthFactor: widthFactor ?? this.widthFactor, - showUserAvatar: showUserAvatar ?? this.showUserAvatar, - showSendingIndicator: showSendingIndicator ?? this.showSendingIndicator, - showEditedLabel: showEditedLabel ?? this.showEditedLabel, - showReactions: showReactions ?? this.showReactions, - showThreadReplyIndicator: - showThreadReplyIndicator ?? this.showThreadReplyIndicator, - showInChannelIndicator: - showInChannelIndicator ?? this.showInChannelIndicator, - onUserAvatarTap: onUserAvatarTap ?? this.onUserAvatarTap, - onLinkTap: onLinkTap ?? this.onLinkTap, - showReactionPicker: showReactionPicker ?? this.showReactionPicker, - showReactionTail: showReactionTail ?? this.showReactionTail, - onShowMessage: onShowMessage ?? this.onShowMessage, - showUsername: showUsername ?? this.showUsername, - showTimestamp: showTimestamp ?? this.showTimestamp, - showReplyMessage: showReplyMessage ?? this.showReplyMessage, - showThreadReplyMessage: - showThreadReplyMessage ?? this.showThreadReplyMessage, - showEditMessage: showEditMessage ?? this.showEditMessage, - showCopyMessage: showCopyMessage ?? this.showCopyMessage, - showDeleteMessage: showDeleteMessage ?? this.showDeleteMessage, - showResendMessage: showResendMessage ?? this.showResendMessage, - showFlagButton: showFlagButton ?? this.showFlagButton, - showPinButton: showPinButton ?? this.showPinButton, - showPinHighlight: showPinHighlight ?? this.showPinHighlight, - showMarkUnreadMessage: - showMarkUnreadMessage ?? this.showMarkUnreadMessage, - attachmentBuilders: attachmentBuilders ?? this.attachmentBuilders, - translateUserAvatar: translateUserAvatar ?? this.translateUserAvatar, - onQuotedMessageTap: onQuotedMessageTap ?? this.onQuotedMessageTap, - onMessageTap: onMessageTap ?? this.onMessageTap, - onReactionsTap: onReactionsTap ?? this.onReactionsTap, - onReactionsHover: onReactionsHover ?? this.onReactionsHover, - customActions: customActions ?? this.customActions, - onAttachmentTap: onAttachmentTap ?? this.onAttachmentTap, - userAvatarBuilder: userAvatarBuilder ?? this.userAvatarBuilder, - imageAttachmentThumbnailSize: - imageAttachmentThumbnailSize ?? this.imageAttachmentThumbnailSize, - imageAttachmentThumbnailResizeType: imageAttachmentThumbnailResizeType ?? - this.imageAttachmentThumbnailResizeType, - imageAttachmentThumbnailCropType: imageAttachmentThumbnailCropType ?? - this.imageAttachmentThumbnailCropType, - attachmentActionsModalBuilder: - attachmentActionsModalBuilder ?? this.attachmentActionsModalBuilder, - ); - } - - @override - _StreamMessageWidgetState createState() => _StreamMessageWidgetState(); -} - -class _StreamMessageWidgetState extends State - with AutomaticKeepAliveClientMixin { - bool get showThreadReplyIndicator => widget.showThreadReplyIndicator; - - bool get showSendingIndicator => widget.showSendingIndicator; - - bool get isDeleted => widget.message.isDeleted; - - bool get showUsername => widget.showUsername; - - bool get showTimeStamp => widget.showTimestamp; - - bool get showEditedLabel => widget.showEditedLabel; - - bool get isTextEdited => widget.message.messageTextUpdatedAt != null; - - bool get showInChannel => widget.showInChannelIndicator; - - /// {@template hasQuotedMessage} - /// `true` if [StreamMessageWidget.quotedMessage] is not null. - /// {@endtemplate} - bool get hasQuotedMessage => widget.message.quotedMessage != null; - - bool get isSendFailed => widget.message.state.isSendingFailed; - - bool get isUpdateFailed => widget.message.state.isUpdatingFailed; - - bool get isDeleteFailed => widget.message.state.isDeletingFailed; - - /// {@template isFailedState} - /// Whether the message has failed to be sent, updated, or deleted. - /// {@endtemplate} - bool get isFailedState => isSendFailed || isUpdateFailed || isDeleteFailed; - - /// {@template isGiphy} - /// `true` if any of the [message]'s attachments are a giphy. - /// {@endtemplate} - bool get isGiphy => widget.message.attachments - .any((element) => element.type == AttachmentType.giphy); - - /// {@template isOnlyEmoji} - /// `true` if [message.text] contains only emoji. - /// {@endtemplate} - bool get isOnlyEmoji => widget.message.text?.isOnlyEmoji == true; - - /// {@template hasNonUrlAttachments} - /// `true` if any of the [message]'s attachments are a giphy and do not - /// have a [Attachment.titleLink]. - /// {@endtemplate} - bool get hasNonUrlAttachments => widget.message.attachments - .any((it) => it.type != AttachmentType.urlPreview); - - /// {@template hasPoll} - /// `true` if the [message] contains a poll. - /// {@endtemplate} - bool get hasPoll => widget.message.poll != null; - - /// {@template hasUrlAttachments} - /// `true` if any of the [message]'s attachments are a giphy with a - /// [Attachment.titleLink]. - /// {@endtemplate} - bool get hasUrlAttachments => widget.message.attachments - .any((it) => it.type == AttachmentType.urlPreview); - - /// {@template showBottomRow} - /// Show the [BottomRow] widget if any of the following are `true`: - /// * [StreamMessageWidget.showThreadReplyIndicator] - /// * [StreamMessageWidget.showUsername] - /// * [StreamMessageWidget.showTimestamp] - /// * [StreamMessageWidget.showInChannelIndicator] - /// * [StreamMessageWidget.showSendingIndicator] - /// * [StreamMessageWidget.message.isDeleted] - /// {@endtemplate} - bool get showBottomRow => - showThreadReplyIndicator || - showUsername || - showTimeStamp || - showInChannel || - showSendingIndicator || - isTextEdited; - - /// {@template isPinned} - /// Whether [StreamMessageWidget.message] is pinned or not. - /// {@endtemplate} - bool get isPinned => widget.message.pinned; - - /// {@template shouldShowReactions} - /// Should show message reactions if [StreamMessageWidget.showReactions] is - /// `true`, if there are reactions to show, and if the message is not deleted. - /// {@endtemplate} - bool get shouldShowReactions => - widget.showReactions && - (widget.message.reactionCounts?.isNotEmpty == true) && - !widget.message.isDeleted; - - bool get shouldShowReplyAction => - widget.showReplyMessage && !isFailedState && widget.onReplyTap != null; - - bool get shouldShowEditAction => - widget.showEditMessage && - !isDeleteFailed && - !hasPoll && - !widget.message.attachments - .any((element) => element.type == AttachmentType.giphy); - - bool get shouldShowResendAction => - widget.showResendMessage && (isSendFailed || isUpdateFailed); - - bool get shouldShowCopyAction => - widget.showCopyMessage && - !isFailedState && - widget.message.text?.trim().isNotEmpty == true; - - bool get shouldShowThreadReplyAction => - widget.showThreadReplyMessage && - !isFailedState && - widget.onThreadTap != null; - - bool get shouldShowDeleteAction => widget.showDeleteMessage || isDeleteFailed; - - @override - bool get wantKeepAlive => widget.message.attachments.isNotEmpty; - - late StreamChatThemeData _streamChatTheme; - late StreamChatState _streamChat; - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - _streamChatTheme = StreamChatTheme.of(context); - _streamChat = StreamChat.of(context); - } - - @override - Widget build(BuildContext context) { - super.build(context); - final avatarWidth = - widget.messageTheme.avatarTheme?.constraints.maxWidth ?? 40; - final bottomRowPadding = - widget.showUserAvatar != DisplayWidget.gone ? avatarWidth + 8.5 : 0.5; - - final showReactions = shouldShowReactions; - - return ConditionalParentBuilder( - builder: (context, child) { - if (!widget.message.state.isDeleted) { - return ContextMenuArea( - verticalPadding: 0, - builder: (_) => _buildContextMenu(), - child: child, - ); - } - - return child; - }, - child: Material( - type: MaterialType.transparency, - child: AnimatedContainer( - duration: const Duration(seconds: 1), - color: widget.message.pinned && widget.showPinHighlight - ? _streamChatTheme.colorTheme.highlight - // ignore: deprecated_member_use - : _streamChatTheme.colorTheme.barsBg.withOpacity(0), - child: Portal( - child: PlatformWidgetBuilder( - mobile: (context, child) { - return InkWell( - onTap: () => widget.onMessageTap!(widget.message), - onLongPress: widget.message.state.isDeleted - ? null - : () => onLongPress(context), - child: child, - ); - }, - desktop: (_, child) => MouseRegion(child: child), - web: (_, child) => MouseRegion(child: child), - child: Padding( - padding: widget.padding ?? const EdgeInsets.all(8), - child: FractionallySizedBox( - alignment: widget.reverse - ? Alignment.centerRight - : Alignment.centerLeft, - widthFactor: widget.widthFactor, - child: Builder(builder: (context) { - return MessageWidgetContent( - streamChatTheme: _streamChatTheme, - showUsername: showUsername, - showTimeStamp: showTimeStamp, - showEditedLabel: showEditedLabel, - showThreadReplyIndicator: showThreadReplyIndicator, - showSendingIndicator: showSendingIndicator, - showInChannel: showInChannel, - isGiphy: isGiphy, - isOnlyEmoji: isOnlyEmoji, - hasUrlAttachments: hasUrlAttachments, - messageTheme: widget.messageTheme, - reverse: widget.reverse, - message: widget.message, - hasNonUrlAttachments: hasNonUrlAttachments, - hasPoll: hasPoll, - hasQuotedMessage: hasQuotedMessage, - textPadding: widget.textPadding, - attachmentBuilders: widget.attachmentBuilders, - attachmentPadding: widget.attachmentPadding, - attachmentShape: widget.attachmentShape, - onAttachmentTap: widget.onAttachmentTap, - onReplyTap: widget.onReplyTap, - onThreadTap: widget.onThreadTap, - onShowMessage: widget.onShowMessage, - attachmentActionsModalBuilder: - widget.attachmentActionsModalBuilder, - avatarWidth: avatarWidth, - bottomRowPadding: bottomRowPadding, - isFailedState: isFailedState, - isPinned: isPinned, - messageWidget: widget, - showBottomRow: showBottomRow, - showPinHighlight: widget.showPinHighlight, - showReactionPickerTail: calculateReactionTailEnabled( - ReactionTailType.list, - ), - showReactions: showReactions, - onReactionsTap: () { - widget.onReactionsTap != null - ? widget.onReactionsTap!(widget.message) - : _showMessageReactionsModal(context); - }, - onReactionsHover: widget.onReactionsHover, - showUserAvatar: widget.showUserAvatar, - streamChat: _streamChat, - translateUserAvatar: widget.translateUserAvatar, - shape: widget.shape, - borderSide: widget.borderSide, - borderRadiusGeometry: widget.borderRadiusGeometry, - textBuilder: widget.textBuilder, - quotedMessageBuilder: widget.quotedMessageBuilder, - onLinkTap: widget.onLinkTap, - onMentionTap: widget.onMentionTap, - onQuotedMessageTap: widget.onQuotedMessageTap, - bottomRowBuilderWithDefaultWidget: - widget.bottomRowBuilderWithDefaultWidget, - onUserAvatarTap: widget.onUserAvatarTap, - userAvatarBuilder: widget.userAvatarBuilder, - ); - }), - ), - ), - ), - ), - ), - ), - ); - } - - List _buildContextMenu() { - final channel = StreamChannel.of(context).channel; - - return [ - if (widget.showReactionPicker) - StreamChatContextMenuItem( - child: StreamChannel( - channel: channel, - child: ContextMenuReactionPicker( - message: widget.message, - ), - ), - ), - if (shouldShowReplyAction) ...[ - StreamChatContextMenuItem( - leading: const StreamSvgIcon(icon: StreamSvgIcons.reply), - title: Text(context.translations.replyLabel), - onClick: () { - Navigator.of(context, rootNavigator: true).pop(); - widget.onReplyTap!(widget.message); - }, - ), - ], - if (widget.showMarkUnreadMessage) - StreamChatContextMenuItem( - leading: const StreamSvgIcon(icon: StreamSvgIcons.messageUnread), - title: Text(context.translations.markAsUnreadLabel), - onClick: () async { - try { - await channel.markUnread(widget.message.id); - } catch (ex) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - context.translations.markUnreadError, - ), - ), - ); - } - - Navigator.of(context, rootNavigator: true).pop(); - }, - ), - if (shouldShowThreadReplyAction) - StreamChatContextMenuItem( - leading: const StreamSvgIcon(icon: StreamSvgIcons.threadReply), - title: Text(context.translations.threadReplyLabel), - onClick: () { - Navigator.of(context, rootNavigator: true).pop(); - widget.onThreadTap!(widget.message); - }, - ), - if (shouldShowCopyAction) - StreamChatContextMenuItem( - leading: const StreamSvgIcon(icon: StreamSvgIcons.copy), - title: Text(context.translations.copyMessageLabel), - onClick: () { - Navigator.of(context, rootNavigator: true).pop(); - var messageToCopy = widget.message.text; - for (final user in widget.message.mentionedUsers.toSet()) { - final userId = user.id; - final userName = user.name; - messageToCopy = messageToCopy?.replaceAll( - RegExp('@($userId|$userName)'), - '@$userName', - ) ?? - ''; - } - - if (messageToCopy != null) { - Clipboard.setData( - ClipboardData(text: messageToCopy), - ); - } - }, - ), - if (shouldShowEditAction) ...[ - StreamChatContextMenuItem( - leading: const StreamSvgIcon( - color: Colors.grey, - icon: StreamSvgIcons.edit, - ), - title: Text(context.translations.editMessageLabel), - onClick: () { - Navigator.of(context, rootNavigator: true).pop(); - showModalBottomSheet( - context: context, - elevation: 2, - clipBehavior: Clip.hardEdge, - isScrollControlled: true, - backgroundColor: - StreamMessageInputTheme.of(context).inputBackgroundColor, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16), - ), - ), - builder: (_) => EditMessageSheet( - message: widget.message, - channel: StreamChannel.of(context).channel, - editMessageInputBuilder: widget.editMessageInputBuilder, - ), - ); - }, - ), - ], - if (widget.showPinButton) - StreamChatContextMenuItem( - leading: const StreamSvgIcon( - size: 24, - color: Colors.grey, - icon: StreamSvgIcons.pin, - ), - title: Text( - context.translations.togglePinUnpinText( - pinned: widget.message.pinned, - ), - ), - onClick: () async { - Navigator.of(context, rootNavigator: true).pop(); - try { - if (!widget.message.pinned) { - await channel.pinMessage(widget.message); - } else { - await channel.unpinMessage(widget.message); - } - } catch (e) { - throw Exception(e); - } - }, - ), - if (shouldShowResendAction) - StreamChatContextMenuItem( - leading: const StreamSvgIcon(icon: StreamSvgIcons.sendMessage), - title: Text( - context.translations.toggleResendOrResendEditedMessage( - isUpdateFailed: widget.message.state.isUpdatingFailed, - ), - ), - onClick: () { - Navigator.of(context, rootNavigator: true).pop(); - final isUpdateFailed = widget.message.state.isUpdatingFailed; - final channel = StreamChannel.of(context).channel; - if (isUpdateFailed) { - channel.updateMessage(widget.message); - } else { - channel.sendMessage(widget.message); - } - }, - ), - if (shouldShowDeleteAction) - StreamChatContextMenuItem( - leading: const StreamSvgIcon( - color: Colors.red, - icon: StreamSvgIcons.delete, - ), - title: Text( - context.translations.deleteMessageLabel, - style: const TextStyle(color: Colors.red), - ), - onClick: () async { - Navigator.of(context, rootNavigator: true).pop(); - final deleted = await showDialog( - context: context, - barrierDismissible: false, - builder: (_) => const DeleteMessageDialog(), - ); - if (deleted == true) { - try { - final onConfirmDeleteTap = widget.onConfirmDeleteTap; - if (onConfirmDeleteTap != null) { - await onConfirmDeleteTap(widget.message); - } else { - await StreamChannel.of(context) - .channel - .deleteMessage(widget.message); - } - } catch (e) { - showDialog( - context: context, - builder: (_) => const MessageDialog(), - ); - } - } - }, - ), - ...widget.customActions.map( - (e) => StreamChatContextMenuItem( - leading: e.leading, - title: e.title, - onClick: () => e.onTap?.call(widget.message), - ), - ), - ]; - } - - void _showMessageReactionsModal(BuildContext context) { - final channel = StreamChannel.of(context).channel; - - showDialog( - useRootNavigator: false, - context: context, - useSafeArea: false, - barrierColor: _streamChatTheme.colorTheme.overlay, - builder: (context) => StreamChannel( - channel: channel, - child: StreamMessageReactionsModal( - showReactionPicker: widget.showReactionPicker, - messageWidget: widget.copyWith( - key: const Key('MessageWidget'), - message: widget.message.copyWith( - text: (widget.message.text?.length ?? 0) > 200 - ? '${widget.message.text!.substring(0, 200)}...' - : widget.message.text, - ), - showReactions: false, - showReactionTail: calculateReactionTailEnabled( - ReactionTailType.reactions, - ), - showUsername: false, - showTimestamp: false, - translateUserAvatar: false, - showSendingIndicator: false, - padding: EdgeInsets.zero, - showReactionPicker: widget.showReactionPicker, - showPinHighlight: false, - showUserAvatar: - widget.message.user!.id == channel.client.state.currentUser!.id - ? DisplayWidget.gone - : DisplayWidget.show, - ), - onUserAvatarTap: widget.onUserAvatarTap, - messageTheme: widget.messageTheme, - reverse: widget.reverse, - message: widget.message, - ), - ), - ); - } - - void onLongPress(BuildContext context) { - if (widget.message.isEphemeral || widget.message.state.isOutgoing) { - return; - } - - if (widget.onMessageActions != null) { - return widget.onMessageActions!(context, widget.message); - } - - return _showMessageActionModalBottomSheet(context); - } - - void _showMessageActionModalBottomSheet(BuildContext context) { - final channel = StreamChannel.of(context).channel; - - showDialog( - useRootNavigator: false, - context: context, - useSafeArea: false, - barrierColor: _streamChatTheme.colorTheme.overlay, - builder: (context) { - return StreamChannel( - channel: channel, - child: MessageActionsModal( - messageWidget: widget.copyWith( - key: const Key('MessageWidget'), - message: widget.message.copyWith( - text: (widget.message.text?.length ?? 0) > 200 - ? '${widget.message.text!.substring(0, 200)}...' - : widget.message.text, - ), - showReactions: false, - showReactionTail: calculateReactionTailEnabled( - ReactionTailType.messageActions, - ), - showUsername: false, - showTimestamp: false, - translateUserAvatar: false, - showSendingIndicator: false, - padding: EdgeInsets.zero, - showPinHighlight: false, - showUserAvatar: widget.message.user!.id == - channel.client.state.currentUser!.id - ? DisplayWidget.gone - : DisplayWidget.show, - ), - onCopyTap: (message) { - var messageToCopy = message.text; - for (final user in widget.message.mentionedUsers.toSet()) { - final userId = user.id; - final userName = user.name; - messageToCopy = messageToCopy?.replaceAll( - RegExp('@($userId|$userName)'), - '@$userName', - ) ?? - ''; - } - if (messageToCopy != null) { - Clipboard.setData( - ClipboardData(text: messageToCopy), - ); - } - }, - messageTheme: widget.messageTheme, - reverse: widget.reverse, - showDeleteMessage: shouldShowDeleteAction, - onConfirmDeleteTap: widget.onConfirmDeleteTap, - message: widget.message, - editMessageInputBuilder: widget.editMessageInputBuilder, - onReplyTap: widget.onReplyTap, - onThreadReplyTap: widget.onThreadTap, - showResendMessage: shouldShowResendAction, - showCopyMessage: shouldShowCopyAction, - showEditMessage: shouldShowEditAction, - showReactionPicker: widget.showReactionPicker, - showReplyMessage: shouldShowReplyAction, - showThreadReplyMessage: shouldShowThreadReplyAction, - showFlagButton: widget.showFlagButton, - showPinButton: widget.showPinButton, - showMarkUnreadMessage: widget.showMarkUnreadMessage, - customActions: widget.customActions, - ), - ); - }, - ); - } - - /// Calculates if the reaction picker tail should be enabled. - bool calculateReactionTailEnabled(ReactionTailType type) { - if (widget.showReactionTail != null) return widget.showReactionTail!; - - switch (type) { - case ReactionTailType.list: - return false; - case ReactionTailType.messageActions: - return widget.showReactionPicker; - case ReactionTailType.reactions: - return widget.showReactionPicker; - } - } -} - -/// Enum for declaring the location of the message for which the reaction picker -/// is to be enabled. -enum ReactionTailType { - /// Message is in the [StreamMessageListView] - list, - - /// Message is in the [MessageActionsModal] - messageActions, - - /// Message is in the message reactions modal - reactions, -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/message_widget_content.dart b/packages/stream_chat_flutter/lib/src/message_widget/message_widget_content.dart deleted file mode 100644 index 963b33f87e..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/message_widget_content.dart +++ /dev/null @@ -1,473 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_portal/flutter_portal.dart'; -import 'package:meta/meta.dart'; -import 'package:stream_chat_flutter/src/message_widget/reactions/desktop_reactions_builder.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Signature for the builder function that will be called when the message -/// bottom row is built. Includes the [Message]. -typedef BottomRowBuilder = Widget Function(BuildContext, Message); - -/// Signature for the builder function that will be called when the message -/// bottom row is built. Includes the [Message] and the default [BottomRow]. -typedef BottomRowBuilderWithDefaultWidget = Widget Function( - BuildContext, - Message, - BottomRow, -); - -/// {@template messageWidgetContent} -/// The main content of a [StreamMessageWidget]. -/// -/// Should not be used outside of [MessageWidget. -/// {@endtemplate} -@internal -class MessageWidgetContent extends StatelessWidget { - /// {@macro messageWidgetContent} - const MessageWidgetContent({ - super.key, - required this.reverse, - required this.isPinned, - required this.showPinHighlight, - required this.showBottomRow, - required this.message, - required this.showUserAvatar, - required this.avatarWidth, - required this.showReactions, - required this.onReactionsTap, - required this.onReactionsHover, - required this.messageTheme, - required this.streamChatTheme, - required this.isFailedState, - required this.hasQuotedMessage, - required this.hasUrlAttachments, - required this.hasNonUrlAttachments, - required this.hasPoll, - required this.isOnlyEmoji, - required this.isGiphy, - required this.attachmentBuilders, - required this.attachmentPadding, - required this.attachmentShape, - required this.onAttachmentTap, - required this.onShowMessage, - required this.onReplyTap, - required this.attachmentActionsModalBuilder, - required this.textPadding, - required this.showReactionPickerTail, - required this.translateUserAvatar, - required this.bottomRowPadding, - required this.showInChannel, - required this.streamChat, - required this.showSendingIndicator, - required this.showThreadReplyIndicator, - required this.showTimeStamp, - required this.showUsername, - required this.showEditedLabel, - required this.messageWidget, - required this.onThreadTap, - this.onUserAvatarTap, - this.borderRadiusGeometry, - this.borderSide, - this.shape, - this.onQuotedMessageTap, - this.onMentionTap, - this.onLinkTap, - this.textBuilder, - this.quotedMessageBuilder, - this.bottomRowBuilderWithDefaultWidget, - this.userAvatarBuilder, - }); - - /// {@macro reverse} - final bool reverse; - - /// {@macro isPinned} - final bool isPinned; - - /// {@macro showPinHighlight} - final bool showPinHighlight; - - /// {@macro showBottomRow} - final bool showBottomRow; - - /// {@macro message} - final Message message; - - /// {@macro showUserAvatar} - final DisplayWidget showUserAvatar; - - /// The width of the avatar. - final double avatarWidth; - - /// {@macro showReactions} - final bool showReactions; - - /// {@macro onReactionsTap} - final VoidCallback onReactionsTap; - - /// {@macro onReactionsHover} - final OnReactionsHover? onReactionsHover; - - /// {@macro messageTheme} - final StreamMessageThemeData messageTheme; - - /// {@macro onUserAvatarTap} - final void Function(User)? onUserAvatarTap; - - /// {@macro streamChatThemeData} - final StreamChatThemeData streamChatTheme; - - /// {@macro isFailedState} - final bool isFailedState; - - /// {@macro borderRadiusGeometry} - final BorderRadiusGeometry? borderRadiusGeometry; - - /// {@macro borderSide} - final BorderSide? borderSide; - - /// {@macro shape} - final ShapeBorder? shape; - - /// {@macro hasQuotedMessage} - final bool hasQuotedMessage; - - /// {@macro hasUrlAttachments} - final bool hasUrlAttachments; - - /// {@macro hasNonUrlAttachments} - final bool hasNonUrlAttachments; - - /// {@macro hasPoll} - final bool hasPoll; - - /// {@macro isOnlyEmoji} - final bool isOnlyEmoji; - - /// {@macro isGiphy} - final bool isGiphy; - - /// {@macro attachmentBuilders} - final List? attachmentBuilders; - - /// {@macro attachmentPadding} - final EdgeInsetsGeometry attachmentPadding; - - /// {@macro attachmentShape} - final ShapeBorder? attachmentShape; - - /// {@macro onAttachmentTap} - final StreamAttachmentWidgetTapCallback? onAttachmentTap; - - /// {@macro onShowMessage} - final ShowMessageCallback? onShowMessage; - - /// {@macro onReplyTap} - final void Function(Message)? onReplyTap; - - /// {@macro onThreadTap} - final void Function(Message)? onThreadTap; - - /// {@macro attachmentActionsBuilder} - final AttachmentActionsBuilder? attachmentActionsModalBuilder; - - /// {@macro textPadding} - final EdgeInsets textPadding; - - /// {@macro onQuotedMessageTap} - final OnQuotedMessageTap? onQuotedMessageTap; - - /// {@macro onMentionTap} - final void Function(User)? onMentionTap; - - /// {@macro onLinkTap} - final void Function(String)? onLinkTap; - - /// {@macro textBuilder} - final Widget Function(BuildContext, Message)? textBuilder; - - /// {@macro quotedMessageBuilder} - final Widget Function(BuildContext, Message)? quotedMessageBuilder; - - /// {@macro showReactionPickerTail} - final bool showReactionPickerTail; - - /// {@macro translateUserAvatar} - final bool translateUserAvatar; - - /// The padding to use for this widget. - final double bottomRowPadding; - - /// {@macro bottomRowBuilderWithDefaultWidget} - final BottomRowBuilderWithDefaultWidget? bottomRowBuilderWithDefaultWidget; - - /// {@macro showInChannelIndicator} - final bool showInChannel; - - /// {@macro streamChat} - final StreamChatState streamChat; - - /// {@macro showSendingIndicator} - final bool showSendingIndicator; - - /// {@macro showThreadReplyIndicator} - final bool showThreadReplyIndicator; - - /// {@macro showTimestamp} - final bool showTimeStamp; - - /// {@macro showUsername} - final bool showUsername; - - /// {@macro showEdited} - final bool showEditedLabel; - - /// {@macro messageWidget} - final StreamMessageWidget messageWidget; - - /// {@macro userAvatarBuilder} - final Widget Function(BuildContext, User)? userAvatarBuilder; - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: - reverse ? CrossAxisAlignment.end : CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Stack( - clipBehavior: Clip.none, - alignment: reverse - ? AlignmentDirectional.bottomEnd - : AlignmentDirectional.bottomStart, - children: [ - if (showBottomRow) - Padding( - padding: EdgeInsets.only( - left: !reverse ? bottomRowPadding : 0, - right: reverse ? bottomRowPadding : 0, - bottom: isPinned && showPinHighlight ? 6.0 : 0.0, - ), - child: _buildBottomRow(context), - ), - Padding( - padding: EdgeInsets.only( - bottom: isPinned && showPinHighlight ? 8.0 : 0.0, - ), - child: Column( - crossAxisAlignment: - reverse ? CrossAxisAlignment.end : CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - if (message.pinned && - message.pinnedBy != null && - showPinHighlight) - PinnedMessage( - pinnedBy: message.pinnedBy!, - currentUser: streamChat.currentUser!, - ), - Row( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisSize: MainAxisSize.min, - children: [ - if (!reverse && - showUserAvatar == DisplayWidget.show && - message.user != null) ...[ - UserAvatarTransform( - onUserAvatarTap: onUserAvatarTap, - userAvatarBuilder: userAvatarBuilder, - translateUserAvatar: translateUserAvatar, - messageTheme: messageTheme, - message: message, - ), - const SizedBox(width: 4), - ], - if (showUserAvatar == DisplayWidget.hide) - SizedBox(width: avatarWidth + 4), - Flexible( - child: PortalTarget( - visible: isMobileDevice && showReactions, - portalFollower: isMobileDevice && showReactions - ? ReactionIndicator( - message: message, - messageTheme: messageTheme, - ownId: streamChat.currentUser!.id, - reverse: reverse, - onTap: onReactionsTap, - ) - : null, - anchor: Aligned( - follower: Alignment( - reverse ? 1 : -1, - -1, - ), - target: Alignment( - reverse ? -1 : 1, - -1, - ), - ), - child: Stack( - clipBehavior: Clip.none, - children: [ - Padding( - padding: showReactions - ? const EdgeInsets.only(top: 18) - : EdgeInsets.zero, - child: (message.isDeleted && !isFailedState) - ? Container( - margin: EdgeInsets.symmetric( - horizontal: showUserAvatar == - DisplayWidget.gone - ? 0 - : 4.0, - ), - child: StreamDeletedMessage( - borderRadiusGeometry: - borderRadiusGeometry, - borderSide: borderSide, - shape: shape, - messageTheme: messageTheme, - ), - ) - : MessageCard( - message: message, - isFailedState: isFailedState, - showUserAvatar: showUserAvatar, - messageTheme: messageTheme, - hasQuotedMessage: hasQuotedMessage, - hasUrlAttachments: hasUrlAttachments, - hasNonUrlAttachments: - hasNonUrlAttachments, - hasPoll: hasPoll, - isOnlyEmoji: isOnlyEmoji, - isGiphy: isGiphy, - attachmentBuilders: attachmentBuilders, - attachmentPadding: attachmentPadding, - attachmentShape: attachmentShape, - onAttachmentTap: onAttachmentTap, - onReplyTap: onReplyTap, - onShowMessage: onShowMessage, - attachmentActionsModalBuilder: - attachmentActionsModalBuilder, - textPadding: textPadding, - reverse: reverse, - onQuotedMessageTap: onQuotedMessageTap, - onMentionTap: onMentionTap, - onLinkTap: onLinkTap, - textBuilder: textBuilder, - quotedMessageBuilder: - quotedMessageBuilder, - borderRadiusGeometry: - borderRadiusGeometry, - borderSide: borderSide, - shape: shape, - ), - ), - // TODO: Make tail part of the Reaction Picker. - if (showReactionPickerTail) - Positioned( - right: reverse ? null : 4, - left: reverse ? 4 : null, - top: -8, - child: CustomPaint( - painter: ReactionBubblePainter( - streamChatTheme.colorTheme.barsBg, - Colors.transparent, - Colors.transparent, - tailCirclesSpace: 1, - flipTail: !reverse, - ), - ), - ), - ], - ), - ), - ), - if (reverse && - showUserAvatar == DisplayWidget.show && - message.user != null) ...[ - UserAvatarTransform( - onUserAvatarTap: onUserAvatarTap, - userAvatarBuilder: userAvatarBuilder, - translateUserAvatar: translateUserAvatar, - messageTheme: messageTheme, - message: message, - ), - const SizedBox(width: 4), - ], - if (showUserAvatar == DisplayWidget.hide) - SizedBox(width: avatarWidth + 4), - ], - ), - if (isDesktopDeviceOrWeb && showReactions) ...[ - Padding( - padding: showUserAvatar != DisplayWidget.gone - ? EdgeInsets.only( - left: avatarWidth + 4, - right: avatarWidth + 4, - ) - : EdgeInsets.zero, - child: DesktopReactionsBuilder( - message: message, - messageTheme: messageTheme, - onHover: onReactionsHover, - borderSide: borderSide, - reverse: reverse, - ), - ), - ], - if (showBottomRow) - SizedBox( - height: context.textScaleFactor * 18.0, - ), - ], - ), - ), - if (isFailedState) - Positioned( - right: reverse ? 0 : null, - left: reverse ? null : 0, - bottom: showBottomRow ? 18 : -2, - child: const StreamSvgIcon( - size: 20, - icon: StreamSvgIcons.error, - ), - ), - ], - ), - ], - ); - } - - Widget _buildBottomRow(BuildContext context) { - final defaultWidget = BottomRow( - onThreadTap: onThreadTap, - message: message, - reverse: reverse, - messageTheme: messageTheme, - hasUrlAttachments: hasUrlAttachments, - isOnlyEmoji: isOnlyEmoji, - isDeleted: message.isDeleted, - isGiphy: isGiphy, - showInChannel: showInChannel, - showSendingIndicator: showSendingIndicator, - showThreadReplyIndicator: showThreadReplyIndicator, - showTimeStamp: showTimeStamp, - showUsername: showUsername, - showEditedLabel: showEditedLabel, - streamChatTheme: streamChatTheme, - streamChat: streamChat, - hasNonUrlAttachments: hasNonUrlAttachments, - ); - - if (bottomRowBuilderWithDefaultWidget != null) { - return bottomRowBuilderWithDefaultWidget!( - context, - message, - defaultWidget, - ); - } - - return defaultWidget; - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/message_widget_content_components.dart b/packages/stream_chat_flutter/lib/src/message_widget/message_widget_content_components.dart deleted file mode 100644 index ccbd8a0e8a..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/message_widget_content_components.dart +++ /dev/null @@ -1,9 +0,0 @@ -export 'bottom_row.dart'; -export 'message_card.dart'; -export 'parse_attachments.dart'; -export 'pinned_message.dart'; -export 'quoted_message.dart'; -export 'reactions/message_reactions_modal.dart'; -export 'reactions/reaction_bubble.dart'; -export 'reactions/reaction_indicator.dart'; -export 'user_avatar_transform.dart'; diff --git a/packages/stream_chat_flutter/lib/src/message_widget/parse_attachments.dart b/packages/stream_chat_flutter/lib/src/message_widget/parse_attachments.dart deleted file mode 100644 index b32d09426b..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/parse_attachments.dart +++ /dev/null @@ -1,139 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/attachment/attachment_widget_catalog.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template parseAttachments} -/// Parses the attachments of a [StreamMessageWidget]. -/// -/// Used in [MessageCard]. Should not be used elsewhere. -/// {@endtemplate} -class ParseAttachments extends StatelessWidget { - /// {@macro parseAttachments} - const ParseAttachments({ - super.key, - required this.message, - required this.attachmentBuilders, - required this.attachmentPadding, - this.attachmentShape, - this.onAttachmentTap, - this.onShowMessage, - this.onReplyTap, - this.attachmentActionsModalBuilder, - }); - - /// {@macro message} - final Message message; - - /// {@macro attachmentBuilders} - final List? attachmentBuilders; - - /// {@macro attachmentPadding} - final EdgeInsetsGeometry attachmentPadding; - - /// {@macro attachmentShape} - final ShapeBorder? attachmentShape; - - /// {@macro onAttachmentTap} - final StreamAttachmentWidgetTapCallback? onAttachmentTap; - - /// {@macro onShowMessage} - final ShowMessageCallback? onShowMessage; - - /// {@macro onReplyTap} - final void Function(Message)? onReplyTap; - - /// {@macro attachmentActionsBuilder} - final AttachmentActionsBuilder? attachmentActionsModalBuilder; - - @override - Widget build(BuildContext context) { - // Create a default onAttachmentTap callback if not provided. - var onAttachmentTap = this.onAttachmentTap; - onAttachmentTap ??= (message, attachment) { - // If the current attachment is a url preview attachment, open the url - // in the browser. - final isUrlPreview = attachment.type == AttachmentType.urlPreview; - if (isUrlPreview) { - final url = attachment.ogScrapeUrl ?? ''; - launchURL(context, url); - return; - } - - final isImage = attachment.type == AttachmentType.image; - final isVideo = attachment.type == AttachmentType.video; - final isGiphy = attachment.type == AttachmentType.giphy; - - // If the current attachment is a media attachment, open the media - // attachment in full screen. - final isMedia = isImage || isVideo || isGiphy; - if (isMedia) { - final channel = StreamChannel.of(context).channel; - - final attachments = message.toAttachmentPackage( - filter: (it) { - final isImage = it.type == AttachmentType.image; - final isVideo = it.type == AttachmentType.video; - final isGiphy = it.type == AttachmentType.giphy; - return isImage || isVideo || isGiphy; - }, - ); - - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) { - return StreamChannel( - channel: channel, - child: StreamFullScreenMediaBuilder( - userName: message.user!.name, - mediaAttachmentPackages: attachments, - startIndex: attachments.indexWhere( - (it) => it.attachment.id == attachment.id, - ), - onReplyMessage: onReplyTap, - onShowMessage: onShowMessage, - attachmentActionsModalBuilder: attachmentActionsModalBuilder, - ), - ); - }, - ), - ); - - return; - } - }; - - // Create a default attachmentBuilders list if not provided. - final builders = StreamAttachmentWidgetBuilder.defaultBuilders( - message: message, - shape: attachmentShape, - padding: attachmentPadding, - onAttachmentTap: onAttachmentTap, - customAttachmentBuilders: attachmentBuilders, - ); - - final catalog = AttachmentWidgetCatalog(builders: builders); - return catalog.build(context, message); - } -} - -extension on Message { - List toAttachmentPackage({ - bool Function(Attachment)? filter, - }) { - // Create a copy of the attachments list. - var attachments = [...this.attachments]; - if (filter != null) { - attachments = [...attachments.where(filter)]; - } - - // Create a list of StreamAttachmentPackage from the attachments list. - return [ - ...attachments.map((it) { - return StreamAttachmentPackage( - attachment: it, - message: this, - ); - }) - ]; - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/pinned_message.dart b/packages/stream_chat_flutter/lib/src/message_widget/pinned_message.dart deleted file mode 100644 index 49b9a4f219..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/pinned_message.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template pinnedMessage} -/// A pinned message in a chat. -/// -/// Used in [MessageWidgetContent]. Should not be used elsewhere. -/// {@endtemplate} -class PinnedMessage extends StatelessWidget { - /// {@macro pinnedMessage} - const PinnedMessage({ - super.key, - required this.pinnedBy, - required this.currentUser, - }); - - /// The [User] who pinned this message. - final User pinnedBy; - - /// The current [User]. - final User currentUser; - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 8), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const StreamSvgIcon( - size: 16, - icon: StreamSvgIcons.pin, - ), - const SizedBox( - width: 4, - ), - Text( - context.translations.pinnedByUserText( - pinnedBy: pinnedBy, - currentUser: currentUser, - ), - style: TextStyle( - color: StreamChatTheme.of(context).colorTheme.textLowEmphasis, - fontSize: 13, - fontWeight: FontWeight.w400, - ), - ), - ], - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/poll_message.dart b/packages/stream_chat_flutter/lib/src/message_widget/poll_message.dart deleted file mode 100644 index 0fb01c498c..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/poll_message.dart +++ /dev/null @@ -1,120 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/poll/interactor/poll_add_comment_dialog.dart'; -import 'package:stream_chat_flutter/src/poll/interactor/poll_suggest_option_dialog.dart'; -import 'package:stream_chat_flutter/src/poll/interactor/stream_poll_interactor.dart'; -import 'package:stream_chat_flutter/src/poll/stream_poll_comments_dialog.dart'; -import 'package:stream_chat_flutter/src/poll/stream_poll_options_dialog.dart'; -import 'package:stream_chat_flutter/src/poll/stream_poll_results_dialog.dart'; -import 'package:stream_chat_flutter/src/stream_chat.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -const _maxVisibleOptionCount = 10; - -const _kDefaultPollMessageConstraints = BoxConstraints( - maxWidth: 270, -); - -/// {@template pollMessage} -/// A widget that displays a poll message. -/// -/// Used in [MessageCard] to display a poll message. -/// {@endtemplate} -class PollMessage extends StatefulWidget { - /// {@macro pollMessage} - const PollMessage({ - super.key, - required this.message, - }); - - /// The message with the poll to display. - final Message message; - - @override - State createState() => _PollMessageState(); -} - -class _PollMessageState extends State { - late final _messageNotifier = ValueNotifier(widget.message); - - @override - void didUpdateWidget(covariant PollMessage oldWidget) { - super.didUpdateWidget(oldWidget); - if (oldWidget.message != widget.message) { - // If the message changes, schedule an update for the next frame - WidgetsBinding.instance.addPostFrameCallback((_) { - _messageNotifier.value = widget.message; - }); - } - } - - @override - void dispose() { - _messageNotifier.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return ValueListenableBuilder( - valueListenable: _messageNotifier, - builder: (context, message, child) { - final poll = message.poll; - if (poll == null) return const SizedBox.shrink(); - - final currentUser = StreamChat.of(context).currentUser; - if (currentUser == null) return const SizedBox.shrink(); - - final channel = StreamChannel.of(context).channel; - - Future onAddComment() async { - final commentText = await showPollAddCommentDialog( - context: context, - // We use the first answer as the initial value because the user - // can only add one comment per poll. - initialValue: poll.ownAnswers.firstOrNull?.answerText ?? '', - ); - - if (commentText == null) return; - channel.addPollAnswer(message, poll, answerText: commentText); - } - - Future onSuggestOption() async { - final optionText = await showPollSuggestOptionDialog( - context: context, - ); - - if (optionText == null) return; - channel.createPollOption(poll, PollOption(text: optionText)); - } - - return ConstrainedBox( - constraints: _kDefaultPollMessageConstraints, - child: StreamPollInteractor( - poll: poll, - currentUser: currentUser, - visibleOptionCount: _maxVisibleOptionCount, - onEndVote: () => channel.closePoll(poll), - onCastVote: (option) => channel.castPollVote(message, poll, option), - onRemoveVote: (vote) => channel.removePollVote(message, poll, vote), - onAddComment: onAddComment, - onSuggestOption: onSuggestOption, - // We need to pass the notifier here instead of the poll because the - // options dialog will have no way to update the poll itself. - onViewComments: () => showStreamPollCommentsDialog( - context: context, - messageNotifier: _messageNotifier, - ), - onSeeMoreOptions: () => showStreamPollOptionsDialog( - context: context, - messageNotifier: _messageNotifier, - ), - onViewResults: () => showStreamPollResultsDialog( - context: context, - messageNotifier: _messageNotifier, - ), - ), - ); - }, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/quoted_message.dart b/packages/stream_chat_flutter/lib/src/message_widget/quoted_message.dart deleted file mode 100644 index 8f73efb01c..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/quoted_message.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/message_input/quoted_message_widget.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template quotedMessage} -/// A quoted message in a chat. -/// -/// Used in [QuotedMessageCard]. Should not be used elsewhere. -/// {@endtemplate} -class QuotedMessage extends StatelessWidget { - /// {@macro quotedMessage} - const QuotedMessage({ - super.key, - required this.message, - required this.hasNonUrlAttachments, - this.textBuilder, - }); - - /// {@macro message} - final Message message; - - /// {@macro hasNonUrlAttachments} - final bool hasNonUrlAttachments; - - /// {@macro textBuilder} - final Widget Function(BuildContext, Message)? textBuilder; - - @override - Widget build(BuildContext context) { - final streamChat = StreamChat.of(context); - final chatThemeData = StreamChatTheme.of(context); - - final isMyMessage = message.user?.id == streamChat.currentUser?.id; - final isMyQuotedMessage = - message.quotedMessage?.user?.id == streamChat.currentUser?.id; - return StreamQuotedMessageWidget( - message: message.quotedMessage!, - messageTheme: isMyMessage - ? chatThemeData.otherMessageTheme - : chatThemeData.ownMessageTheme, - reverse: !isMyQuotedMessage, - textBuilder: textBuilder, - padding: EdgeInsets.only( - right: 8, - left: 8, - top: 8, - bottom: hasNonUrlAttachments ? 8 : 0, - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/reactions/desktop_reactions_builder.dart b/packages/stream_chat_flutter/lib/src/message_widget/reactions/desktop_reactions_builder.dart deleted file mode 100644 index 753341083b..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/reactions/desktop_reactions_builder.dart +++ /dev/null @@ -1,254 +0,0 @@ -// ignore_for_file: cascade_invocations - -import 'package:collection/collection.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_portal/flutter_portal.dart'; -import 'package:stream_chat_flutter/src/message_widget/reactions/reactions_card.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template desktopReactionsBuilder} -/// Builds a list of reactions to a message on desktop & web. -/// -/// Not intended for use outside of [MessageWidgetContent]. -/// {@endtemplate} -class DesktopReactionsBuilder extends StatefulWidget { - /// {@macro desktopReactionsBuilder} - const DesktopReactionsBuilder({ - super.key, - required this.message, - required this.messageTheme, - this.onHover, - this.borderSide, - required this.reverse, - }); - - /// The message to show reactions for. - final Message message; - - /// The theme to use for the reactions. - /// - /// [StreamMessageThemeData] is used because the design spec for desktop - /// reactions matches the design spec for messages. - final StreamMessageThemeData messageTheme; - - /// Callback to run when the mouse enters or exits the reactions. - final OnReactionsHover? onHover; - - /// {@macro borderSide} - final BorderSide? borderSide; - - /// {@macro reverse} - final bool reverse; - - @override - State createState() => - _DesktopReactionsBuilderState(); - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties.add( - DiagnosticsProperty('message', message), - ); - properties.add( - DiagnosticsProperty( - 'messageTheme', - messageTheme, - ), - ); - properties.add( - DiagnosticsProperty('borderSide', borderSide), - ); - properties.add(DiagnosticsProperty('reverse', reverse)); - } -} - -class _DesktopReactionsBuilderState extends State { - bool _showReactionsPopup = false; - - @override - Widget build(BuildContext context) { - final streamChat = StreamChat.of(context); - final currentUser = streamChat.currentUser!; - final reactionIcons = StreamChatConfiguration.of(context).reactionIcons; - final streamChatTheme = StreamChatTheme.of(context); - - final reactionsMap = {}; - widget.message.latestReactions?.forEach((element) { - if (!reactionsMap.containsKey(element.type) || - element.user!.id == currentUser.id) { - reactionsMap[element.type] = element; - } - }); - - final reactionsList = reactionsMap.values.toList() - ..sort((a, b) => a.user!.id == currentUser.id ? 1 : -1); - - return PortalTarget( - visible: _showReactionsPopup, - portalCandidateLabels: const [kPortalMessageListViewLabel], - anchor: Aligned( - target: widget.reverse ? Alignment.topRight : Alignment.topLeft, - follower: widget.reverse ? Alignment.bottomRight : Alignment.bottomLeft, - shiftToWithinBound: const AxisFlag(y: true), - ), - portalFollower: MouseRegion( - onEnter: (_) => _onReactionsHover(true), - onExit: (_) => _onReactionsHover(false), - child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 336, - maxHeight: 342, - ), - child: ReactionsCard( - currentUser: currentUser, - message: widget.message, - messageTheme: widget.messageTheme, - ), - ), - ), - child: MouseRegion( - cursor: SystemMouseCursors.click, - onEnter: (_) => _onReactionsHover(true), - onExit: (_) => _onReactionsHover(false), - child: Padding( - padding: EdgeInsets.symmetric( - vertical: 2, - horizontal: widget.reverse ? 0 : 4, - ), - child: Wrap( - spacing: 4, - runSpacing: 4, - children: [ - ...reactionsList.map((reaction) { - final reactionIcon = reactionIcons.firstWhereOrNull( - (r) => r.type == reaction.type, - ); - - return _BottomReaction( - currentUser: currentUser, - reaction: reaction, - message: widget.message, - borderSide: widget.borderSide, - messageTheme: widget.messageTheme, - reactionIcon: reactionIcon, - streamChatTheme: streamChatTheme, - ); - }).toList(), - ], - ), - ), - ), - ); - } - - void _onReactionsHover(bool isHovering) { - if (widget.onHover != null) { - return widget.onHover!(isHovering); - } - - setState(() => _showReactionsPopup = isHovering); - } -} - -class _BottomReaction extends StatelessWidget { - const _BottomReaction({ - required this.currentUser, - required this.reaction, - required this.message, - required this.borderSide, - required this.messageTheme, - required this.reactionIcon, - required this.streamChatTheme, - }); - - final User currentUser; - final Reaction reaction; - final Message message; - final BorderSide? borderSide; - final StreamMessageThemeData? messageTheme; - final StreamReactionIcon? reactionIcon; - final StreamChatThemeData streamChatTheme; - - @override - Widget build(BuildContext context) { - final userId = currentUser.id; - - final backgroundColor = messageTheme?.reactionsBackgroundColor; - - return GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - if (reaction.userId == userId) { - StreamChannel.of(context).channel.deleteReaction( - message, - reaction, - ); - } else if (reactionIcon != null) { - StreamChannel.of(context).channel.sendReaction( - message, - reactionIcon!.type, - score: reaction.score + 1, - enforceUnique: - StreamChatConfiguration.of(context).enforceUniqueReactions, - ); - } - }, - child: Card( - margin: EdgeInsets.zero, - // Setting elevation as null when background color is transparent. - // This is done to avoid shadow when background color is transparent. - elevation: backgroundColor == Colors.transparent ? 0 : null, - shape: RoundedRectangleBorder( - side: borderSide ?? - BorderSide( - color: messageTheme?.reactionsBorderColor ?? Colors.transparent, - ), - borderRadius: BorderRadius.circular(10), - ), - color: backgroundColor, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - ConstrainedBox( - constraints: BoxConstraints.tight( - const Size.square(14), - ), - child: reactionIcon?.builder( - context, - reaction.user?.id == userId, - 14, - ) ?? - Icon( - Icons.help_outline_rounded, - size: 14, - color: reaction.user?.id == userId - ? streamChatTheme.colorTheme.accentPrimary - : streamChatTheme.colorTheme.textLowEmphasis, - ), - ), - const SizedBox(width: 4), - Text( - '${reaction.score}', - style: const TextStyle( - fontSize: 10, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - ), - ), - ); - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties.add(DiagnosticsProperty('reaction', reaction)); - properties.add(DiagnosticsProperty('message', message)); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/reactions/message_reactions_modal.dart b/packages/stream_chat_flutter/lib/src/message_widget/reactions/message_reactions_modal.dart deleted file mode 100644 index 072ee5bece..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/reactions/message_reactions_modal.dart +++ /dev/null @@ -1,131 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/message_widget/reactions/reactions_align.dart'; -import 'package:stream_chat_flutter/src/message_widget/reactions/reactions_card.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamMessageReactionsModal} -/// Modal widget for displaying message reactions -/// {@endtemplate} -class StreamMessageReactionsModal extends StatelessWidget { - /// {@macro streamMessageReactionsModal} - const StreamMessageReactionsModal({ - super.key, - required this.message, - required this.messageWidget, - required this.messageTheme, - this.showReactionPicker = true, - this.reverse = false, - this.onUserAvatarTap, - }); - - /// Widget that shows the message - final Widget messageWidget; - - /// Message to display reactions of - final Message message; - - /// [StreamMessageThemeData] to apply to [message] - final StreamMessageThemeData messageTheme; - - /// {@macro reverse} - final bool reverse; - - /// Flag for showing reaction picker. - final bool showReactionPicker; - - /// {@macro onUserAvatarTap} - final void Function(User)? onUserAvatarTap; - - @override - Widget build(BuildContext context) { - final user = StreamChat.of(context).currentUser; - final _userPermissions = StreamChannel.of(context).channel.ownCapabilities; - final orientation = MediaQuery.of(context).orientation; - - final hasReactionPermission = - _userPermissions.contains(PermissionType.sendReaction); - final fontSize = messageTheme.messageTextStyle?.fontSize; - - final child = Center( - child: SingleChildScrollView( - child: SafeArea( - child: Padding( - padding: const EdgeInsets.all(8), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - if (showReactionPicker && hasReactionPermission) - LayoutBuilder( - builder: (context, constraints) { - return Align( - alignment: Alignment( - calculateReactionsHorizontalAlignment( - user, - message, - constraints, - fontSize, - orientation, - ), - 0, - ), - child: StreamReactionPicker( - message: message, - ), - ); - }, - ), - const SizedBox(height: 10), - IgnorePointer( - child: messageWidget, - ), - if (message.latestReactions?.isNotEmpty == true) ...[ - const SizedBox(height: 8), - ReactionsCard( - currentUser: user!, - message: message, - messageTheme: messageTheme, - ), - ], - ], - ), - ), - ), - ), - ); - - return GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () => Navigator.of(context).maybePop(), - child: Stack( - children: [ - Positioned.fill( - child: BackdropFilter( - filter: ImageFilter.blur( - sigmaX: 10, - sigmaY: 10, - ), - child: DecoratedBox( - decoration: BoxDecoration( - color: StreamChatTheme.of(context).colorTheme.overlay, - ), - ), - ), - ), - TweenAnimationBuilder( - tween: Tween(begin: 0, end: 1), - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOutBack, - builder: (context, val, widget) => Transform.scale( - scale: val, - child: widget, - ), - child: child, - ), - ], - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/reactions/reaction_bubble.dart b/packages/stream_chat_flutter/lib/src/message_widget/reactions/reaction_bubble.dart deleted file mode 100644 index 82e5b34d02..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/reactions/reaction_bubble.dart +++ /dev/null @@ -1,335 +0,0 @@ -import 'dart:math'; - -import 'package:collection/collection.dart' show IterableExtension; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamReactionBubble} -/// Creates a reaction bubble that displays over messages. -/// {@endtemplate} -class StreamReactionBubble extends StatelessWidget { - /// {@macro streamReactionBubble} - const StreamReactionBubble({ - super.key, - required this.reactions, - required this.borderColor, - required this.backgroundColor, - required this.maskColor, - this.reverse = false, - this.flipTail = false, - this.highlightOwnReactions = true, - this.tailCirclesSpacing = 0, - }); - - /// Reactions to show - final List reactions; - - /// Border color of bubble - final Color borderColor; - - /// Background color of bubble - final Color backgroundColor; - - /// Mask color - final Color maskColor; - - /// Reverse for other side - final bool reverse; - - /// Reverse tail for other side - final bool flipTail; - - /// Flag for highlighting own reactions - final bool highlightOwnReactions; - - /// Spacing for tail circles - final double tailCirclesSpacing; - - @override - Widget build(BuildContext context) { - final reactionIcons = StreamChatConfiguration.of(context).reactionIcons; - final totalReactions = reactions.length; - final offset = - totalReactions > 1 ? 16.0.mirrorConditionally(flipTail) : 2.0; - return Stack( - alignment: Alignment.center, - children: [ - Transform.translate( - offset: Offset(-offset, 0), - child: Container( - padding: const EdgeInsets.all(2), - decoration: BoxDecoration( - color: maskColor, - borderRadius: const BorderRadius.all(Radius.circular(16)), - ), - child: Container( - padding: EdgeInsets.symmetric( - vertical: 4, - horizontal: totalReactions > 1 ? 4.0 : 0, - ), - decoration: BoxDecoration( - border: Border.all( - color: borderColor, - ), - color: backgroundColor, - borderRadius: const BorderRadius.all(Radius.circular(14)), - ), - child: LayoutBuilder( - builder: (context, constraints) => Flex( - direction: Axis.horizontal, - mainAxisSize: MainAxisSize.min, - children: [ - if (constraints.maxWidth < double.infinity) - ...reactions - .take((constraints.maxWidth) ~/ 24) - .map((reaction) => _buildReaction( - reactionIcons, - reaction, - context, - )) - .toList(), - if (constraints.maxWidth == double.infinity) - ...reactions - .map((reaction) => _buildReaction( - reactionIcons, - reaction, - context, - )) - .toList(), - ], - ), - ), - ), - ), - ), - Positioned( - bottom: 2, - left: reverse ? null : 13, - right: reverse ? 13 : null, - child: _buildReactionsTail(context), - ), - ], - ); - } - - Widget _buildReaction( - List reactionIcons, - Reaction reaction, - BuildContext context, - ) { - final reactionIcon = reactionIcons.firstWhereOrNull( - (r) => r.type == reaction.type, - ); - - final chatThemeData = StreamChatTheme.of(context); - final userId = StreamChat.of(context).currentUser?.id; - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: reactionIcon != null - ? ConstrainedBox( - constraints: BoxConstraints.tight(const Size.square(14)), - child: reactionIcon.builder( - context, - highlightOwnReactions && reaction.user?.id == userId, - 16, - ), - ) - : Icon( - Icons.help_outline_rounded, - size: 14, - color: (highlightOwnReactions && reaction.user?.id == userId) - ? chatThemeData.colorTheme.accentPrimary - : chatThemeData.colorTheme.textLowEmphasis, - ), - ); - } - - Widget _buildReactionsTail(BuildContext context) { - final tail = CustomPaint( - painter: ReactionBubblePainter( - backgroundColor, - borderColor, - maskColor, - tailCirclesSpace: tailCirclesSpacing, - flipTail: !flipTail, - numberOfReactions: reactions.length, - ), - ); - return tail; - } -} - -/// Painter widget for a reaction bubble -class ReactionBubblePainter extends CustomPainter { - /// Constructor for creating a [ReactionBubblePainter] - ReactionBubblePainter( - this.color, - this.borderColor, - this.maskColor, { - this.tailCirclesSpace = 0, - this.flipTail = false, - this.numberOfReactions = 0, - }); - - /// Color of bubble - final Color color; - - /// Border color of bubble - final Color borderColor; - - /// Mask color - final Color maskColor; - - /// Tail circle space - final double tailCirclesSpace; - - /// Flip tail - final bool flipTail; - - /// Number of reactions on the page - final int numberOfReactions; - - @override - void paint(Canvas canvas, Size size) { - _drawOvalMask(size, canvas); - - _drawMask(size, canvas); - - _drawOval(size, canvas); - - _drawOvalBorder(size, canvas); - - _drawArc(size, canvas); - - _drawBorder(size, canvas); - } - - void _drawOvalMask(Size size, Canvas canvas) { - final paint = Paint() - ..color = maskColor - ..style = PaintingStyle.fill; - - final path = Path() - ..addOval( - Rect.fromCircle( - center: const Offset(4, 3).mirrorConditionally(flipTail) + - Offset(tailCirclesSpace, tailCirclesSpace) - .mirrorConditionally(flipTail), - radius: 4, - ), - ); - canvas.drawPath(path, paint); - } - - void _drawOvalBorder(Size size, Canvas canvas) { - final paint = Paint() - ..color = borderColor - ..strokeWidth = 1 - ..style = PaintingStyle.stroke; - - final path = Path() - ..addOval( - Rect.fromCircle( - center: const Offset(4, 3).mirrorConditionally(flipTail) + - Offset(tailCirclesSpace, tailCirclesSpace) - .mirrorConditionally(flipTail), - radius: 2, - ), - ); - canvas.drawPath(path, paint); - } - - void _drawOval(Size size, Canvas canvas) { - final paint = Paint() - ..color = color - ..strokeWidth = 1; - - final path = Path() - ..addOval(Rect.fromCircle( - center: const Offset(4, 3).mirrorConditionally(flipTail) + - Offset(tailCirclesSpace, tailCirclesSpace) - .mirrorConditionally(flipTail), - radius: 2, - )); - canvas.drawPath(path, paint); - } - - void _drawBorder(Size size, Canvas canvas) { - final paint = Paint() - ..color = borderColor - ..strokeWidth = 1 - ..style = PaintingStyle.stroke; - - const dy = -2.2; - final startAngle = flipTail ? -0.1 : 1.1; - final sweepAngle = flipTail ? -1.2 : (numberOfReactions > 1 ? 1.2 : 0.9); - final path = Path() - ..addArc( - Rect.fromCircle( - center: const Offset(1, dy).mirrorConditionally(flipTail), - radius: 4, - ), - -pi * startAngle, - -pi / sweepAngle, - ); - canvas.drawPath(path, paint); - } - - void _drawArc(Size size, Canvas canvas) { - final paint = Paint() - ..color = color - ..strokeWidth = 1; - - const dy = -2.2; - final startAngle = flipTail ? -0.0 : 1.0; - final sweepAngle = flipTail ? -1.3 : 1.3; - final path = Path() - ..addArc( - Rect.fromCircle( - center: const Offset(1, dy).mirrorConditionally(flipTail), - radius: 4, - ), - -pi * startAngle, - -pi * sweepAngle, - ); - canvas.drawPath(path, paint); - } - - void _drawMask(Size size, Canvas canvas) { - final paint = Paint() - ..color = maskColor - ..strokeWidth = 1 - ..style = PaintingStyle.fill; - - const dy = -2.2; - final startAngle = flipTail ? -0.1 : 1.1; - final sweepAngle = flipTail ? -1.2 : 1.2; - final path = Path() - ..addArc( - Rect.fromCircle( - center: const Offset(1, dy).mirrorConditionally(flipTail), - radius: 6, - ), - -pi * startAngle, - -pi / sweepAngle, - ); - canvas.drawPath(path, paint); - } - - @override - bool shouldRepaint(CustomPainter oldDelegate) => true; -} - -/// Extension on [Offset] -extension YTransformer on Offset { - /// Flips x coordinate when flip is true - // ignore: avoid_positional_boolean_parameters - Offset mirrorConditionally(bool flip) => Offset(flip ? -dx : dx, dy); -} - -/// Extension on [Offset] -extension IntTransformer on double { - /// Flips x coordinate when flip is true - // ignore: avoid_positional_boolean_parameters - double mirrorConditionally(bool flip) => flip ? -this : this; -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/reactions/reaction_indicator.dart b/packages/stream_chat_flutter/lib/src/message_widget/reactions/reaction_indicator.dart deleted file mode 100644 index 873d60f7a4..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/reactions/reaction_indicator.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template reactionIndicator} -/// Indicates the reaction a [StreamMessageWidget] has. -/// -/// Used in [MessageWidgetContent]. -/// {@endtemplate} -class ReactionIndicator extends StatelessWidget { - /// {@macro reactionIndicator} - const ReactionIndicator({ - super.key, - required this.ownId, - required this.message, - required this.onTap, - required this.reverse, - required this.messageTheme, - }); - - /// The id of the current user. - final String ownId; - - /// {@macro message} - final Message message; - - /// The callback to perform when the widget is tapped or clicked. - final VoidCallback onTap; - - /// {@macro reverse} - final bool reverse; - - /// {@macro messageTheme} - final StreamMessageThemeData messageTheme; - - @override - Widget build(BuildContext context) { - final reactionsMap = {}; - message.latestReactions?.forEach((element) { - if (!reactionsMap.containsKey(element.type) || - element.user!.id == ownId) { - reactionsMap[element.type] = element; - } - }); - final reactionsList = reactionsMap.values.toList() - ..sort((a, b) => a.user!.id == ownId ? 1 : -1); - - return Transform( - transform: Matrix4.translationValues(reverse ? 12 : -12, 0, 0), - child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 22 * 6.0, - ), - child: AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - child: GestureDetector( - onTap: onTap, - child: StreamReactionBubble( - key: ValueKey('${message.id}.reactions'), - reverse: reverse, - flipTail: reverse, - backgroundColor: - messageTheme.reactionsBackgroundColor ?? Colors.transparent, - borderColor: - messageTheme.reactionsBorderColor ?? Colors.transparent, - maskColor: messageTheme.reactionsMaskColor ?? Colors.transparent, - reactions: reactionsList, - ), - ), - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/reactions/reaction_picker.dart b/packages/stream_chat_flutter/lib/src/message_widget/reactions/reaction_picker.dart deleted file mode 100644 index 5f61d1dfcc..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/reactions/reaction_picker.dart +++ /dev/null @@ -1,170 +0,0 @@ -import 'package:ezanimation/ezanimation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamReactionPicker} -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/reaction_picker.png) -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/reaction_picker_paint.png) -/// -/// Allows the user to select reactions to a message on mobile. -/// -/// It is not recommended to use this widget directly as it's one of the -/// default widgets used by [StreamMessageWidget.onMessageActions]. -/// {@endtemplate} -class StreamReactionPicker extends StatefulWidget { - /// {@macro streamReactionPicker} - const StreamReactionPicker({ - super.key, - required this.message, - }); - - /// Message to attach the reaction to - final Message message; - - @override - _StreamReactionPickerState createState() => _StreamReactionPickerState(); -} - -class _StreamReactionPickerState extends State - with TickerProviderStateMixin { - List animations = []; - - @override - Widget build(BuildContext context) { - final chatThemeData = StreamChatTheme.of(context); - final reactionIcons = StreamChatConfiguration.of(context).reactionIcons; - - if (animations.isEmpty && reactionIcons.isNotEmpty) { - reactionIcons.forEach((element) { - animations.add( - EzAnimation.tween( - Tween(begin: 0.0, end: 1.0), - const Duration(milliseconds: 500), - curve: Curves.easeInOutBack, - ), - ); - }); - - triggerAnimations(); - } - - final child = Material( - borderRadius: BorderRadius.circular(24), - color: chatThemeData.colorTheme.barsBg, - clipBehavior: Clip.hardEdge, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 8, - ), - child: Row( - spacing: 16, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.end, - mainAxisSize: MainAxisSize.min, - children: reactionIcons.map((reactionIcon) { - final ownReactionIndex = widget.message.ownReactions?.indexWhere( - (reaction) => reaction.type == reactionIcon.type, - ) ?? - -1; - final index = reactionIcons.indexOf(reactionIcon); - - final child = reactionIcon.builder( - context, - ownReactionIndex != -1, - 24, - ); - - return ConstrainedBox( - constraints: const BoxConstraints.tightFor( - height: 24, - width: 24, - ), - child: RawMaterialButton( - elevation: 0, - shape: ContinuousRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - constraints: const BoxConstraints.tightFor( - height: 24, - width: 24, - ), - onPressed: () { - if (ownReactionIndex != -1) { - removeReaction( - context, - widget.message.ownReactions![ownReactionIndex], - ); - } else { - sendReaction( - context, - reactionIcon.type, - ); - } - }, - child: AnimatedBuilder( - animation: animations[index], - builder: (context, child) => Transform.scale( - scale: animations[index].value, - child: child, - ), - child: child, - ), - ), - ); - }).toList(), - ), - ), - ); - - return TweenAnimationBuilder( - tween: Tween(begin: 0, end: 1), - curve: Curves.easeInOutBack, - duration: const Duration(milliseconds: 500), - builder: (context, val, widget) => Transform.scale( - scale: val, - child: widget, - ), - child: child, - ); - } - - Future triggerAnimations() async { - for (final a in animations) { - a.start(); - await Future.delayed(const Duration(milliseconds: 100)); - } - } - - Future pop() async { - for (final a in animations) { - a.stop(); - } - Navigator.of(context).pop(); - } - - /// Add a reaction to the message - void sendReaction(BuildContext context, String reactionType) { - StreamChannel.of(context).channel.sendReaction( - widget.message, - reactionType, - enforceUnique: - StreamChatConfiguration.of(context).enforceUniqueReactions, - ); - pop(); - } - - /// Remove a reaction from the message - void removeReaction(BuildContext context, Reaction reaction) { - StreamChannel.of(context).channel.deleteReaction(widget.message, reaction); - pop(); - } - - @override - void dispose() { - for (final a in animations) { - a.dispose(); - } - super.dispose(); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/reactions/reactions_align.dart b/packages/stream_chat_flutter/lib/src/message_widget/reactions/reactions_align.dart deleted file mode 100644 index a7baad7eec..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/reactions/reactions_align.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// This method calculates the align that the modal of reactions should have. -/// This is an approximation based on the size of the message and the -/// available space in the screen. -double calculateReactionsHorizontalAlignment( - User? user, - Message message, - BoxConstraints constraints, - double? fontSize, - Orientation orientation, -) { - final maxWidth = constraints.maxWidth; - - final roughSentenceSize = message.roughMessageSize(fontSize); - final hasAttachments = message.attachments.isNotEmpty; - final isReply = message.quotedMessageId != null; - final isAttachment = hasAttachments && !isReply; - - // divFactor is the percentage of the available space that the message takes. - // When the divFactor is bigger than 0.5 that means that the messages is - // bigger than 50% of the available space and the modal should have an offset - // in the direction that the message grows. When the divFactor is smaller - // than 0.5 then the offset should be to he side opposite of the message - // growth. - // In resume, when divFactor > 0.5 then result > 0, when divFactor < 0.5 - // then result < 0. - var divFactor = 0.5; - - // When in portrait, attachments normally take 75% of the screen, when in - // landscape, attachments normally take 50% of the screen. - if (isAttachment) { - if (orientation == Orientation.portrait) { - divFactor = 0.75; - } else { - divFactor = 0.5; - } - } else { - divFactor = roughSentenceSize == 0 ? 0.5 : (roughSentenceSize / maxWidth); - } - - final signal = user?.id == message.user?.id ? 1 : -1; - final result = signal * (1 - divFactor * 2.0); - - // Ensure reactions don't get pushed past the edge of the screen. - // - // This happens if divFactor is really big. When this happens, we can simply - // move the model all the way to the end of screen. - return result.clamp(-1, 1); -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/reactions/reactions_card.dart b/packages/stream_chat_flutter/lib/src/message_widget/reactions/reactions_card.dart deleted file mode 100644 index 727b44affc..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/reactions/reactions_card.dart +++ /dev/null @@ -1,142 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/avatars/user_avatar.dart'; -import 'package:stream_chat_flutter/src/message_widget/reactions/reaction_bubble.dart'; -import 'package:stream_chat_flutter/src/theme/message_theme.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter/src/utils/extensions.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// {@template reactionsCard} -/// A card that displays the reactions to a message. -/// -/// Used in [StreamMessageReactionsModal] and [DesktopReactionsBuilder]. -/// {@endtemplate} -class ReactionsCard extends StatelessWidget { - /// {@macro reactionsCard} - const ReactionsCard({ - super.key, - required this.currentUser, - required this.message, - required this.messageTheme, - this.onUserAvatarTap, - }); - - /// Current logged in user. - final User currentUser; - - /// Message to display reactions of. - final Message message; - - /// [StreamMessageThemeData] to apply to [message]. - final StreamMessageThemeData messageTheme; - - /// {@macro onUserAvatarTap} - final void Function(User)? onUserAvatarTap; - - @override - Widget build(BuildContext context) { - final chatThemeData = StreamChatTheme.of(context); - return Card( - color: chatThemeData.colorTheme.barsBg, - clipBehavior: Clip.hardEdge, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - margin: EdgeInsets.zero, - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - context.translations.messageReactionsLabel, - style: chatThemeData.textTheme.headlineBold, - ), - const SizedBox(height: 16), - Flexible( - child: SingleChildScrollView( - child: Wrap( - spacing: 16, - runSpacing: 16, - children: message.latestReactions! - .map((e) => _buildReaction( - e, - currentUser, - context, - )) - .toList(), - ), - ), - ), - ], - ), - ), - ); - } - - Widget _buildReaction( - Reaction reaction, - User currentUser, - BuildContext context, - ) { - final isCurrentUser = reaction.user?.id == currentUser.id; - final chatThemeData = StreamChatTheme.of(context); - final reverse = !isCurrentUser; - return ConstrainedBox( - constraints: BoxConstraints.loose( - const Size(64, 100), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Stack( - clipBehavior: Clip.none, - children: [ - StreamUserAvatar( - onTap: onUserAvatarTap, - user: reaction.user!, - constraints: const BoxConstraints.tightFor( - height: 64, - width: 64, - ), - onlineIndicatorConstraints: const BoxConstraints.tightFor( - height: 12, - width: 12, - ), - borderRadius: BorderRadius.circular(32), - ), - Positioned( - bottom: 6, - left: !reverse ? -3 : null, - right: reverse ? -3 : null, - child: Align( - alignment: - reverse ? Alignment.centerRight : Alignment.centerLeft, - child: StreamReactionBubble( - reactions: [reaction], - reverse: !reverse, - flipTail: !reverse, - borderColor: - messageTheme.reactionsBorderColor ?? Colors.transparent, - backgroundColor: messageTheme.reactionsBackgroundColor ?? - Colors.transparent, - maskColor: chatThemeData.colorTheme.barsBg, - tailCirclesSpacing: 1, - ), - ), - ), - ], - ), - const SizedBox(height: 8), - Text( - reaction.user!.name.split(' ')[0], - style: chatThemeData.textTheme.footnoteBold, - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis, - maxLines: 1, - ), - ], - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/sending_indicator_builder.dart b/packages/stream_chat_flutter/lib/src/message_widget/sending_indicator_builder.dart deleted file mode 100644 index 0a7b6c4121..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/sending_indicator_builder.dart +++ /dev/null @@ -1,99 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template sendingIndicatorWrapper} -/// Helper widget for building a [StreamSendingIndicator]. -/// -/// Used in [BottomRow]. Should not be used elsewhere. -/// {@endtemplate} -class SendingIndicatorBuilder extends StatelessWidget { - /// {@macro sendingIndicatorWrapper} - const SendingIndicatorBuilder({ - super.key, - required this.messageTheme, - required this.message, - required this.hasNonUrlAttachments, - required this.streamChat, - required this.streamChatTheme, - this.channel, - }); - - /// {@macro messageTheme} - final StreamMessageThemeData messageTheme; - - /// {@macro message} - final Message message; - - /// {@macro hasNonUrlAttachments} - final bool hasNonUrlAttachments; - - /// {@macro streamChat} - final StreamChatState streamChat; - - /// {@macro streamChatThemeData} - final StreamChatThemeData streamChatTheme; - - /// {@macro channel} - final Channel? channel; - - @override - Widget build(BuildContext context) { - final style = messageTheme.createdAtStyle; - final channel = this.channel ?? StreamChannel.of(context).channel; - final memberCount = channel.memberCount ?? 0; - - if (hasNonUrlAttachments && message.state.isOutgoing) { - final totalAttachments = message.attachments.length; - final attachmentsToUpload = message.attachments.where((it) { - return !it.uploadState.isSuccess; - }); - - if (attachmentsToUpload.isNotEmpty) { - return Text( - context.translations.attachmentsUploadProgressText( - remaining: attachmentsToUpload.length, - total: totalAttachments, - ), - style: style, - ); - } - } - - return BetterStreamBuilder>( - stream: channel.state?.readStream, - initialData: channel.state?.read, - builder: (context, data) { - final readList = data.where((it) => - it.user.id != streamChat.currentUser?.id && - (it.lastRead.isAfter(message.createdAt) || - it.lastRead.isAtSameMomentAs(message.createdAt))); - - final isMessageRead = readList.isNotEmpty; - Widget child = StreamSendingIndicator( - message: message, - isMessageRead: isMessageRead, - size: style?.fontSize, - ); - - if (isMessageRead) { - child = Row( - mainAxisSize: MainAxisSize.min, - children: [ - if (memberCount > 2) - Text( - readList.length.toString(), - style: style?.copyWith( - color: streamChatTheme.colorTheme.accentPrimary, - ), - ), - const SizedBox(width: 2), - child, - ], - ); - } - - return child; - }, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/text_bubble.dart b/packages/stream_chat_flutter/lib/src/message_widget/text_bubble.dart deleted file mode 100644 index 3dd243341a..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/text_bubble.dart +++ /dev/null @@ -1,72 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template textBubble} -/// The bubble around a [StreamMessageText]. -/// -/// Used in [MessageCard]. Should not be used elsewhere. -/// {@endtemplate} -class TextBubble extends StatelessWidget { - /// {@macro textBubble} - const TextBubble({ - super.key, - required this.message, - required this.isOnlyEmoji, - required this.textPadding, - required this.messageTheme, - required this.hasUrlAttachments, - required this.hasQuotedMessage, - this.textBuilder, - this.onLinkTap, - this.onMentionTap, - }); - - /// {@macro message} - final Message message; - - /// {@macro isOnlyEmoji} - final bool isOnlyEmoji; - - /// {@macro textPadding} - final EdgeInsets textPadding; - - /// {@macro textBuilder} - final Widget Function(BuildContext, Message)? textBuilder; - - /// {@macro onLinkTap} - final void Function(String)? onLinkTap; - - /// {@macro onMentionTap} - final void Function(User)? onMentionTap; - - /// {@macro messageTheme} - final StreamMessageThemeData messageTheme; - - /// {@macro hasUrlAttachments} - final bool hasUrlAttachments; - - /// {@macro hasQuotedMessage} - final bool hasQuotedMessage; - - @override - Widget build(BuildContext context) { - if (message.text?.trim().isEmpty ?? true) return const Offstage(); - return Padding( - padding: isOnlyEmoji ? EdgeInsets.zero : textPadding, - child: textBuilder != null - ? textBuilder!(context, message) - : StreamMessageText( - onLinkTap: onLinkTap, - message: message, - onMentionTap: onMentionTap, - messageTheme: isOnlyEmoji - ? messageTheme.copyWith( - messageTextStyle: messageTheme.messageTextStyle!.copyWith( - fontSize: 42, - ), - ) - : messageTheme, - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/thread_painter.dart b/packages/stream_chat_flutter/lib/src/message_widget/thread_painter.dart deleted file mode 100644 index 09bb63c93f..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/thread_painter.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template threadReplyPainter} -/// A custom painter used to render thread replies. -/// -/// Used in [BottomRow]. -/// {@endtemplate} -class ThreadReplyPainter extends CustomPainter { - /// {@macro threadReplyPainter} - const ThreadReplyPainter({ - this.context, - required this.color, - this.reverse = false, - }); - - /// The color to paint the thread reply with. - final Color? color; - - /// The [BuildContext] to use to retrieve the [StreamChatTheme]. - final BuildContext? context; - - /// {@macro reverse} - final bool reverse; - - @override - void paint(Canvas canvas, Size size) { - final paint = Paint() - ..color = color ?? StreamChatTheme.of(context!).colorTheme.disabled - ..style = PaintingStyle.stroke - ..strokeWidth = 1 - ..strokeCap = StrokeCap.round; - - final path = Path() - ..moveTo(reverse ? size.width : 0, 0) - ..quadraticBezierTo( - reverse ? size.width : 0, - size.height * 0.38, - reverse ? size.width : 0, - size.height * 0.5, - ) - ..quadraticBezierTo( - reverse ? size.width : 0, - size.height, - reverse ? 0 : size.width, - size.height, - ); - canvas.drawPath(path, paint); - } - - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) => false; -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/thread_participants.dart b/packages/stream_chat_flutter/lib/src/message_widget/thread_participants.dart deleted file mode 100644 index 5068e2931e..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/thread_participants.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template threadParticipants} -/// Shows the users participating in a thread. -/// -/// Used in [BottomRow]. -/// {@endtemplate} -class ThreadParticipants extends StatelessWidget { - /// {@macro threadParticipants} - const ThreadParticipants({ - super.key, - required StreamChatThemeData streamChatTheme, - required this.threadParticipants, - }) : _streamChatTheme = streamChatTheme; - - /// {@macro streamChatThemeData} - final StreamChatThemeData _streamChatTheme; - - /// The users participating in the thread. - final Iterable threadParticipants; - - @override - Widget build(BuildContext context) { - var padding = 0.0; - return Stack( - children: threadParticipants.map((user) { - padding += 8.0; - return Positioned( - right: padding - 8, - bottom: 0, - top: 0, - child: Container( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: _streamChatTheme.colorTheme.barsBg, - ), - padding: const EdgeInsets.all(1), - child: StreamUserAvatar( - user: user, - constraints: BoxConstraints.tight(const Size.fromRadius(7)), - showOnlineStatus: false, - ), - ), - ); - }).toList(), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/user_avatar_transform.dart b/packages/stream_chat_flutter/lib/src/message_widget/user_avatar_transform.dart deleted file mode 100644 index 275d63e05e..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/user_avatar_transform.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template userAvatarTransform} -/// Transforms a [StreamUserAvatar] according to the specified translation. -/// -/// Used in [MessageWidgetContent]. -/// {@endtemplate} -class UserAvatarTransform extends StatelessWidget { - /// {@macro userAvatarTransform} - const UserAvatarTransform({ - super.key, - required this.translateUserAvatar, - required this.messageTheme, - required this.message, - this.userAvatarBuilder, - this.onUserAvatarTap, - }); - - /// {@macro translateUserAvatar} - final bool translateUserAvatar; - - /// {@macro messageTheme} - final StreamMessageThemeData messageTheme; - - /// {@macro userAvatarBuilder} - final Widget Function(BuildContext, User)? userAvatarBuilder; - - /// {@macro message} - final Message message; - - /// {@macro onUserAvatarTap} - final void Function(User)? onUserAvatarTap; - - @override - Widget build(BuildContext context) { - return Transform.translate( - offset: Offset( - 0, - translateUserAvatar - ? (messageTheme.avatarTheme?.constraints.maxHeight ?? 40) / 2 - : 0, - ), - child: userAvatarBuilder?.call(context, message.user!) ?? - StreamUserAvatar( - user: message.user!, - onTap: onUserAvatarTap, - constraints: messageTheme.avatarTheme!.constraints, - borderRadius: messageTheme.avatarTheme!.borderRadius, - showOnlineStatus: false, - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/message_widget/username.dart b/packages/stream_chat_flutter/lib/src/message_widget/username.dart deleted file mode 100644 index fad0ff6fb1..0000000000 --- a/packages/stream_chat_flutter/lib/src/message_widget/username.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template username} -/// Displays the username of a particular message's sender. -/// {@endtemplate} -class Username extends StatelessWidget { - /// {@macro username} - const Username({ - super.key, - required this.message, - required this.messageTheme, - }); - - /// {@macro message} - final Message message; - - /// {@macro messageTheme} - final StreamMessageThemeData messageTheme; - - @override - Widget build(BuildContext context) { - return Text( - message.user?.name ?? '', - maxLines: 1, - key: key, - style: messageTheme.messageAuthorStyle, - overflow: TextOverflow.ellipsis, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/misc/animated_circle_border_painter.dart b/packages/stream_chat_flutter/lib/src/misc/animated_circle_border_painter.dart deleted file mode 100644 index bd02678fe0..0000000000 --- a/packages/stream_chat_flutter/lib/src/misc/animated_circle_border_painter.dart +++ /dev/null @@ -1,72 +0,0 @@ -import 'dart:math' as math; - -import 'package:flutter/widgets.dart'; - -/// A custom painter that animates a circle border or fills it based on a -/// progress value. -/// -/// This painter draws a circle with a border that can be animated to fill the -/// circle or stroke its outline based on a given progress value. The progress -/// value is a double between 0.0 and 1.0, representing the completion of the -/// animation. -/// -/// When the progress is 0.0, the circle is completely empty, and when the -/// progress is 1.0, the circle is fully filled or stroked. -/// -/// The color of the arc/circle can be customized by providing a [color]. -/// -/// Example usage: -/// ```dart -/// AnimatedCircleBorderPainter painter = AnimatedCircleBorderPainter( -/// progress: 0.5, -/// color: Colors.blue, -/// ); -/// -/// CustomPaint( -/// painter: painter, -/// size: Size(200, 200), -/// // ... other properties -/// ) -/// ``` -class AnimatedCircleBorderPainter extends CustomPainter { - /// Creates an [AnimatedCircleBorderPainter] with the specified [progress] - /// and [color]. - const AnimatedCircleBorderPainter({ - required this.progress, - required this.color, - }); - - /// The progress of the animation, as a value between 0.0 and 1.0. - final double progress; - - /// The color of the arc/circle. - final Color color; - - @override - void paint(Canvas canvas, Size size) { - final style = progress == 1.0 ? PaintingStyle.fill : PaintingStyle.stroke; - - final arcPaint = Paint() - ..style = style - ..color = color - ..strokeWidth = 2.0 - ..strokeCap = StrokeCap.round; - - final radius = size.width / 2; - final center = Offset(size.width / 2, size.height / 2); - final sweepAngle = math.pi * 2 * progress; - - canvas.drawArc( - Rect.fromCircle(center: center, radius: radius), - -math.pi / 2, - sweepAngle, - false, - arcPaint, - ); - } - - @override - bool shouldRepaint(AnimatedCircleBorderPainter oldPainter) { - return oldPainter.progress != progress || oldPainter.color != color; - } -} diff --git a/packages/stream_chat_flutter/lib/src/misc/audio_waveform.dart b/packages/stream_chat_flutter/lib/src/misc/audio_waveform.dart deleted file mode 100644 index e35f9277a5..0000000000 --- a/packages/stream_chat_flutter/lib/src/misc/audio_waveform.dart +++ /dev/null @@ -1,487 +0,0 @@ -import 'dart:math' as math; - -import 'package:collection/collection.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/theme/audio_waveform_slider_theme.dart'; -import 'package:stream_chat_flutter/src/theme/audio_waveform_theme.dart'; - -const _kAudioWaveformSliderThumbWidth = 4.0; -const _kAudioWaveformSliderThumbHeight = 28.0; - -/// {@template streamAudioWaveformSlider} -/// A widget that displays an audio waveform and allows the user to interact -/// with it using a slider. -/// {@endtemplate} -class StreamAudioWaveformSlider extends StatefulWidget { - /// {@macro streamAudioWaveformSlider} - const StreamAudioWaveformSlider({ - super.key, - required this.waveform, - this.onChangeStart, - required this.onChanged, - this.onChangeEnd, - this.limit = 100, - this.color, - this.progress = 0, - this.progressColor, - this.minBarHeight, - this.spacingRatio, - this.heightScale, - this.inverse = true, - this.thumbColor, - this.thumbBorderColor, - }); - - /// The waveform data to be drawn. - /// - /// Note: The values should be between 0 and 1. - final List waveform; - - /// Called when the thumb starts being dragged. - final ValueChanged? onChangeStart; - - /// Called while the thumb is being dragged. - final ValueChanged? onChanged; - - /// Called when the thumb stops being dragged. - final ValueChanged? onChangeEnd; - - /// The color of the wave bars. - /// - /// Defaults to [StreamAudioWaveformSliderThemeData.color]. - final Color? color; - - /// The number of wave bars that will be draw in the screen. When the length - /// of [waveform] is bigger than [limit] only the X last bars will be shown. - /// - /// Defaults to 100. - final int limit; - - /// The progress of the audio track. Used to show the progress of the audio. - /// - /// Defaults to 0. - final double progress; - - /// The color of the progressed wave bars. - /// - /// Defaults to [StreamAudioWaveformSliderThemeData.progressColor]. - final Color? progressColor; - - /// The minimum height of the bars. - /// - /// Defaults to [StreamAudioWaveformSliderThemeData.minBarHeight]. - final double? minBarHeight; - - /// The ratio of the spacing between the bars. - /// - /// Defaults to [StreamAudioWaveformSliderThemeData.spacingRatio]. - final double? spacingRatio; - - /// The scale of the height of the bars. - /// - /// Defaults to [StreamAudioWaveformSliderThemeData.heightScale]. - final double? heightScale; - - /// If true, the bars grow from right to left otherwise they grow from left - /// to right. - /// - /// Defaults to true. - final bool inverse; - - /// The color of the slider thumb. - /// - /// Defaults to [StreamAudioWaveformSliderThemeData.thumbColor]. - final Color? thumbColor; - - /// The color of the slider thumb border. - /// - /// Defaults to [StreamAudioWaveformSliderThemeData.thumbBorderColor]. - final Color? thumbBorderColor; - - @override - State createState() => - _StreamAudioWaveformSliderState(); -} - -class _StreamAudioWaveformSliderState extends State { - @override - Widget build(BuildContext context) { - final theme = StreamAudioWaveformSliderTheme.of(context); - final waveformTheme = theme.audioWaveformTheme; - - final color = widget.color ?? waveformTheme!.color!; - final progressColor = widget.progressColor ?? waveformTheme!.progressColor!; - final minBarHeight = widget.minBarHeight ?? waveformTheme!.minBarHeight!; - final spacingRatio = widget.spacingRatio ?? waveformTheme!.spacingRatio!; - final heightScale = widget.heightScale ?? waveformTheme!.heightScale!; - final thumbColor = widget.thumbColor ?? theme.thumbColor!; - final thumbBorderColor = widget.thumbBorderColor ?? theme.thumbBorderColor!; - - return HorizontalSlider( - onChangeStart: widget.onChangeStart, - onChanged: widget.onChanged, - onChangeEnd: widget.onChangeEnd, - child: LayoutBuilder( - builder: (context, constraints) => Stack( - fit: StackFit.expand, - clipBehavior: Clip.none, - alignment: Alignment.center, - children: [ - StreamAudioWaveform( - waveform: widget.waveform, - limit: widget.limit, - color: color, - progress: widget.progress, - progressColor: progressColor, - minBarHeight: minBarHeight, - spacingRatio: spacingRatio, - heightScale: heightScale, - inverse: widget.inverse, - ), - Builder( - // Just using it for the calculation of the thumb position. - builder: (context) { - final progressWidth = constraints.maxWidth * widget.progress; - return AnimatedPositioned( - curve: const ElasticOutCurve(1.05), - duration: const Duration(milliseconds: 300), - left: progressWidth - _kAudioWaveformSliderThumbWidth / 2, - child: StreamAudioWaveformSliderThumb( - color: thumbColor, - borderColor: thumbBorderColor, - height: constraints.maxHeight, - ), - ); - }, - ), - ], - ), - ), - ); - } -} - -/// {@template streamAudioWaveformSliderThumb} -/// A widget that represents the thumb of the [StreamAudioWaveformSlider]. -/// {@endtemplate} -class StreamAudioWaveformSliderThumb extends StatelessWidget { - /// {@macro streamAudioWaveformSliderThumb} - const StreamAudioWaveformSliderThumb({ - super.key, - this.width = _kAudioWaveformSliderThumbWidth, - this.height = _kAudioWaveformSliderThumbHeight, - this.color = Colors.white, - this.borderColor = const Color(0xffecebeb), - }); - - /// The width of the thumb. - final double width; - - /// The height of the thumb. - final double height; - - /// The color of the thumb. - final Color color; - - /// The border color of the thumb. - final Color borderColor; - - @override - Widget build(BuildContext context) { - return Container( - width: width, - height: height, - decoration: BoxDecoration( - color: color, - border: Border.all( - color: borderColor, - strokeAlign: BorderSide.strokeAlignOutside, - ), - borderRadius: BorderRadius.circular(2), - ), - ); - } -} - -/// {@template streamAudioWaveform} -/// A widget that displays an audio waveform. -/// -/// The waveform is drawn using the [waveform] data. The waveform is drawn -/// horizontally and the bars grow from right to left. -/// {@endtemplate} -class StreamAudioWaveform extends StatelessWidget { - /// {@macro streamAudioWaveform} - const StreamAudioWaveform({ - super.key, - required this.waveform, - this.limit = 100, - this.color, - this.progress = 0, - this.progressColor, - this.minBarHeight, - this.spacingRatio, - this.heightScale, - this.inverse = true, - }); - - /// The waveform data to be drawn. - /// - /// Note: The values should be between 0 and 1. - final List waveform; - - /// The color of the wave bars. - /// - /// Defaults to [StreamAudioWaveformThemeData.color]. - final Color? color; - - /// The number of wave bars that will be draw in the screen. When the length - /// of [waveform] is bigger than [limit] only the X last bars will be shown. - /// - /// Defaults to 100. - final int limit; - - /// The progress of the audio track. Used to show the progress of the audio. - /// - /// Defaults to 0. - final double progress; - - /// The color of the progressed wave bars. - /// - /// Defaults to [StreamAudioWaveformThemeData.progressColor]. - final Color? progressColor; - - /// The minimum height of the bars. - /// - /// Defaults to [StreamAudioWaveformThemeData.minBarHeight]. - final double? minBarHeight; - - /// The ratio of the spacing between the bars. - /// - /// Defaults to [StreamAudioWaveformThemeData.spacingRatio]. - final double? spacingRatio; - - /// The scale of the height of the bars. - /// - /// Defaults to [StreamAudioWaveformThemeData.heightScale]. - final double? heightScale; - - /// If true, the bars grow from right to left otherwise they grow from left - /// to right. - /// - /// Defaults to true. - final bool inverse; - - @override - Widget build(BuildContext context) { - final theme = StreamAudioWaveformTheme.of(context); - - final color = this.color ?? theme.color!; - final progressColor = this.progressColor ?? theme.progressColor!; - final minBarHeight = this.minBarHeight ?? theme.minBarHeight!; - final spacingRatio = this.spacingRatio ?? theme.spacingRatio!; - final heightScale = this.heightScale ?? theme.heightScale!; - - return CustomPaint( - willChange: true, - painter: _WaveformPainter( - waveform: waveform.reversed, - limit: limit, - color: color, - progress: progress, - progressColor: progressColor, - minBarHeight: minBarHeight, - spacingRatio: spacingRatio, - heightScale: heightScale, - inverse: inverse, - ), - ); - } -} - -class _WaveformPainter extends CustomPainter { - _WaveformPainter({ - required Iterable waveform, - this.limit = 100, - this.color = const Color(0xff7E828B), - this.progress = 0, - this.progressColor = const Color(0xff005FFF), - this.minBarHeight = 2, - double spacingRatio = 0.3, - this.heightScale = 1, - this.inverse = true, - }) : waveform = [ - ...waveform.take(limit), - if (waveform.length < limit) - // Fill the remaining bars with 0 value - ...List.filled(limit - waveform.length, 0) - ], - spacingRatio = spacingRatio.clamp(0, 1); - - final List waveform; - final Color color; - final int limit; - final double progress; - final Color progressColor; - final double minBarHeight; - final double spacingRatio; - final bool inverse; - final double heightScale; - - @override - void paint(Canvas canvas, Size size) { - final canvasWidth = size.width; - final canvasHeight = size.height; - - // The total spacing between the bars in the canvas. - final spacingWidth = canvasWidth * spacingRatio; - final barsWidth = canvasWidth - spacingWidth; - final barWidth = barsWidth / limit; - final barSpacing = spacingWidth / (limit - 1); - final progressWidth = progress * canvasWidth; - - void _paintBar(int index, double barValue) { - var dx = index * (barWidth + barSpacing) + barWidth / 2; - if (inverse) dx = canvasWidth - dx; - final dy = canvasHeight / 2; - - final barHeight = math.max(barValue * canvasHeight, minBarHeight); - - final rect = RRect.fromRectAndRadius( - Rect.fromCenter( - center: Offset(dx, dy), - width: barWidth, - height: barHeight, - ), - const Radius.circular(2), - ); - - final waveColor = switch (dx <= progressWidth) { - true => progressColor, - false => color, - }; - - final wavePaint = Paint() - ..color = waveColor - ..strokeCap = StrokeCap.round; - - canvas.drawRRect(rect, wavePaint); - } - - // Paint all the bars - waveform.forEachIndexed(_paintBar); - } - - @override - bool shouldRepaint(covariant _WaveformPainter oldDelegate) => - !const ListEquality().equals(waveform, oldDelegate.waveform) || - color != oldDelegate.color || - limit != oldDelegate.limit || - progress != oldDelegate.progress || - progressColor != oldDelegate.progressColor || - minBarHeight != oldDelegate.minBarHeight || - spacingRatio != oldDelegate.spacingRatio || - heightScale != oldDelegate.heightScale || - inverse != oldDelegate.inverse; -} - -/// {@template horizontalSlider} -/// A widget that allows interactive horizontal sliding gestures. -/// -/// The `HorizontalSlider` widget wraps a child widget and allows users to -/// perform sliding gestures horizontally. It can be configured with callbacks -/// to notify the parent widget about the changes in the horizontal value. -/// {@endtemplate} -class HorizontalSlider extends StatefulWidget { - /// Creates a horizontal slider. - const HorizontalSlider({ - super.key, - required this.child, - required this.onChanged, - this.onChangeStart, - this.onChangeEnd, - }); - - /// The child widget wrapped by the slider. - final Widget child; - - /// Called when the horizontal value starts changing. - final ValueChanged? onChangeStart; - - /// Called when the horizontal value changes. - final ValueChanged? onChanged; - - /// Called when the horizontal value stops changing. - final ValueChanged? onChangeEnd; - - @override - State createState() => _HorizontalSliderState(); -} - -class _HorizontalSliderState extends State { - bool _active = false; - - /// Returns true if the slider is interactive. - bool get isInteractive => widget.onChanged != null; - - /// Converts the visual position to a value based on the text direction. - double _getValueFromVisualPosition(double visualPosition) { - final textDirection = Directionality.of(context); - final value = switch (textDirection) { - TextDirection.rtl => 1.0 - visualPosition, - TextDirection.ltr => visualPosition, - }; - - return clampDouble(value, 0, 1); - } - - /// Converts the local position to a horizontal value. - double _getValueFromLocalPosition(Offset globalPosition) { - final box = context.findRenderObject()! as RenderBox; - final localPosition = box.globalToLocal(globalPosition); - final visualPosition = localPosition.dx / box.size.width; - return _getValueFromVisualPosition(visualPosition); - } - - void _handleDragStart(DragStartDetails details) { - if (!_active && isInteractive) { - _active = true; - final value = _getValueFromLocalPosition(details.globalPosition); - widget.onChangeStart?.call(value); - } - } - - void _handleDragUpdate(DragUpdateDetails details) { - _handleHorizontalDrag(details.globalPosition); - } - - void _handleDragEnd(DragEndDetails details) { - if (!mounted) return; - - if (_active && mounted) { - final value = _getValueFromLocalPosition(details.globalPosition); - widget.onChangeEnd?.call(value); - _active = false; - } - } - - /// Handles the sliding gesture. - void _handleHorizontalDrag(Offset globalPosition) { - if (!mounted) return; - - if (isInteractive) { - final value = _getValueFromLocalPosition(globalPosition); - widget.onChanged?.call(value); - } - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onHorizontalDragStart: _handleDragStart, - onHorizontalDragUpdate: _handleDragUpdate, - onHorizontalDragEnd: _handleDragEnd, - child: widget.child, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/misc/back_button.dart b/packages/stream_chat_flutter/lib/src/misc/back_button.dart deleted file mode 100644 index a589a5921d..0000000000 --- a/packages/stream_chat_flutter/lib/src/misc/back_button.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamBackButton} -/// A custom back button implementation -/// {@endtemplate} -class StreamBackButton extends StatelessWidget { - /// {@macro streamBackButton} - const StreamBackButton({ - super.key, - this.onPressed, - this.showUnreadCount = false, - this.channelId, - }); - - /// Callback for when button is pressed - final VoidCallback? onPressed; - - /// Show unread count - final bool showUnreadCount; - - /// Channel ID used to retrieve unread count - final String? channelId; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - - Widget icon = StreamSvgIcon( - size: 24, - icon: StreamSvgIcons.left, - color: theme.colorTheme.textHighEmphasis, - ); - - if (showUnreadCount) { - icon = Stack( - clipBehavior: Clip.none, - children: [ - icon, - PositionedDirectional( - top: -4, - start: 12, - child: switch (channelId) { - final cid? => StreamUnreadIndicator.channels(cid: cid), - _ => StreamUnreadIndicator(), - }, - ), - ], - ); - } - - return IconButton( - icon: icon, - onPressed: () { - if (onPressed case final onPressed?) { - return onPressed(); - } - - Navigator.maybePop(context); - }, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/misc/connection_status_builder.dart b/packages/stream_chat_flutter/lib/src/misc/connection_status_builder.dart deleted file mode 100644 index 161112ecbe..0000000000 --- a/packages/stream_chat_flutter/lib/src/misc/connection_status_builder.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamConnectionStatusBuilder} -/// A widget that builds itself based on the latest snapshot of interaction with -/// a [Stream] of type [ConnectionStatus]. -/// -/// The widget will use the closest [StreamChatClient.wsConnectionStatusStream] -/// in case no stream is provided. -/// {@endtemplate} -class StreamConnectionStatusBuilder extends StatelessWidget { - /// {@macro streamConnectionStatusBuilder} - const StreamConnectionStatusBuilder({ - super.key, - required this.statusBuilder, - this.connectionStatusStream, - this.errorBuilder, - this.loadingBuilder, - }); - - /// The asynchronous computation to which this builder is currently connected. - final Stream? connectionStatusStream; - - /// The builder that will be used in case of error - final Widget Function(BuildContext context, Object? error)? errorBuilder; - - /// The builder that will be used in case of loading - final WidgetBuilder? loadingBuilder; - - /// The builder that will be used in case of data - final Widget Function(BuildContext context, ConnectionStatus status) - statusBuilder; - - @override - Widget build(BuildContext context) { - final stream = connectionStatusStream ?? - StreamChat.of(context).client.wsConnectionStatusStream; - final client = StreamChat.of(context).client; - return BetterStreamBuilder( - initialData: client.wsConnectionStatus, - stream: stream, - noDataBuilder: loadingBuilder, - errorBuilder: (context, error) { - if (errorBuilder != null) { - return errorBuilder!(context, error); - } - return const Offstage(); - }, - builder: statusBuilder, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/misc/date_divider.dart b/packages/stream_chat_flutter/lib/src/misc/date_divider.dart deleted file mode 100644 index 24c0c9a738..0000000000 --- a/packages/stream_chat_flutter/lib/src/misc/date_divider.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamDateDivider} -/// Shows a date divider depending on the date difference -/// {@endtemplate} -class StreamDateDivider extends StatelessWidget { - /// {@macro streamDateDivider} - const StreamDateDivider({ - super.key, - required this.dateTime, - this.uppercase = false, - }); - - /// [DateTime] to display - final DateTime dateTime; - - /// If text is uppercase - final bool uppercase; - - @override - Widget build(BuildContext context) { - final createdAt = Jiffy.parseFromDateTime(dateTime); - final now = Jiffy.parseFromDateTime(DateTime.now()); - - var dayInfo = createdAt.MMMd; - if (createdAt.isSame(now, unit: Unit.day)) { - dayInfo = context.translations.todayLabel; - } else if (createdAt.isSame(now.subtract(days: 1), unit: Unit.day)) { - dayInfo = context.translations.yesterdayLabel; - } else if (createdAt.isAfter(now.subtract(days: 7), unit: Unit.day)) { - dayInfo = createdAt.EEEE; - } else if (createdAt.isAfter(now.subtract(years: 1), unit: Unit.day)) { - dayInfo = createdAt.MMMd; - } - - if (uppercase) dayInfo = dayInfo.toUpperCase(); - - final chatThemeData = StreamChatTheme.of(context); - return Center( - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 1), - decoration: BoxDecoration( - color: chatThemeData.colorTheme.overlayDark, - borderRadius: BorderRadius.circular(8), - ), - child: Text( - dayInfo, - style: chatThemeData.textTheme.footnote.copyWith( - color: chatThemeData.colorTheme.barsBg, - ), - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/misc/flex_grid.dart b/packages/stream_chat_flutter/lib/src/misc/flex_grid.dart deleted file mode 100644 index 5210cf9aa1..0000000000 --- a/packages/stream_chat_flutter/lib/src/misc/flex_grid.dart +++ /dev/null @@ -1,256 +0,0 @@ -import 'package:flutter/material.dart'; - -/// {@template matrix} -/// A matrix represents a 2D array of integers. -/// {@endtemplate} -typedef Matrix = List>; - -extension on Matrix { - /// Returns the total number of items in the matrix. - int get count => fold(0, (sum, row) => sum + row.length); - - /// Creates a lazy iterable of the [count] first elements of this iterable. - /// - /// The returned `Iterable` may contain fewer than `count` elements, if `this` - /// contains fewer than `count` elements. - Matrix takeItems(int count) { - final matrix = [...this]; - - // Remove items from the end of the matrix until the count is equal to n. - while (matrix.count > count) { - matrix.last.removeLast(); - if (matrix.last.isEmpty) { - matrix.removeLast(); - } - } - - return matrix; - } -} - -/// Signature for a function that builds a widget for the overlay of the last -/// child in the grid in case the number of children exceeds the maximum. -/// -/// The [remaining] parameter represents the number of children that are not -/// displayed in the grid. -typedef OverlayBuilder = Widget Function(BuildContext context, int remaining); - -/// {@template flex_grid} -/// A flexible grid widget that arranges its children based on a provided -/// [pattern]. -/// -/// The [FlexGrid] widget displays a grid of [children] widgets based on a -/// provided [pattern]. Each numeric value in the matrix represents the -/// flex value of the corresponding widget in the grid. The number of widgets -/// must match the number of cells in the matrix. -/// -/// The grid can be configured to have a maximum number of children to display. -/// If the number of children exceeds the maximum, the last child will show the -/// remaining number of children as a count in an overlay. An overlay builder -/// can be provided to customize the overlay for the last child. -/// -/// The direction of the grid can be reversed, with either the column or row as -/// the primary direction. Spacing can be applied between children in the main -/// axis and between the runs (rows or columns) themselves in the cross axis. -/// -/// Example usage: -/// ```dart -/// FlexGrid( -/// pattern: const [ -/// [1, 1], -/// [1, 1], -/// ], -/// children: [ -/// Container(color: Colors.red), -/// Container(color: Colors.blue), -/// Container(color: Colors.green), -/// Container(color: Colors.yellow), -/// ], -/// ) -/// ``` -/// {@endtemplate} -class FlexGrid extends StatelessWidget { - /// {@macro flex_grid} - FlexGrid({ - super.key, - required this.pattern, - required this.children, - this.maxChildren, - this.overlayBuilder, - this.reverse = false, - this.spacing = 2.0, - this.runSpacing = 2.0, - }) : assert( - pattern.count == children.length, - 'The number of children must match the number of cells in the matrix', - ), - assert( - maxChildren == null || maxChildren <= pattern.count, - 'The number of maxChildren must be less than or equal to the number ' - 'of cells in the matrix', - ), - assert( - maxChildren == null || overlayBuilder != null, - 'overlayBuilder must be provided when maxChildren is not null', - ); - - /// The pattern of the grid. - /// - /// Each numeric value in the array represents the flex value of - /// corresponding widget in grid. - /// - /// For example, a grid with 2 rows and 2 columns can be represented as: - /// - /// ```dart - /// [ - /// [1, 1], - /// [1, 1], - /// ] - /// ``` - /// - /// This will create a grid with 4 cells with each cell having a flex value - /// of 1. - final Matrix pattern; - - /// The widgets to display in the grid. - /// - /// The number of widgets must match the number of cells in the matrix. - final List children; - - /// Whether to reverse the direction of the grid. - /// - /// By default, the grid is rendered with column as primary direction and row - /// as secondary direction. - /// - /// If this is set to `true`, the grid will be rendered with row as primary - /// direction and column as secondary direction. - final bool reverse; - - /// The maximum number of children to display in the grid. - /// - /// If this is set, the grid will be rendered with a maximum of [maxChildren] - /// children. If the number of children is greater than [maxChildren], The - /// last child will show the remaining number of children as a count in a - /// overlay. - final int? maxChildren; - - /// The builder to use to build the overlay for the last child in case the - /// number of children is greater than [maxChildren]. - /// - /// The builder will be passed the number of remaining children to display. - final OverlayBuilder? overlayBuilder; - - /// How much space to place between children in a run in the main axis. - /// - /// For example, if [spacing] is 10.0, the children will be spaced at least - /// 10.0 logical pixels apart in the main axis. - /// - /// Defaults to 2.0. - final double spacing; - - /// How much space to place between the runs themselves in the cross axis. - /// - /// For example, if [runSpacing] is 10.0, the runs will be spaced at least - /// 10.0 logical pixels apart in the cross axis. - /// - /// Defaults to 2.0. - final double runSpacing; - - @override - Widget build(BuildContext context) { - // Determine the primary and secondary directions. - final primaryDirection = reverse ? Axis.horizontal : Axis.vertical; - final secondaryDirection = reverse ? Axis.vertical : Axis.horizontal; - - var pattern = [...this.pattern]; - var children = [...this.children]; - - // If the number of children is greater than the maximum number of children, - // remove the extra children. - final maxChildren = this.maxChildren; - if (maxChildren != null && maxChildren < pattern.count) { - children = [...children.take(maxChildren)]; - pattern = [...pattern.takeItems(maxChildren)]; - } - - // Track the current child index. - // - // This is used to determine which child to render next. - var childIndex = 0; - - return Flex( - spacing: spacing, - direction: primaryDirection, - children: [ - for (final row in pattern) - Expanded( - child: Flex( - spacing: runSpacing, - direction: secondaryDirection, - children: [ - ...row.map((flex) { - final isLastChild = childIndex == children.length - 1; - - // Determine the number of remaining children. - final remaining = (this.children.length - 1) - childIndex; - - // If we are at the last child and there are remaining - // children, show the remaining number of children as a - // count in a overlay. - if (isLastChild && remaining > 0) { - return Expanded( - flex: flex, - child: Stack( - fit: StackFit.passthrough, - children: [ - children[childIndex++], - overlayBuilder!.call(context, remaining), - ], - ), - ); - } - - // Otherwise, return the child. - return Expanded( - flex: flex, - child: children[childIndex++], - ); - }), - ], - ), - ), - ], - ); - } -} - -/// {@template gap} -/// A gap widget used to add spacing between children in either the horizontal -/// or vertical direction. -/// {@endtemplate} -class Gap extends StatelessWidget { - /// {@macro gap} - const Gap({ - super.key, - required this.direction, - this.spacing = 0.0, - }); - - /// The direction of the gap. - final Axis direction; - - /// The spacing between children in the gap. - /// - /// Defaults to 0.0. - final double spacing; - - @override - Widget build(BuildContext context) { - switch (direction) { - case Axis.horizontal: - return SizedBox(width: spacing); - case Axis.vertical: - return SizedBox(height: spacing); - } - } -} diff --git a/packages/stream_chat_flutter/lib/src/misc/giphy_chip.dart b/packages/stream_chat_flutter/lib/src/misc/giphy_chip.dart deleted file mode 100644 index c956517ba5..0000000000 --- a/packages/stream_chat_flutter/lib/src/misc/giphy_chip.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/icons/stream_svg_icon.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter/src/utils/extensions.dart'; - -/// {@template giphy_chip} -/// Simple widget which displays a Giphy attribution chip. -/// {@endtemplate} -class GiphyChip extends StatelessWidget { - /// {@macro giphy_chip} - const GiphyChip({super.key}); - - @override - Widget build(BuildContext context) { - final colorTheme = StreamChatTheme.of(context).colorTheme; - return Container( - decoration: BoxDecoration( - color: colorTheme.overlayDark, - borderRadius: BorderRadius.circular(12), - ), - padding: const EdgeInsets.fromLTRB(4, 4, 8, 4), - child: Row( - children: [ - StreamSvgIcon( - size: 16, - icon: StreamSvgIcons.lightning, - color: colorTheme.barsBg, - ), - Text( - context.translations.giphyLabel.toUpperCase(), - style: TextStyle( - color: StreamChatTheme.of(context).colorTheme.barsBg, - fontWeight: FontWeight.bold, - fontSize: 10, - ), - ), - ], - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/misc/gradient_box_border.dart b/packages/stream_chat_flutter/lib/src/misc/gradient_box_border.dart deleted file mode 100644 index 7a881edd16..0000000000 --- a/packages/stream_chat_flutter/lib/src/misc/gradient_box_border.dart +++ /dev/null @@ -1,334 +0,0 @@ -import 'dart:math' as math; -import 'dart:ui' as ui show lerpDouble; - -import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; - -const _kDefaultGradient = LinearGradient(colors: [ - Color(0xFF000000), - Color(0xFF000000), -]); - -const _kTransparentGradient = LinearGradient(colors: [ - Color(0x00000000), - Color(0x00000000), -]); - -/// {@template gradientBoxBorder} -/// A border that draws a gradient instead of a solid color. -/// {@endtemplate} -class GradientBoxBorder extends BoxBorder { - /// {@macro gradientBoxBorder} - const GradientBoxBorder({ - this.gradient = _kDefaultGradient, - this.width = 1.0, - this.style = BorderStyle.solid, - this.strokeAlign = strokeAlignInside, - }) : assert(width >= 0.0, 'The width must be greater than or equal to zero.'); - - /// A hairline default gradient border that is not rendered. - static const GradientBoxBorder none = GradientBoxBorder( - width: 0, - style: BorderStyle.none, - ); - - /// The gradient to use in the border. - final Gradient gradient; - - /// The width of the border, in logical pixels. - /// - /// Setting width to 0.0 will result in a hairline border. This means that - /// the border will have the width of one physical pixel. Hairline - /// rendering takes shortcuts when the path overlaps a pixel more than once. - /// This means that it will render faster than otherwise, but it might - /// double-hit pixels, giving it a slightly darker/lighter result. - /// - /// To omit the border entirely, set the [style] to [BorderStyle.none]. - final double width; - - /// The style of the border. - /// - /// To omit a side, set [style] to [BorderStyle.none]. This skips - /// painting the border, but the border still has a [width]. - final BorderStyle style; - - /// The relative position of the stroke on a [BorderSide] in an - /// [OutlinedBorder] or [Border]. - /// - /// Values typically range from -1.0 ([strokeAlignInside], inside border, - /// default) to 1.0 ([strokeAlignOutside], outside border), without any - /// bound constraints (e.g., a value of -2.0 is not typical, but allowed). - /// A value of 0 ([strokeAlignCenter]) will center the border on the edge - /// of the widget. - /// - /// When set to [strokeAlignInside], the stroke is drawn completely inside - /// the widget. For [strokeAlignCenter] and [strokeAlignOutside], a property - /// such as [Container.clipBehavior] can be used in an outside widget to clip - /// it. If [Container.decoration] has a border, the container may incorporate - /// [width] as additional padding: - /// - [strokeAlignInside] provides padding with full [width]. - /// - [strokeAlignCenter] provides padding with half [width]. - /// - [strokeAlignOutside] provides zero padding, as stroke is drawn entirely - /// outside. - /// - /// This property is not honored by [toPaint] (because the [Paint] object - /// cannot represent it); it is intended that classes that use [BorderSide] - /// objects implement this property when painting borders by suitably - /// inflating or deflating their regions. - /// - /// {@tool dartpad} - /// This example shows an animation of how [strokeAlign] affects the drawing - /// when applied to borders of various shapes. - /// - /// ** See code in examples/api/lib/painting/borders/border_side.stroke_align.0.dart ** - /// {@end-tool} - final double strokeAlign; - - /// The border is drawn fully inside of the border path. - /// - /// This is a constant for use with [strokeAlign]. - /// - /// This is the default value for [strokeAlign]. - static const double strokeAlignInside = -1; - - /// The border is drawn on the center of the border path, with half of the - /// [BorderSide.width] on the inside, and the other half on the outside of - /// the path. - /// - /// This is a constant for use with [strokeAlign]. - static const double strokeAlignCenter = 0; - - /// The border is drawn on the outside of the border path. - /// - /// This is a constant for use with [strokeAlign]. - static const double strokeAlignOutside = 1; - - /// Whether the two given [GradientBoxBorder]s can be merged using - /// [GradientBoxBorder.merge]. - /// - /// Two sides can be merged if one or both are zero-width with - /// [GradientBoxBorder.none], or if they both have the same gradient and style - bool canMerge(GradientBoxBorder b) { - if ((style == BorderStyle.none && width == 0.0) || - (b.style == BorderStyle.none && b.width == 0.0)) { - return true; - } - return style == b.style && gradient == b.gradient; - } - - /// Creates a [GradientBoxBorder] that represents the addition of the two - /// given [GradientBoxBorder]s. - /// - /// It is only valid to call this if [canMerge] returns true for the two - /// borders. - /// - /// If one of the border is zero-width with [BorderStyle.none], then the other - /// border is return as-is. If both of the border are zero-width with - /// [BorderStyle.none], then [GradientBoxBorder.none] is returned. - GradientBoxBorder merge(GradientBoxBorder b) { - assert(canMerge(b), ''); - final aIsNone = style == BorderStyle.none && width == 0.0; - final bIsNone = b.style == BorderStyle.none && b.width == 0.0; - if (aIsNone && bIsNone) return GradientBoxBorder.none; - if (aIsNone) return b; - if (bIsNone) return this; - - assert(gradient == b.gradient, ''); - assert(style == b.style, ''); - return GradientBoxBorder( - gradient: gradient, // == b.gradient - width: width + b.width, - strokeAlign: math.max(strokeAlign, b.strokeAlign), - style: style, // == b.style - ); - } - - @override - GradientBoxBorder scale(double t) { - return GradientBoxBorder( - gradient: gradient, - width: math.max(0, width * t), - style: t <= 0.0 ? BorderStyle.none : style, - ); - } - - @override - bool get isUniform => true; - - @override - BorderSide get bottom => BorderSide.none; - - @override - BorderSide get top => BorderSide.none; - - /// Get the amount of the stroke width that lies inside of the [BorderSide]. - /// - /// For example, this will return the [width] for a [strokeAlign] of -1, half - /// the [width] for a [strokeAlign] of 0, and 0 for a [strokeAlign] of 1. - double get strokeInset => width * (1 - (1 + strokeAlign) / 2); - - /// Get the amount of the stroke width that lies outside of the [BorderSide]. - /// - /// For example, this will return 0 for a [strokeAlign] of -1, half the - /// [width] for a [strokeAlign] of 0, and the [width] for a [strokeAlign] - /// of 1. - double get strokeOutset => width * (1 + strokeAlign) / 2; - - /// The offset of the stroke, taking into account the stroke alignment. - /// - /// For example, this will return the negative [width] of the stroke - /// for a [strokeAlign] of -1, 0 for a [strokeAlign] of 0, and the - /// [width] for a [strokeAlign] of -1. - double get strokeOffset => width * strokeAlign; - - @override - EdgeInsetsGeometry get dimensions => EdgeInsets.all(strokeInset); - - @override - GradientBoxBorder? add(ShapeBorder other, {bool reversed = false}) { - if (other is GradientBoxBorder && canMerge(other)) return merge(other); - return null; - } - - @override - ShapeBorder? lerpFrom(ShapeBorder? a, double t) { - if (a is GradientBoxBorder) { - return GradientBoxBorder.lerp(a, this, t); - } - return super.lerpFrom(a, t); - } - - @override - ShapeBorder? lerpTo(ShapeBorder? b, double t) { - if (b is GradientBoxBorder) { - return GradientBoxBorder.lerp(this, b, t); - } - return super.lerpTo(b, t); - } - - /// Linearly interpolate between two gradient borders. - /// - /// {@macro dart.ui.shadow.lerp} - static GradientBoxBorder? lerp( - GradientBoxBorder? a, GradientBoxBorder? b, double t) { - if (identical(a, b)) return a; - if (a == null) return b!.scale(t); - if (b == null) return a.scale(1.0 - t); - - final width = ui.lerpDouble(a.width, b.width, t)!; - if (width < 0.0) return GradientBoxBorder.none; - - if (a.style == b.style && a.strokeAlign == b.strokeAlign) { - return GradientBoxBorder( - gradient: Gradient.lerp(a.gradient, b.gradient, t)!, - width: width, - style: a.style, // == b.style - strokeAlign: a.strokeAlign, // == b.strokeAlign - ); - } - - final gradientA = switch (a.style) { - BorderStyle.solid => a.gradient, - BorderStyle.none => _kTransparentGradient, - }; - - final gradientB = switch (b.style) { - BorderStyle.solid => b.gradient, - BorderStyle.none => _kTransparentGradient, - }; - - if (a.strokeAlign != b.strokeAlign) { - return GradientBoxBorder( - gradient: Gradient.lerp(gradientA, gradientB, t)!, - width: width, - strokeAlign: ui.lerpDouble(a.strokeAlign, b.strokeAlign, t)!, - ); - } - - return GradientBoxBorder( - gradient: Gradient.lerp(gradientA, gradientB, t)!, - width: width, - strokeAlign: a.strokeAlign, // == b.strokeAlign - ); - } - - @override - void paint( - Canvas canvas, - Rect rect, { - TextDirection? textDirection, - BoxShape shape = BoxShape.rectangle, - BorderRadius? borderRadius, - }) { - assert( - shape != BoxShape.circle || borderRadius == null, - 'A borderRadius can only be given for rectangular boxes.', - ); - - if (style == BorderStyle.none) return; - - return switch (shape) { - BoxShape.circle => _paintUniformBorderWithCircle(canvas, rect), - BoxShape.rectangle => switch (borderRadius) { - final radius? when radius != BorderRadius.zero => - _paintUniformBorderWithRadius(canvas, rect, radius), - _ => _paintUniformBorderWithRectangle(canvas, rect), - }, - }; - } - - void _paintUniformBorderWithRadius( - Canvas canvas, - Rect rect, - BorderRadius borderRadius, - ) { - assert(style != BorderStyle.none, ''); - - if (width == 0) { - return canvas.drawRRect(borderRadius.toRRect(rect), _getPaint(rect)); - } - - final borderRect = borderRadius.toRRect(rect); - final inner = borderRect.deflate(strokeInset); - final outer = borderRect.inflate(strokeOutset); - canvas.drawDRRect(outer, inner, _getPaint(rect)); - } - - void _paintUniformBorderWithRectangle(Canvas canvas, Rect rect) { - assert(style != BorderStyle.none, ''); - return canvas.drawRect(rect.inflate(strokeOffset / 2), _getPaint(rect)); - } - - void _paintUniformBorderWithCircle(Canvas canvas, Rect rect) { - assert(style != BorderStyle.none, ''); - final radius = (rect.shortestSide + strokeOffset) / 2; - canvas.drawCircle(rect.center, radius, _getPaint(rect)); - } - - Paint _getPaint(Rect rect) { - return switch (style) { - BorderStyle.solid => Paint() - ..strokeWidth = width - ..style = PaintingStyle.stroke - ..shader = gradient.createShader(rect), - BorderStyle.none => Paint() - ..strokeWidth = 0.0 - ..style = PaintingStyle.stroke - ..shader = _kTransparentGradient.createShader(rect), - }; - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - if (other.runtimeType != runtimeType) return false; - return other is GradientBoxBorder && - other.gradient == gradient && - other.width == width && - other.style == style && - other.strokeAlign == strokeAlign; - } - - @override - int get hashCode => Object.hash(gradient, width, style, strokeAlign); -} diff --git a/packages/stream_chat_flutter/lib/src/misc/info_tile.dart b/packages/stream_chat_flutter/lib/src/misc/info_tile.dart deleted file mode 100644 index fd6106491a..0000000000 --- a/packages/stream_chat_flutter/lib/src/misc/info_tile.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_portal/flutter_portal.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -/// {@template streamInfoTile} -/// Displays a message. Often used to display connection status. -/// {@endtemplate} -class StreamInfoTile extends StatelessWidget { - /// {@macro streamInfoTile} - const StreamInfoTile({ - super.key, - required this.message, - required this.child, - required this.showMessage, - this.tileAnchor, - this.childAnchor, - this.textStyle, - this.backgroundColor, - }); - - /// String to display - final String message; - - /// Widget to display over - final Widget child; - - /// Flag to show message - final bool showMessage; - - /// Anchor for tile - [portalAnchor] for [PortalEntry] - final Alignment? tileAnchor; - - /// Alignment for child - [childAnchor] for [PortalEntry] - final Alignment? childAnchor; - - /// [TextStyle] for message - final TextStyle? textStyle; - - /// Background color for tile - final Color? backgroundColor; - - @override - Widget build(BuildContext context) { - final chatThemeData = StreamChatTheme.of(context); - return PortalTarget( - visible: showMessage, - anchor: Aligned( - follower: tileAnchor ?? Alignment.topCenter, - target: childAnchor ?? Alignment.bottomCenter, - ), - portalFollower: Container( - height: 25, - color: backgroundColor ?? - // ignore: deprecated_member_use - chatThemeData.colorTheme.textLowEmphasis.withOpacity(0.9), - child: Center( - child: Text( - message, - style: textStyle ?? - chatThemeData.textTheme.body.copyWith( - color: Colors.white, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - ), - child: child, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/misc/markdown_message.dart b/packages/stream_chat_flutter/lib/src/misc/markdown_message.dart deleted file mode 100644 index 9b0926ed08..0000000000 --- a/packages/stream_chat_flutter/lib/src/misc/markdown_message.dart +++ /dev/null @@ -1,108 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_markdown/flutter_markdown.dart'; -import 'package:stream_chat_flutter/src/ai_assistant/streaming_message_view.dart'; -import 'package:stream_chat_flutter/src/theme/message_theme.dart'; - -import 'package:stream_chat_flutter/src/utils/device_segmentation.dart'; - -/// {@template streamMarkdownMessage} -/// A widget that displays a markdown message. This widget uses the markdown -/// package to parse the markdown data and display it. -/// -/// This widget is used by [StreamMessageText] and [StreamingMessageView] to -/// display the message text. -/// {@endtemplate} -class StreamMarkdownMessage extends StatelessWidget { - /// {@macro streamMarkdownMessage} - const StreamMarkdownMessage({ - super.key, - required this.data, - this.selectable, - this.onTapLink, - this.messageTheme, - this.styleSheet, - this.syntaxHighlighter, - this.builders = const {}, - this.paddingBuilders = const {}, - }); - - /// The markdown data to display. - final String data; - - /// Whether the text is selectable. - final bool? selectable; - - /// Called when the user taps a link. - final MarkdownTapLinkCallback? onTapLink; - - /// The theme to apply to the message text. - final StreamMessageThemeData? messageTheme; - - /// Optional style sheet to customize the markdown output. - /// - /// When provided, it will be merged with the default one. - final MarkdownStyleSheet? styleSheet; - - /// The syntax highlighter used to color text in `pre` elements. - /// - /// If null, the [MarkdownStyleSheet.code] style is used for `pre` elements. - final SyntaxHighlighter? syntaxHighlighter; - - /// Render certain tags, usually used with [extensionSet] - /// - /// For example, we will add support for `sub` tag: - /// - /// ```dart - /// builders: { - /// 'sub': SubscriptBuilder(), - /// } - /// ``` - /// - /// The `SubscriptBuilder` is a subclass of [MarkdownElementBuilder]. - final Map builders; - - /// Add padding for different tags (use only for block elements and img) - /// - /// For example, we will add padding for `img` tag: - /// - /// ```dart - /// paddingBuilders: { - /// 'img': ImgPaddingBuilder(), - /// } - /// ``` - /// - /// The `ImgPaddingBuilder` is a subclass of [MarkdownPaddingBuilder]. - final Map paddingBuilders; - - @override - Widget build(BuildContext context) { - final themeData = Theme.of(context); - - return MarkdownBody( - data: data, - selectable: selectable ?? isDesktopDeviceOrWeb, - onTapText: () {}, - onSelectionChanged: (val, selection, cause) {}, - onTapLink: onTapLink, - syntaxHighlighter: syntaxHighlighter, - builders: builders, - paddingBuilders: paddingBuilders, - styleSheet: MarkdownStyleSheet.fromTheme( - themeData.copyWith( - textTheme: themeData.textTheme.apply( - bodyColor: messageTheme?.messageTextStyle?.color, - decoration: messageTheme?.messageTextStyle?.decoration, - decorationColor: messageTheme?.messageTextStyle?.decorationColor, - decorationStyle: messageTheme?.messageTextStyle?.decorationStyle, - fontFamily: messageTheme?.messageTextStyle?.fontFamily, - ), - ), - ) - .copyWith( - a: messageTheme?.messageLinksStyle, - p: messageTheme?.messageTextStyle, - ) - .merge(styleSheet), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/misc/option_list_tile.dart b/packages/stream_chat_flutter/lib/src/misc/option_list_tile.dart deleted file mode 100644 index ddb1db768d..0000000000 --- a/packages/stream_chat_flutter/lib/src/misc/option_list_tile.dart +++ /dev/null @@ -1,96 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -/// {@template streamOptionListTile} -/// List tile for [ChannelBottomSheet] -/// {@endtemplate} -class StreamOptionListTile extends StatelessWidget { - /// {@macro streamOptionListTile} - const StreamOptionListTile({ - super.key, - required this.title, - this.leading, - this.trailing, - this.onTap, - this.titleColor, - this.tileColor, - this.separatorColor, - this.titleTextStyle, - }); - - /// Title for tile - final String title; - - /// Leading widget (start) - final Widget? leading; - - /// Trailing widget (end) - final Widget? trailing; - - /// The action to perform when the tile is tapped - final VoidCallback? onTap; - - /// Title color - final Color? titleColor; - - /// Background tile color - final Color? tileColor; - - /// Separator color - final Color? separatorColor; - - /// [TextStyle] to apply to [title] - final TextStyle? titleTextStyle; - - @override - Widget build(BuildContext context) { - final chatThemeData = StreamChatTheme.of(context); - return Column( - children: [ - Container( - height: 1, - color: separatorColor ?? chatThemeData.colorTheme.disabled, - ), - Material( - color: tileColor ?? chatThemeData.colorTheme.barsBg, - child: SizedBox( - height: 63, - child: InkWell( - onTap: onTap, - child: Row( - children: [ - if (leading != null) - Center(child: leading) - else - const SizedBox(width: 16), - Expanded( - flex: 4, - child: Text( - title, - style: titleTextStyle ?? - (titleColor == null - ? chatThemeData.textTheme.bodyBold - : chatThemeData.textTheme.bodyBold.copyWith( - color: titleColor, - )), - ), - ), - Expanded( - flex: 2, - child: Padding( - padding: const EdgeInsets.only(right: 16), - child: Align( - alignment: Alignment.centerRight, - child: trailing ?? Container(), - ), - ), - ), - ], - ), - ), - ), - ), - ], - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/misc/reaction_icon.dart b/packages/stream_chat_flutter/lib/src/misc/reaction_icon.dart deleted file mode 100644 index 60ac8fe9c2..0000000000 --- a/packages/stream_chat_flutter/lib/src/misc/reaction_icon.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:flutter/material.dart'; - -/// {@template reactionIconBuilder} -/// Signature for a function that builds a reaction icon. -/// {@endtemplate} -typedef ReactionIconBuilder = Widget Function( - BuildContext context, - // ignore: avoid_positional_boolean_parameters - bool isHighlighted, - double iconSize, -); - -/// {@template streamReactionIcon} -/// Reaction icon data -/// {@endtemplate} -class StreamReactionIcon { - /// {@macro streamReactionIcon} - const StreamReactionIcon({ - required this.type, - required this.builder, - }); - - /// Type of reaction - final String type; - - /// {@macro reactionIconBuilder} - final ReactionIconBuilder builder; -} diff --git a/packages/stream_chat_flutter/lib/src/misc/separated_reorderable_list_view.dart b/packages/stream_chat_flutter/lib/src/misc/separated_reorderable_list_view.dart deleted file mode 100644 index 054c1de520..0000000000 --- a/packages/stream_chat_flutter/lib/src/misc/separated_reorderable_list_view.dart +++ /dev/null @@ -1,83 +0,0 @@ -// ignore_for_file: use_is_even_rather_than_modulo -import 'dart:math' as math; -import 'package:flutter/material.dart'; - -/// {@template streamSeparatedReorderableListView} -/// A custom implementation of [ReorderableListView] that supports separators. -/// {@endtemplate} -class SeparatedReorderableListView extends ReorderableListView { - /// {@macro streamSeparatedReorderableListView} - SeparatedReorderableListView({ - super.key, - required IndexedWidgetBuilder itemBuilder, - required IndexedWidgetBuilder separatorBuilder, - required int itemCount, - required ReorderCallback onReorder, - super.itemExtent, - super.prototypeItem, - super.proxyDecorator, - super.padding, - super.header, - super.scrollDirection, - super.reverse, - super.scrollController, - super.primary, - super.physics, - super.shrinkWrap, - super.anchor, - super.cacheExtent, - super.dragStartBehavior, - super.keyboardDismissBehavior, - super.restorationId, - super.clipBehavior, - }) : super.builder( - buildDefaultDragHandles: false, - itemCount: math.max(0, itemCount * 2 - 1), - itemBuilder: (BuildContext context, int index) { - final itemIndex = index ~/ 2; - if (index.isEven) { - final listItem = itemBuilder(context, itemIndex); - return ReorderableDelayedDragStartListener( - key: listItem.key, - index: index, - child: listItem, - ); - } - - final separator = separatorBuilder(context, itemIndex); - if (separator.key == null) { - return KeyedSubtree( - key: ValueKey('reorderable_separator_$itemIndex'), - child: IgnorePointer(child: separator), - ); - } - - return separator; - }, - onReorder: (int oldIndex, int newIndex) { - // Adjust the indexes due to an issue in the ReorderableListView - // which isn't going to be fixed in the near future. - // - // issue: https://github.com/flutter/flutter/issues/24786 - if (newIndex > oldIndex) { - newIndex -= 1; - } - - // Ideally should never happen as separators are wrapped in the - // IgnorePointer widget. This is just a safety check. - if (oldIndex % 2 == 1) return; - - // The item moved behind the top/bottom separator we should not - // reorder it. - if ((oldIndex - newIndex).abs() == 1) return; - - // Calculate the updated indexes - final updatedOldIndex = oldIndex ~/ 2; - final updatedNewIndex = oldIndex > newIndex && newIndex % 2 == 1 - ? (newIndex + 1) ~/ 2 - : newIndex ~/ 2; - - onReorder(updatedOldIndex, updatedNewIndex); - }, - ); -} diff --git a/packages/stream_chat_flutter/lib/src/misc/stream_neumorphic_button.dart b/packages/stream_chat_flutter/lib/src/misc/stream_neumorphic_button.dart deleted file mode 100644 index 0c6ca1580c..0000000000 --- a/packages/stream_chat_flutter/lib/src/misc/stream_neumorphic_button.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:flutter/material.dart'; - -/// {@template neumorphicButton} -/// Neumorphic button -/// {@endtemplate} -class StreamNeumorphicButton extends StatelessWidget { - /// {@macro neumorphicButton} - const StreamNeumorphicButton({ - super.key, - required this.child, - this.backgroundColor = Colors.white, - }); - - /// Child contained in the button - final Widget child; - - /// Background color of the button - final Color backgroundColor; - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.all(8), - height: 40, - width: 40, - decoration: BoxDecoration( - color: backgroundColor, - shape: BoxShape.circle, - boxShadow: [ - BoxShadow( - color: Colors.grey.shade700, - offset: const Offset(0, 1), - blurRadius: 0.5, - ), - const BoxShadow( - color: Colors.white, - blurRadius: 0.5, - ), - ], - ), - child: child, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/misc/swipeable.dart b/packages/stream_chat_flutter/lib/src/misc/swipeable.dart deleted file mode 100644 index 45b9b2dceb..0000000000 --- a/packages/stream_chat_flutter/lib/src/misc/swipeable.dart +++ /dev/null @@ -1,464 +0,0 @@ -import 'dart:math' as math; - -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; - -/// Signature used by [swipeable] to indicate that it has been swiped in -/// the given `direction`. -/// -/// Used by [Swipeable.onSwiped]. -typedef SwipeDirectionCallback = void Function(SwipeDirection direction); - -/// Signature for a function that builds a widget given the progress of the -/// dismissing action. -/// -/// Used by [Swipeable.backgroundBuilder]. -typedef BackgroundWidgetBuilder = Widget Function( - BuildContext context, - SwipeUpdateDetails details, -); - -/// The direction in which a [Swipeable] can be swiped. -enum SwipeDirection { - /// The [Swipeable] can be swiped by dragging either left or right. - horizontal, - - /// The [Swipeable] can be swiped by dragging in the reverse of the - /// reading direction (e.g., from right to left in left-to-right languages). - endToStart, - - /// The [Swipeable] can be swiped by dragging in the reading direction - /// (e.g., from left to right in left-to-right languages). - startToEnd, - - /// The [Swipeable] cannot be swiped by dragging. - none -} - -/// A widget that can be swiped in a specified direction. -/// -/// The `Swipeable` widget allows its child to be swiped by the user in a -/// specified direction. -/// -/// It provides options for customizing the swipe behavior, including the -/// ability to specify a background widget that appears during the swipe, -/// callbacks for handling swipe completion, and more. -/// -/// Example usage: -/// ```dart -/// Swipeable( -/// child: Container( -/// height: 100, -/// width: 200, -/// color: Colors.blue, -/// child: Center( -/// child: Text('Swipe me'), -/// ), -/// ), -/// backgroundBuilder: (context, details) { -/// final direction = details.direction; -/// return Container( -/// color: Colors.red, -/// child: Center( -/// child: Text( -/// direction == SwipeDirection.left ? 'Swipe left' : 'Swipe right', -/// style: TextStyle( -/// color: Colors.white, -/// fontWeight: FontWeight.bold, -/// ), -/// ), -/// ), -/// ); -/// }, -/// onSwiped: (direction) { -/// if (direction == SwipeDirection.left) { -/// // Handle left swipe -/// } else if (direction == SwipeDirection.right) { -/// // Handle right swipe -/// } -/// }, -/// direction: SwipeDirection.horizontal, -/// swipeThreshold: 0.4, -/// movementDuration: Duration(milliseconds: 200), -/// ) -/// ``` -class Swipeable extends StatefulWidget { - /// Creates a widget that can be swiped . - const Swipeable({ - required super.key, - required this.child, - this.backgroundBuilder, - this.onSwiped, - this.direction = SwipeDirection.horizontal, - this.swipeThreshold = 0.4, - this.movementDuration = const Duration(milliseconds: 200), - this.dragStartBehavior = DragStartBehavior.start, - this.behavior = HitTestBehavior.opaque, - }) : assert( - swipeThreshold >= 0.0 && swipeThreshold <= 1.0, - 'swipeThreshold must be between 0.0 and 1.0', - ); - - /// The widget below this widget in the tree. - /// - /// {@macro flutter.widgets.ProxyWidget.child} - final Widget child; - - /// A widget that is stacked behind the child. If secondaryBackground is also - /// specified then this widget only appears when the child has been dragged - /// down or to the right. - final BackgroundWidgetBuilder? backgroundBuilder; - - /// Called when the widget has been successfully swiped based on the - /// [direction] and [swipeThreshold]. - final SwipeDirectionCallback? onSwiped; - - /// The direction in which the widget can be swiped. - final SwipeDirection direction; - - /// The offset threshold the item has to be dragged in order to be considered - /// swiped. - /// - /// Represented as a fraction, e.g. if it is 0.4 (the default), then the item - /// has to be dragged at least 40% towards one direction to be considered - /// swiped. - /// - /// See also: - /// - /// * [direction], which controls the directions in which the items can - /// be swiped. - final double swipeThreshold; - - /// Defines the duration for card to come back to original position. - final Duration movementDuration; - - /// Determines the way that drag start behavior is handled. - /// - /// If set to [DragStartBehavior.start], the drag gesture used to dismiss a - /// swipeable will begin at the position where the drag gesture won the - /// arena. - /// - /// If set to [DragStartBehavior.down] it will begin at the position where - /// a down event is first detected. - /// - /// In general, setting this to [DragStartBehavior.start] will make drag - /// animation smoother and setting it to [DragStartBehavior.down] will make - /// drag behavior feel slightly more reactive. - /// - /// By default, the drag start behavior is [DragStartBehavior.start]. - /// - /// See also: - /// - /// * [DragGestureRecognizer.dragStartBehavior], which gives an example for - /// the different behaviors. - final DragStartBehavior dragStartBehavior; - - /// How to behave during hit tests. - /// - /// This defaults to [HitTestBehavior.opaque]. - final HitTestBehavior behavior; - - @override - _SwipeableState createState() => _SwipeableState(); -} - -/// Details for [DismissUpdateCallback]. -/// -/// See also: -/// -/// * [swipeable.onUpdate], which receives this information. -class SwipeUpdateDetails { - /// Create a new instance of [SwipeUpdateDetails]. - SwipeUpdateDetails({ - this.direction = SwipeDirection.horizontal, - this.reached = false, - this.previousReached = false, - this.progress = 0.0, - }); - - /// The direction that the swipeable is being dragged. - final SwipeDirection direction; - - /// Whether the swipe threshold is currently reached. - final bool reached; - - /// Whether the swipe threshold was reached the last time this callback was - /// invoked. - /// - /// This can be used in conjunction with [SwipeUpdateDetails.reached] to catch - /// the moment that the [Swipeable] is dragged across the threshold. - final bool previousReached; - - /// The offset ratio of the swipeable in its parent container. - /// - /// A value of 0.0 represents the normal position and 1.0 means the child is - /// completely outside its parent. - /// - /// This can be used to synchronize other elements to what the swipeable is - /// doing on screen, e.g. using this value to set the opacity thereby fading - /// swipeable as it's dragged offscreen. - final double progress; -} - -class _SwipeableClipper extends CustomClipper { - _SwipeableClipper({ - required this.moveAnimation, - }) : super(reclip: moveAnimation); - - final Animation moveAnimation; - - @override - Rect getClip(Size size) { - final offset = moveAnimation.value.dx * size.width; - if (offset < 0) { - return Rect.fromLTRB(size.width + offset, 0, size.width, size.height); - } - return Rect.fromLTRB(0, 0, offset, size.height); - } - - @override - Rect getApproximateClipRect(Size size) => getClip(size); - - @override - bool shouldReclip(_SwipeableClipper oldClipper) { - return oldClipper.moveAnimation.value != moveAnimation.value; - } -} - -class _SwipeableState extends State - with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin { - @override - void initState() { - super.initState(); - _moveController = AnimationController( - duration: widget.movementDuration, - vsync: this, - )..addStatusListener((_) => updateKeepAlive()); - _updateMoveAnimation(); - } - - AnimationController? _moveController; - late Animation _moveAnimation; - - double _dragExtent = 0; - bool _dragUnderway = false; - bool _swipeThresholdReached = false; - - final GlobalKey _contentKey = GlobalKey(); - - @override - bool get wantKeepAlive => _moveController?.isAnimating ?? false; - - @override - void dispose() { - _moveController?.dispose(); - super.dispose(); - } - - SwipeDirection _extentToDirection(double extent) { - if (extent == 0.0) { - return SwipeDirection.none; - } - switch (Directionality.of(context)) { - case TextDirection.rtl: - return extent < 0 - ? SwipeDirection.startToEnd - : SwipeDirection.endToStart; - case TextDirection.ltr: - return extent > 0 - ? SwipeDirection.startToEnd - : SwipeDirection.endToStart; - } - } - - SwipeDirection get _swipeDirection => _extentToDirection(_dragExtent); - - bool get _isActive => _dragUnderway || _moveController!.isAnimating; - - double get _overallDragAxisExtent => context.size!.width; - - void _handleDragStart(DragStartDetails details) { - _dragUnderway = true; - if (_moveController!.isAnimating) { - _dragExtent = - _moveController!.value * _overallDragAxisExtent * _dragExtent.sign; - _moveController!.stop(); - } else { - _dragExtent = 0.0; - _moveController!.value = 0.0; - } - setState(_updateMoveAnimation); - } - - void _handleDragUpdate(DragUpdateDetails details) { - if (!_isActive || _moveController!.isAnimating) { - return; - } - - final delta = details.primaryDelta!; - final oldDragExtent = _dragExtent; - switch (widget.direction) { - case SwipeDirection.horizontal: - _dragExtent += delta; - break; - case SwipeDirection.endToStart: - switch (Directionality.of(context)) { - case TextDirection.rtl: - if (_dragExtent + delta > 0) { - _dragExtent += delta; - } - break; - case TextDirection.ltr: - if (_dragExtent + delta < 0) { - _dragExtent += delta; - } - break; - } - break; - case SwipeDirection.startToEnd: - switch (Directionality.of(context)) { - case TextDirection.rtl: - if (_dragExtent + delta < 0) { - _dragExtent += delta; - } - break; - case TextDirection.ltr: - if (_dragExtent + delta > 0) { - _dragExtent += delta; - } - break; - } - break; - case SwipeDirection.none: - _dragExtent = 0; - break; - } - - if (oldDragExtent.sign != _dragExtent.sign) { - setState(_updateMoveAnimation); - } - - if (!_moveController!.isAnimating) { - final currentDragExtent = _dragExtent.abs(); - final overallDragExtent = _overallDragAxisExtent; - final movePastThresholdExtent = widget.swipeThreshold * overallDragExtent; - - final double newPos; - if (currentDragExtent > movePastThresholdExtent) { - // How many "thresholds" past the threshold we are. - final n = currentDragExtent / movePastThresholdExtent; - - // Take the number of thresholds past the threshold, and reduce it by - // the threshold amount, then normalize it to the drag extents. - final reducedThreshold = math.pow(n, 0.3); - final adjustedDragExtent = movePastThresholdExtent * reducedThreshold; - - newPos = adjustedDragExtent / overallDragExtent; - } else { - newPos = currentDragExtent / overallDragExtent; - } - - _moveController!.value = newPos; - } - } - - SwipeUpdateDetails _currentSwipeUpdateDetails() { - final oldSwipeThresholdReached = _swipeThresholdReached; - _swipeThresholdReached = _moveController!.value > widget.swipeThreshold; - - return SwipeUpdateDetails( - direction: _swipeDirection, - reached: _swipeThresholdReached, - previousReached: oldSwipeThresholdReached, - progress: _moveController!.value, - ); - } - - void _updateMoveAnimation() { - final end = _dragExtent.sign; - _moveAnimation = _moveController!.drive( - Tween( - begin: Offset.zero, - end: Offset(end, 0), - ), - ); - } - - void _handleDragEnd(DragEndDetails details) { - if (!_isActive || _moveController!.isAnimating) return; - _dragUnderway = false; - - // Once dragging ends, animate back to the initial offset. - _moveController!.reverse(); - - // If the threshold was reached, report it. - if (_moveController!.value > widget.swipeThreshold) { - if (widget.onSwiped != null) { - final direction = _swipeDirection; - widget.onSwiped!(direction); - } - } - } - - @override - Widget build(BuildContext context) { - super.build(context); // See AutomaticKeepAliveClientMixin. - - assert( - debugCheckHasDirectionality(context), - 'Swipeable must be inside of a Directionality widget.', - ); - - Widget? background; - final backgroundBuilder = widget.backgroundBuilder; - if (backgroundBuilder != null) { - background = AnimatedBuilder( - animation: _moveAnimation, - builder: (context, _) { - final updateDetails = _currentSwipeUpdateDetails(); - return backgroundBuilder.call(context, updateDetails); - }, - ); - } - - Widget content = SlideTransition( - position: _moveAnimation, - child: KeyedSubtree(key: _contentKey, child: widget.child), - ); - - if (background != null) { - content = Stack( - fit: StackFit.passthrough, - alignment: Alignment.center, - children: [ - if (!_moveAnimation.isDismissed) - Positioned.fill( - child: ClipRect( - clipper: _SwipeableClipper( - moveAnimation: _moveAnimation, - ), - child: background, - ), - ), - content, - ], - ); - } - - // If the SwipeDirection is none, we do not add drag gestures because the - // content cannot be dragged. - if (widget.direction == SwipeDirection.none) { - return content; - } - - // We are not resizing but we may be being dragging in widget.direction. - return GestureDetector( - onHorizontalDragStart: _handleDragStart, - onHorizontalDragUpdate: _handleDragUpdate, - onHorizontalDragEnd: _handleDragEnd, - behavior: widget.behavior, - dragStartBehavior: widget.dragStartBehavior, - child: content, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/misc/system_message.dart b/packages/stream_chat_flutter/lib/src/misc/system_message.dart deleted file mode 100644 index 81aeba10e7..0000000000 --- a/packages/stream_chat_flutter/lib/src/misc/system_message.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamSystemMessage} -/// {@endtemplate} -class StreamSystemMessage extends StatelessWidget { - /// {@macro streamSystemMessage} - const StreamSystemMessage({ - super.key, - required this.message, - this.onMessageTap, - }); - - /// The message to display. - final Message message; - - /// The action to perform when tapping on the message. - final void Function(Message)? onMessageTap; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - final message = this.message.replaceMentions(linkify: false); - - final messageText = message.text; - if (messageText == null) return const SizedBox.shrink(); - - return GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: onMessageTap == null ? null : () => onMessageTap!(message), - child: Text( - messageText, - textAlign: TextAlign.center, - softWrap: true, - style: theme.textTheme.captionBold.copyWith( - color: theme.colorTheme.textLowEmphasis, - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/misc/thread_header.dart b/packages/stream_chat_flutter/lib/src/misc/thread_header.dart deleted file mode 100644 index 1142ac4507..0000000000 --- a/packages/stream_chat_flutter/lib/src/misc/thread_header.dart +++ /dev/null @@ -1,204 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamThreadHeader} -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/thread_header.png) -/// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/packages/stream_chat_flutter/screenshots/thread_header_paint.png) -/// -/// Shows information about the current message thread. -/// -/// ```dart -/// class ThreadPage extends StatelessWidget { -/// final Message parent; -/// -/// ThreadPage({ -/// Key key, -/// this.parent, -/// }) : super(key: key); -/// -/// @override -/// Widget build(BuildContext context) { -/// return Scaffold( -/// appBar: ThreadHeader( -/// parent: parent, -/// ), -/// body: Column( -/// children: [ -/// Expanded( -/// child: MessageListView( -/// parentMessage: parent, -/// ), -/// ), -/// MessageInput( -/// parentMessage: parent, -/// ), -/// ], -/// ), -/// ); -/// } -/// } -/// ``` -/// -/// Usually you would use this widget as an [AppBar] inside a [Scaffold]. -/// However you can also use it as a normal widget. -/// -/// A [StreamChannel] ancestor is required in order to provide the -/// information about the channel. -/// -/// Every part of the widget uses a [StreamBuilder] to render the channel -/// information as soon as it updates. -/// -/// By default the widget shows a backButton that calls [Navigator.pop]. -/// You can disable this button using the [showBackButton] property. -/// Alternatively, you can override the behavior with [onBackPressed]. -/// -/// The UI is rendered based on the first ancestor of type [StreamChatTheme] -/// and the [ChannelTheme.channelHeaderTheme] property. Modify it to change -/// the widget's appearance. -/// {@endtemplate} -class StreamThreadHeader extends StatelessWidget - implements PreferredSizeWidget { - /// {@macro streamThreadHeader} - const StreamThreadHeader({ - super.key, - required this.parent, - this.showBackButton = true, - this.onBackPressed, - this.title, - this.subtitle, - this.centerTitle, - this.leading, - this.actions, - this.onTitleTap, - this.showTypingIndicator = true, - this.backgroundColor, - this.elevation = 1, - }) : preferredSize = const Size.fromHeight(kToolbarHeight); - - /// Whether to show the leading back button. - /// - /// Defaults to `true`. - final bool showBackButton; - - /// The action to perform when pressing the back button. - /// - /// By default it calls [Navigator.pop] - final VoidCallback? onBackPressed; - - /// The action to perform when the title is tapped. - final VoidCallback? onTitleTap; - - /// The message parent of this thread - final Message parent; - - /// Title widget - final Widget? title; - - /// Subtitle widget - final Widget? subtitle; - - /// Whether the title should be centered - final bool? centerTitle; - - /// Leading widget - final Widget? leading; - - /// {@macro flutter.material.appbar.actions} - final List? actions; - - /// Whether to show the typing indicator if users are currently typing. - /// - /// Defaults to `true`. - final bool showTypingIndicator; - - /// The background color of this [StreamThreadHeader]. - final Color? backgroundColor; - - /// The elevation for this [StreamThreadHeader]. - final double elevation; - - @override - Widget build(BuildContext context) { - final effectiveCenterTitle = getEffectiveCenterTitle( - Theme.of(context), - actions: actions, - centerTitle: centerTitle, - ); - - final channelHeaderTheme = StreamChannelHeaderTheme.of(context); - - final defaultSubtitle = subtitle ?? - Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - '${context.translations.withText} ', - style: channelHeaderTheme.subtitleStyle, - ), - Flexible( - child: StreamChannelName( - channel: StreamChannel.of(context).channel, - textStyle: channelHeaderTheme.subtitleStyle, - ), - ), - ], - ); - - final theme = Theme.of(context); - return AppBar( - automaticallyImplyLeading: false, - toolbarTextStyle: theme.textTheme.bodyMedium, - titleTextStyle: theme.textTheme.titleLarge, - systemOverlayStyle: theme.brightness == Brightness.dark - ? SystemUiOverlayStyle.light - : SystemUiOverlayStyle.dark, - elevation: elevation, - leading: leading ?? - (showBackButton - ? StreamBackButton( - channelId: StreamChannel.of(context).channel.cid, - onPressed: onBackPressed, - showUnreadCount: true, - ) - : const SizedBox()), - backgroundColor: backgroundColor ?? channelHeaderTheme.color, - centerTitle: centerTitle, - actions: actions, - title: InkWell( - onTap: onTitleTap, - child: SizedBox( - height: preferredSize.height, - width: 250, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: effectiveCenterTitle - ? CrossAxisAlignment.center - : CrossAxisAlignment.stretch, - children: [ - title ?? - Text( - context.translations.threadReplyLabel, - style: channelHeaderTheme.titleStyle, - ), - const SizedBox(height: 2), - if (showTypingIndicator) - StreamTypingIndicator( - channel: StreamChannel.of(context).channel, - style: channelHeaderTheme.subtitleStyle, - parentId: parent.id, - alternativeWidget: defaultSubtitle, - ) - else - defaultSubtitle, - ], - ), - ), - ), - ); - } - - @override - final Size preferredSize; -} diff --git a/packages/stream_chat_flutter/lib/src/misc/timestamp.dart b/packages/stream_chat_flutter/lib/src/misc/timestamp.dart deleted file mode 100644 index e0b75f1f39..0000000000 --- a/packages/stream_chat_flutter/lib/src/misc/timestamp.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/utils/date_formatter.dart'; - -/// {@template streamTimestamp} -/// Represents a timestamp, that's used primarily for showing the time of a -/// message. -/// -/// This widget uses the [formatDate] function to format the date to a String. -/// {@endtemplate} -class StreamTimestamp extends StatelessWidget { - /// {@macro streamTimestamp} - const StreamTimestamp({ - super.key, - required this.date, - this.formatter = formatDate, - this.style, - this.textAlign, - this.textDirection, - }); - - /// The date to show in the timestamp. - final DateTime date; - - /// The formatter that's used to format the date to a String. - final DateFormatter formatter; - - /// The style to apply to the text. - final TextStyle? style; - - /// The alignment of the text. - final TextAlign? textAlign; - - /// The direction of the text. - final TextDirection? textDirection; - - @override - Widget build(BuildContext context) { - return Text( - formatter(context, date), - maxLines: 1, - style: style, - textAlign: textAlign, - textDirection: textDirection, - overflow: TextOverflow.ellipsis, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/misc/visible_footnote.dart b/packages/stream_chat_flutter/lib/src/misc/visible_footnote.dart deleted file mode 100644 index d2308f9d7d..0000000000 --- a/packages/stream_chat_flutter/lib/src/misc/visible_footnote.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamVisibleFootnote} -/// Informs the user about a [StreamMessageWidget]'s visibility to the current -/// user. -/// -/// Used in [StreamGiphyAttachment]. -/// {@endtemplate} -class StreamVisibleFootnote extends StatelessWidget { - /// {@macro streamVisibleFootnote} - const StreamVisibleFootnote({super.key}); - - @override - Widget build(BuildContext context) { - final chatThemeData = StreamChatTheme.of(context); - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - StreamSvgIcon( - size: 16, - icon: StreamSvgIcons.eye, - color: chatThemeData.colorTheme.textLowEmphasis, - ), - const SizedBox(width: 8), - Text( - context.translations.onlyVisibleToYouText, - style: chatThemeData.textTheme.footnote.copyWith( - color: chatThemeData.colorTheme.textLowEmphasis, - ), - ), - ], - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/poll/creator/poll_option_reorderable_list_view.dart b/packages/stream_chat_flutter/lib/src/poll/creator/poll_option_reorderable_list_view.dart deleted file mode 100644 index a186201180..0000000000 --- a/packages/stream_chat_flutter/lib/src/poll/creator/poll_option_reorderable_list_view.dart +++ /dev/null @@ -1,310 +0,0 @@ -import 'dart:ui'; - -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/misc/separated_reorderable_list_view.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -class _NullConst { - const _NullConst(); -} - -const _nullConst = _NullConst(); - -/// {@template pollOptionItem} -/// A data class that represents a poll option item -/// {@endtemplate} -class PollOptionItem { - /// {@macro pollOptionItem} - PollOptionItem({ - String? id, - this.text = '', - this.error, - }) : id = id ?? const Uuid().v4(); - - /// The unique id of the poll option item. - final String id; - - /// The text of the poll option item. - final String text; - - /// Optional error message based on the validation of the poll option item. - /// - /// If the poll option item is valid, this will be `null`. - final String? error; - - /// A copy of the current [PollOptionItem] with the provided values. - PollOptionItem copyWith({ - String? id, - String? text, - Object? error = _nullConst, - }) { - return PollOptionItem( - id: id ?? this.id, - text: text ?? this.text, - error: error == _nullConst ? this.error : error as String?, - ); - } -} - -/// {@template pollOptionListItem} -/// A widget that represents a poll option list item. -/// {@endtemplate} -class PollOptionListItem extends StatelessWidget { - /// {@macro pollOptionListItem} - const PollOptionListItem({ - super.key, - required this.option, - this.hintText, - this.onChanged, - }); - - /// The poll option item. - final PollOptionItem option; - - /// Hint to be displayed in the poll option list item. - final String? hintText; - - /// Callback called when the poll option item is changed. - final ValueSetter? onChanged; - - @override - Widget build(BuildContext context) { - final theme = StreamPollCreatorTheme.of(context); - final fillColor = theme.optionsTextFieldFillColor; - final borderRadius = theme.optionsTextFieldBorderRadius; - - return DecoratedBox( - decoration: BoxDecoration( - color: fillColor, - borderRadius: borderRadius, - ), - child: Row( - children: [ - Expanded( - child: StreamPollTextField( - initialValue: option.text, - hintText: hintText, - style: theme.optionsTextFieldStyle, - fillColor: fillColor, - borderRadius: borderRadius, - errorText: option.error, - errorStyle: theme.optionsTextFieldErrorStyle, - onChanged: (text) => onChanged?.call(option.copyWith(text: text)), - ), - ), - const SizedBox( - width: 48, - height: 48, - child: Icon(Icons.drag_handle_rounded), - ), - ], - ), - ); - } -} - -/// {@template pollOptionReorderableListView} -/// A widget that represents a reorderable list view of poll options. -/// {@endtemplate} -class PollOptionReorderableListView extends StatefulWidget { - /// {@macro pollOptionReorderableListView} - const PollOptionReorderableListView({ - super.key, - this.title, - this.itemHintText, - this.allowDuplicate = false, - this.initialOptions = const [], - this.onOptionsChanged, - }); - - /// An optional title to be displayed above the list of poll options. - final String? title; - - /// The hint text to be displayed in the poll option list item. - final String? itemHintText; - - /// Whether the poll options allow duplicates. - /// - /// If `true`, the poll options can be duplicated. - final bool allowDuplicate; - - /// The initial list of poll options. - final List initialOptions; - - /// Callback called when the items are updated or reordered. - final ValueSetter>? onOptionsChanged; - - @override - State createState() => - _PollOptionReorderableListViewState(); -} - -class _PollOptionReorderableListViewState - extends State { - late var _options = { - for (final option in widget.initialOptions) option.id: option, - }; - - @override - void didUpdateWidget(covariant PollOptionReorderableListView oldWidget) { - super.didUpdateWidget(oldWidget); - // Update the options if the updated options are different from the current - // set of options. - final currOptions = [..._options.values]; - final newOptions = widget.initialOptions; - - final optionItemEquality = ListEquality( - EqualityBy((it) => (it.id, it.text)), - ); - - if (optionItemEquality.equals(currOptions, newOptions) case false) { - _options = { - for (final option in widget.initialOptions) option.id: option, - }; - } - } - - Widget _proxyDecorator(Widget child, int index, Animation animation) { - return AnimatedBuilder( - animation: animation, - builder: (BuildContext context, Widget? child) { - final animValue = Curves.easeInOut.transform(animation.value); - final elevation = lerpDouble(0, 6, animValue)!; - return Material( - borderRadius: BorderRadius.circular(12), - elevation: elevation, - child: child, - ); - }, - child: child, - ); - } - - String? _validateOption(PollOptionItem option) { - final translations = context.translations; - - // Check if the option is empty. - if (option.text.isEmpty) return translations.pollOptionEmptyError; - - // Check for duplicate options if duplicates are not allowed. - if (widget.allowDuplicate case false) { - if (_options.values.any((it) { - // Skip if it's the same option - if (it.id == option.id) return false; - - return it.text == option.text; - })) { - return translations.pollOptionDuplicateError; - } - } - - return null; - } - - void _onOptionChanged(PollOptionItem option) { - setState(() { - // Update the changed option. - _options[option.id] = option.copyWith( - error: _validateOption(option), - ); - - // Validate every other option to check for duplicates. - _options.updateAll((key, value) { - // Skip the changed option as it's already validated. - if (key == option.id) return value; - - return value.copyWith(error: _validateOption(value)); - }); - - // Notify the parent widget about the change - widget.onOptionsChanged?.call([..._options.values]); - }); - } - - void _onOptionReorder(int oldIndex, int newIndex) { - setState(() { - final options = [..._options.values]; - - // Move the dragged option to the new index - final option = options.removeAt(oldIndex); - options.insert(newIndex, option); - - // Update the options map - _options = { - for (final option in options) option.id: option, - }; - - // Notify the parent widget about the change - widget.onOptionsChanged?.call([..._options.values]); - }); - } - - void _onAddOptionPressed() { - setState(() { - // Create a new option and add it to the map. - final option = PollOptionItem(); - _options[option.id] = option; - - // Notify the parent widget about the change - widget.onOptionsChanged?.call([..._options.values]); - }); - } - - @override - Widget build(BuildContext context) { - final theme = StreamPollCreatorTheme.of(context); - final borderRadius = theme.optionsTextFieldBorderRadius; - - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (widget.title case final title?) ...[ - Text(title, style: theme.optionsHeaderStyle), - const SizedBox(height: 8), - ], - Flexible( - child: SeparatedReorderableListView( - shrinkWrap: true, - itemCount: _options.length, - physics: const NeverScrollableScrollPhysics(), - proxyDecorator: _proxyDecorator, - separatorBuilder: (_, __) => const SizedBox(height: 8), - onReorder: _onOptionReorder, - itemBuilder: (context, index) { - final option = _options.values.elementAt(index); - return PollOptionListItem( - key: Key(option.id), - option: option, - hintText: widget.itemHintText, - onChanged: _onOptionChanged, - ); - }, - ), - ), - const SizedBox(height: 8), - SizedBox( - width: double.infinity, - child: FilledButton.tonal( - onPressed: _onAddOptionPressed, - style: TextButton.styleFrom( - alignment: Alignment.centerLeft, - textStyle: theme.optionsTextFieldStyle, - shape: RoundedRectangleBorder( - borderRadius: borderRadius ?? BorderRadius.zero, - ), - padding: const EdgeInsets.symmetric( - vertical: 18, - horizontal: 16, - ), - backgroundColor: theme.optionsTextFieldFillColor, - foregroundColor: theme.optionsTextFieldStyle?.color, - ), - child: Text(context.translations.addAnOptionLabel), - ), - ), - ], - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/poll/creator/poll_question_text_field.dart b/packages/stream_chat_flutter/lib/src/poll/creator/poll_question_text_field.dart deleted file mode 100644 index a5e8ea25be..0000000000 --- a/packages/stream_chat_flutter/lib/src/poll/creator/poll_question_text_field.dart +++ /dev/null @@ -1,156 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/poll/stream_poll_text_field.dart'; -import 'package:stream_chat_flutter/src/theme/poll_creator_theme.dart'; -import 'package:stream_chat_flutter/src/utils/extensions.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -class _NullConst { - const _NullConst(); -} - -const _nullConst = _NullConst(); - -/// {@template pollQuestion} -/// A data class that represents a poll question. -/// {@endtemplate} -class PollQuestion { - /// {@macro pollQuestion} - PollQuestion({ - String? id, - this.text = '', - this.error, - }) : id = id ?? const Uuid().v4(); - - /// The unique id of the poll this question belongs to. - final String id; - - /// The text of the poll question. - final String text; - - /// Optional error message based on the validation of the poll question. - /// - /// If the poll question is valid, this will be `null`. - final String? error; - - /// A copy of the current [PollQuestion] with the provided values. - PollQuestion copyWith({ - String? id, - String? text, - Object? error = _nullConst, - }) { - return PollQuestion( - id: id ?? this.id, - text: text ?? this.text, - error: error == _nullConst ? this.error : error as String?, - ); - } -} - -/// {@template pollQuestionTextField} -/// A widget that represents a text field for poll question input. -/// {@endtemplate} -class PollQuestionTextField extends StatefulWidget { - /// {@macro pollQuestionTextField} - const PollQuestionTextField({ - super.key, - required this.initialQuestion, - this.title, - this.hintText, - this.questionRange = const (min: 1, max: 80), - this.onChanged, - }); - - /// An optional title to be displayed above the text field. - final String? title; - - /// The hint text to be displayed in the text field. - final String? hintText; - - /// The length constraints of the poll question. - /// - /// If `null`, there are no constraints on the length of the question. - final Range? questionRange; - - /// The poll question. - final PollQuestion initialQuestion; - - /// Callback called when the poll question is changed. - final ValueChanged? onChanged; - - @override - State createState() => _PollQuestionTextFieldState(); -} - -class _PollQuestionTextFieldState extends State { - late var _question = widget.initialQuestion; - - @override - void didUpdateWidget(covariant PollQuestionTextField oldWidget) { - super.didUpdateWidget(oldWidget); - - // Update the question if the updated initial question is different from - // the current question. - final currQuestion = _question; - final newQuestion = widget.initialQuestion; - final questionEquality = EqualityBy( - (it) => (it.id, it.text), - ); - - if (questionEquality.equals(currQuestion, newQuestion) case false) { - _question = newQuestion; - } - } - - String? _validateQuestion(String question) { - if (widget.questionRange case final range?) { - return context.translations.pollQuestionValidationError( - question.length, - range, - ); - } - - return null; - } - - @override - Widget build(BuildContext context) { - final theme = StreamPollCreatorTheme.of(context); - final fillColor = theme.questionTextFieldFillColor; - final borderRadius = theme.questionTextFieldBorderRadius; - - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (widget.title case final title?) ...[ - Text(title, style: theme.questionHeaderStyle), - const SizedBox(height: 8), - ], - DecoratedBox( - decoration: BoxDecoration( - color: fillColor, - borderRadius: borderRadius, - ), - child: StreamPollTextField( - initialValue: _question.text, - hintText: widget.hintText, - fillColor: fillColor, - style: theme.questionTextFieldStyle, - borderRadius: borderRadius, - errorText: _question.error, - errorStyle: theme.questionTextFieldErrorStyle, - onChanged: (text) { - _question = _question.copyWith( - text: text, - error: _validateQuestion(text), - ); - - widget.onChanged?.call(_question); - }, - ), - ), - ], - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/poll/creator/poll_switch_list_tile.dart b/packages/stream_chat_flutter/lib/src/poll/creator/poll_switch_list_tile.dart deleted file mode 100644 index b1088d3a90..0000000000 --- a/packages/stream_chat_flutter/lib/src/poll/creator/poll_switch_list_tile.dart +++ /dev/null @@ -1,240 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -class _NullConst { - const _NullConst(); -} - -const _nullConst = _NullConst(); - -/// {@template pollSwitchListTile} -/// A widget that represents a switch list tile for poll input. -/// -/// The switch list tile contains a title and a switch. -/// -/// Optionally, it can contain a list of children widgets that are displayed -/// below the switch when the switch is enabled. -/// -/// see also: -/// - [PollSwitchTextField], a widget that represents a toggleable text field -/// for poll input. -/// {@endtemplate} -class PollSwitchListTile extends StatelessWidget { - /// {@macro pollSwitchListTile} - const PollSwitchListTile({ - super.key, - this.value = false, - required this.title, - this.children = const [], - this.onChanged, - }); - - /// The current value of the switch. - final bool value; - - /// The title of the switch list tile. - final String title; - - /// Optional list of children widgets to be displayed when the switch is - /// enabled. - /// - /// If `null`, no children will be displayed. - final List children; - - /// Callback called when the switch value is changed. - final ValueSetter? onChanged; - - @override - Widget build(BuildContext context) { - final theme = StreamPollCreatorTheme.of(context); - final fillColor = theme.switchListTileFillColor; - final borderRadius = theme.switchListTileBorderRadius; - - final listTile = SwitchListTile( - value: value, - onChanged: onChanged, - tileColor: fillColor, - title: Text(title, style: theme.switchListTileTitleStyle), - contentPadding: const EdgeInsets.only(left: 16, right: 8), - shape: RoundedRectangleBorder( - borderRadius: borderRadius ?? BorderRadius.zero, - ), - ); - - return DecoratedBox( - decoration: BoxDecoration( - color: fillColor, - borderRadius: borderRadius, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - listTile, - if (value) ...children, - ], - ), - ); - } -} - -/// {@template pollSwitchItem} -/// A data class that represents a poll boolean item. -/// {@endtemplate} -class PollSwitchItem { - /// {@macro pollSwitchItem} - PollSwitchItem({ - String? id, - this.value = false, - this.inputValue, - this.error, - }) : id = id ?? const Uuid().v4(); - - /// The unique id of the poll option item. - final String id; - - /// The boolean value of the poll switch item. - final bool value; - - /// Optional input value linked to the poll switch item. - final T? inputValue; - - /// Optional error message based on the validation of the poll switch item - /// and its input value. - /// - /// If the poll switch item is valid, this will be `null`. - final String? error; - - /// A copy of the current [PollSwitchItem] with the provided values. - PollSwitchItem copyWith({ - String? id, - bool? value, - Object? error = _nullConst, - Object? inputValue = _nullConst, - }) { - return PollSwitchItem( - id: id ?? this.id, - value: value ?? this.value, - error: error == _nullConst ? this.error : error as String?, - inputValue: inputValue == _nullConst ? this.inputValue : inputValue as T?, - ); - } -} - -/// {@template pollSwitchTextField} -/// A widget that represents a toggleable text field for poll input. -/// -/// Generally used as one of the children of [PollSwitchListTile]. -/// {@endtemplate} -class PollSwitchTextField extends StatefulWidget { - /// {@macro pollSwitchTextField} - const PollSwitchTextField({ - super.key, - required this.item, - this.hintText, - this.keyboardType, - this.onChanged, - this.validator, - }); - - /// The current value of the switch text field. - final PollSwitchItem item; - - /// The hint text to be displayed in the text field. - final String? hintText; - - /// The keyboard type of the text field. - final TextInputType? keyboardType; - - /// Callback called when the switch text field is changed. - final ValueChanged>? onChanged; - - /// The validator function to validate the input value. - final String? Function(PollSwitchItem)? validator; - - @override - State createState() => _PollSwitchTextFieldState(); -} - -class _PollSwitchTextFieldState extends State { - late var _item = widget.item.copyWith( - error: widget.validator?.call(widget.item), - ); - - @override - void didUpdateWidget(covariant PollSwitchTextField oldWidget) { - super.didUpdateWidget(oldWidget); - // Update the item if the updated item is different from the current item. - final currItem = _item; - final newItem = widget.item; - final itemEquality = EqualityBy, (bool, int?)>( - (it) => (it.value, it.inputValue), - ); - - if (itemEquality.equals(currItem, newItem) case false) { - _item = newItem; - } - } - - void _onSwitchToggled(bool value) { - setState(() { - // Update the switch value. - _item = _item.copyWith(value: value); - // Validate the switch value. - _item = _item.copyWith(error: widget.validator?.call(_item)); - - // Notify the parent widget about the change - widget.onChanged?.call(_item); - }); - } - - void _onFieldChanged(String text) { - setState(() { - // Update the input value. - _item = _item.copyWith(inputValue: int.tryParse(text)); - // Validate the input value. - _item = _item.copyWith(error: widget.validator?.call(_item)); - - // Notify the parent widget about the change - widget.onChanged?.call(_item); - }); - } - - @override - Widget build(BuildContext context) { - final theme = StreamPollCreatorTheme.of(context); - final fillColor = theme.switchListTileFillColor; - final borderRadius = theme.switchListTileBorderRadius; - - return DecoratedBox( - decoration: BoxDecoration( - color: fillColor, - borderRadius: borderRadius, - ), - child: Row( - children: [ - Expanded( - child: StreamPollTextField( - hintText: widget.hintText, - enabled: _item.value, - fillColor: fillColor, - style: theme.switchListTileTitleStyle, - keyboardType: widget.keyboardType, - borderRadius: borderRadius, - errorText: _item.value ? _item.error : null, - errorStyle: theme.switchListTileErrorStyle, - initialValue: _item.inputValue?.toString(), - onChanged: _onFieldChanged, - ), - ), - Switch( - value: _item.value, - onChanged: _onSwitchToggled, - ), - const SizedBox(width: 8), - ], - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/poll/creator/stream_poll_creator_dialog.dart b/packages/stream_chat_flutter/lib/src/poll/creator/stream_poll_creator_dialog.dart deleted file mode 100644 index f5cb1e05a1..0000000000 --- a/packages/stream_chat_flutter/lib/src/poll/creator/stream_poll_creator_dialog.dart +++ /dev/null @@ -1,269 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template showStreamPollCreatorDialog} -/// Shows the poll creator dialog based on the screen size. -/// -/// The regular dialog is shown on larger screens such as tablets and desktops -/// and a full screen dialog is shown on smaller screens such as mobile phones. -/// -/// The [poll] and [config] parameters can be used to provide an initial poll -/// and a configuration to validate the poll. -/// {@endtemplate} -Future showStreamPollCreatorDialog({ - required BuildContext context, - Poll? poll, - PollConfig? config, - bool barrierDismissible = true, - Color? barrierColor, - String? barrierLabel, - bool useSafeArea = true, - bool useRootNavigator = false, - RouteSettings? routeSettings, - Offset? anchorPoint, - EdgeInsets padding = const EdgeInsets.all(16), - TraversalEdgeBehavior? traversalEdgeBehavior, -}) { - final size = MediaQuery.sizeOf(context); - final isTabletOrDesktop = size.width > 600; - - final colorTheme = StreamChatTheme.of(context).colorTheme; - - // Open it as a regular dialog on bigger screens such as tablets and desktops. - if (isTabletOrDesktop) { - return showDialog( - context: context, - barrierColor: barrierColor ?? colorTheme.overlay, - barrierDismissible: barrierDismissible, - barrierLabel: barrierLabel, - useSafeArea: useSafeArea, - useRootNavigator: useRootNavigator, - routeSettings: routeSettings, - builder: (context) => StreamPollCreatorDialog( - poll: poll, - config: config, - padding: padding, - ), - ); - } - - // Open it as a full screen dialog on smaller screens such as mobile phones. - final navigator = Navigator.of(context, rootNavigator: useRootNavigator); - return navigator.push( - MaterialPageRoute( - fullscreenDialog: true, - barrierDismissible: barrierDismissible, - builder: (context) => StreamPollCreatorFullScreenDialog( - poll: poll, - config: config, - padding: padding, - ), - ), - ); -} - -/// {@template streamPollCreatorDialog} -/// A dialog that allows users to create a poll. -/// -/// The dialog provides a form to create a poll with a question and multiple -/// options. -/// -/// This widget is intended to be used on larger screens such as tablets and -/// desktops. -/// -/// For smaller screens, consider using [StreamPollCreatorFullScreenDialog]. -/// {@endtemplate} -class StreamPollCreatorDialog extends StatefulWidget { - /// {@macro streamPollCreatorDialog} - const StreamPollCreatorDialog({ - super.key, - this.poll, - this.config, - this.padding = const EdgeInsets.all(16), - }); - - /// The initial poll to be used in the poll creator. - final Poll? poll; - - /// The configuration used to validate the poll. - final PollConfig? config; - - /// The padding around the poll creator. - final EdgeInsets padding; - - @override - State createState() => - _StreamPollCreatorDialogState(); -} - -class _StreamPollCreatorDialogState extends State { - late final _controller = StreamPollController( - poll: widget.poll, - config: widget.config, - ); - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - final pollCreatorTheme = StreamPollCreatorTheme.of(context); - - final actions = [ - TextButton( - onPressed: Navigator.of(context).pop, - style: TextButton.styleFrom( - textStyle: theme.textTheme.headlineBold, - foregroundColor: theme.colorTheme.accentPrimary, - disabledForegroundColor: theme.colorTheme.disabled, - ), - child: Text(context.translations.cancelLabel.toUpperCase()), - ), - ValueListenableBuilder( - valueListenable: _controller, - builder: (context, poll, child) { - final isValid = _controller.validate(); - return TextButton( - onPressed: isValid - ? () { - final errors = _controller.validateGranularly(); - if (errors.isNotEmpty) { - return; - } - - final sanitizedPoll = _controller.sanitizedPoll; - return Navigator.of(context).pop(sanitizedPoll); - } - : null, - style: TextButton.styleFrom( - textStyle: theme.textTheme.headlineBold, - foregroundColor: theme.colorTheme.accentPrimary, - disabledForegroundColor: theme.colorTheme.disabled, - ), - child: Text(context.translations.createLabel.toUpperCase()), - ); - }, - ), - ]; - - return AlertDialog( - title: Text( - context.translations.createPollLabel(), - style: pollCreatorTheme.appBarTitleStyle, - ), - titlePadding: const EdgeInsets.symmetric(vertical: 14, horizontal: 16), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), - actions: actions, - contentPadding: EdgeInsets.zero, - actionsPadding: const EdgeInsets.all(8), - backgroundColor: pollCreatorTheme.backgroundColor, - content: SizedBox( - width: 640, // Similar to BottomSheet default width on M3 - child: StreamPollCreatorWidget( - shrinkWrap: true, - padding: widget.padding, - controller: _controller, - ), - ), - ); - } -} - -/// {@template streamPollCreatorFullScreenDialog} -/// A page that allows users to create a poll. -/// -/// The page provides a form to create a poll with a question and multiple -/// options. -/// -/// This widget is intended to be used on smaller screens such as mobile phones. -/// -/// For larger screens, consider using [StreamPollCreatorDialog]. -/// {@endtemplate} -class StreamPollCreatorFullScreenDialog extends StatefulWidget { - /// {@macro streamPollCreatorFullScreenDialog} - const StreamPollCreatorFullScreenDialog({ - super.key, - this.poll, - this.config, - this.padding = const EdgeInsets.all(16), - }); - - /// The initial poll to be used in the poll creator. - final Poll? poll; - - /// The configuration used to validate the poll. - final PollConfig? config; - - /// The padding around the poll creator. - final EdgeInsets padding; - - @override - State createState() => - _StreamPollCreatorFullScreenDialogState(); -} - -class _StreamPollCreatorFullScreenDialogState - extends State { - late final _controller = StreamPollController( - poll: widget.poll, - config: widget.config, - ); - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final theme = StreamPollCreatorTheme.of(context); - - return Scaffold( - backgroundColor: theme.backgroundColor, - appBar: AppBar( - elevation: theme.appBarElevation, - backgroundColor: theme.appBarBackgroundColor, - title: Text( - context.translations.createPollLabel(), - style: theme.appBarTitleStyle, - ), - actions: [ - ValueListenableBuilder( - valueListenable: _controller, - builder: (context, poll, child) { - final colorTheme = StreamChatTheme.of(context).colorTheme; - - final isValid = _controller.validate(); - - return IconButton( - color: colorTheme.accentPrimary, - disabledColor: colorTheme.disabled, - icon: const StreamSvgIcon(icon: StreamSvgIcons.send), - onPressed: isValid - ? () { - final errors = _controller.validateGranularly(); - if (errors.isNotEmpty) { - return; - } - - final sanitizedPoll = _controller.sanitizedPoll; - return Navigator.of(context).pop(sanitizedPoll); - } - : null, - ); - }, - ), - ], - ), - body: StreamPollCreatorWidget( - padding: widget.padding, - controller: _controller, - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/poll/creator/stream_poll_creator_widget.dart b/packages/stream_chat_flutter/lib/src/poll/creator/stream_poll_creator_widget.dart deleted file mode 100644 index c824045141..0000000000 --- a/packages/stream_chat_flutter/lib/src/poll/creator/stream_poll_creator_widget.dart +++ /dev/null @@ -1,142 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/poll/creator/poll_option_reorderable_list_view.dart'; -import 'package:stream_chat_flutter/src/poll/creator/poll_question_text_field.dart'; -import 'package:stream_chat_flutter/src/poll/creator/poll_switch_list_tile.dart'; -import 'package:stream_chat_flutter/src/utils/extensions.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// {@template streamPollCreator} -/// A widget that allows users to create a poll. -/// -/// The widget provides a form to create a poll with a question and multiple -/// options. -/// -/// {@endtemplate} -class StreamPollCreatorWidget extends StatelessWidget { - /// {@macro streamPollCreator} - const StreamPollCreatorWidget({ - super.key, - required this.controller, - this.shrinkWrap = false, - this.physics, - this.padding = const EdgeInsets.all(16), - }); - - /// The padding around the poll creator. - final EdgeInsets padding; - - /// Whether the scroll view should shrink-wrap its content. - final bool shrinkWrap; - - /// The physics of the scroll view. - final ScrollPhysics? physics; - - /// The controller used to manage the state of the poll. - final StreamPollController controller; - - @override - Widget build(BuildContext context) { - return ValueListenableBuilder( - valueListenable: controller, - builder: (context, poll, child) { - final config = controller.config; - final translations = context.translations; - - // Using a combination of SingleChildScrollView and Column instead of - // ListView to avoid the item color overflow issue. - // - // More info: https://github.com/flutter/flutter/issues/86584 - return SingleChildScrollView( - padding: padding, - physics: physics, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - PollQuestionTextField( - questionRange: config.nameRange, - title: translations.questionsLabel, - hintText: translations.askAQuestionLabel, - initialQuestion: PollQuestion(id: poll.id, text: poll.name), - onChanged: (question) => controller.question = question.text, - ), - const SizedBox(height: 32), - PollOptionReorderableListView( - title: translations.optionLabel(isPlural: true), - itemHintText: translations.optionLabel(), - allowDuplicate: config.allowDuplicateOptions, - initialOptions: [ - for (final option in poll.options) - PollOptionItem(id: option.id, text: option.text), - ], - onOptionsChanged: (options) => controller.options = [ - for (final option in options) - PollOption(id: option.id, text: option.text), - ], - ), - const SizedBox(height: 32), - PollSwitchListTile( - title: translations.multipleAnswersLabel, - value: poll.enforceUniqueVote == false, - onChanged: (value) { - controller.enforceUniqueVote = !value; - // We also need to reset maxVotesAllowed if disabled. - if (value case false) controller.maxVotesAllowed = null; - }, - children: [ - PollSwitchTextField( - hintText: translations.maximumVotesPerPersonLabel, - item: PollSwitchItem( - value: poll.maxVotesAllowed != null, - inputValue: poll.maxVotesAllowed, - ), - keyboardType: TextInputType.number, - validator: (item) { - if (config.allowedVotesRange case final allowedRange?) { - final votes = item.inputValue; - if (votes == null) return null; - - return translations.maxVotesPerPersonValidationError( - votes, - allowedRange, - ); - } - - return null; - }, - onChanged: (option) { - final enabled = option.value; - final maxVotes = option.inputValue; - - controller.maxVotesAllowed = enabled ? maxVotes : null; - }, - ), - ], - ), - const SizedBox(height: 8), - PollSwitchListTile( - title: translations.anonymousPollLabel, - value: poll.votingVisibility == VotingVisibility.anonymous, - onChanged: (anon) => controller.votingVisibility = anon // - ? VotingVisibility.anonymous - : VotingVisibility.public, - ), - const SizedBox(height: 8), - PollSwitchListTile( - title: translations.suggestAnOptionLabel, - value: poll.allowUserSuggestedOptions, - onChanged: (allow) => controller.allowSuggestions = allow, - ), - const SizedBox(height: 8), - PollSwitchListTile( - title: translations.addACommentLabel, - value: poll.allowAnswers, - onChanged: (allow) => controller.allowComments = allow, - ), - ], - ), - ); - }, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/poll/interactor/poll_add_comment_dialog.dart b/packages/stream_chat_flutter/lib/src/poll/interactor/poll_add_comment_dialog.dart deleted file mode 100644 index 47c67a7b76..0000000000 --- a/packages/stream_chat_flutter/lib/src/poll/interactor/poll_add_comment_dialog.dart +++ /dev/null @@ -1,106 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/poll/stream_poll_text_field.dart'; -import 'package:stream_chat_flutter/src/theme/poll_interactor_theme.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter/src/utils/extensions.dart'; - -/// {@template showPollAddCommentDialog} -/// Shows a dialog that allows the user to add a poll comment. -/// -/// Optionally, you can provide an [initialValue] to pre-fill the text field. -/// {@endtemplate} -Future showPollAddCommentDialog({ - required BuildContext context, - String initialValue = '', -}) => - showDialog( - context: context, - barrierDismissible: false, - builder: (_) => PollAddCommentDialog( - initialValue: initialValue, - ), - ); - -/// {@template pollAddCommentDialog} -/// A dialog that allows the user to add or update a poll comment. -/// -/// Optionally, you can provide an [initialValue] to pre-fill the text field. -/// {@endtemplate} -class PollAddCommentDialog extends StatefulWidget { - /// {@macro pollAddCommentDialog} - const PollAddCommentDialog({ - super.key, - this.initialValue = '', - }); - - /// Initial answer to be displayed in the text field. - /// - /// Defaults to an empty string. - final String initialValue; - - @override - State createState() => _PollAddCommentDialogState(); -} - -class _PollAddCommentDialogState extends State { - late String _comment = widget.initialValue; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - final pollInteractorTheme = StreamPollInteractorTheme.of(context); - - final actions = [ - TextButton( - onPressed: Navigator.of(context).pop, - style: TextButton.styleFrom( - textStyle: theme.textTheme.headlineBold, - foregroundColor: theme.colorTheme.accentPrimary, - disabledForegroundColor: theme.colorTheme.disabled, - ), - child: Text(context.translations.cancelLabel.toUpperCase()), - ), - TextButton( - onPressed: switch (_comment == widget.initialValue) { - true => null, - false => () => Navigator.of(context).pop(_comment), - }, - style: TextButton.styleFrom( - textStyle: theme.textTheme.headlineBold, - foregroundColor: theme.colorTheme.accentPrimary, - disabledForegroundColor: theme.colorTheme.disabled, - ), - child: Text(context.translations.sendLabel.toUpperCase()), - ), - ]; - - return AlertDialog( - title: Text( - switch (widget.initialValue.isEmpty) { - true => context.translations.addACommentLabel, - false => context.translations.updateYourCommentLabel, - }, - style: pollInteractorTheme.pollActionDialogTitleStyle, - ), - actions: actions, - titlePadding: const EdgeInsets.symmetric(vertical: 14, horizontal: 16), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), - contentPadding: const EdgeInsets.all(16), - actionsPadding: const EdgeInsets.all(8), - backgroundColor: theme.colorTheme.appBg, - content: StreamPollTextField( - autoFocus: true, - initialValue: _comment, - hintText: context.translations.enterYourCommentLabel, - contentPadding: const EdgeInsets.symmetric( - vertical: 12, - horizontal: 16, - ), - style: pollInteractorTheme.pollActionDialogTextFieldStyle, - fillColor: pollInteractorTheme.pollActionDialogTextFieldFillColor, - borderRadius: pollInteractorTheme.pollActionDialogTextFieldBorderRadius, - onChanged: (value) => setState(() => _comment = value), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/poll/interactor/poll_footer.dart b/packages/stream_chat_flutter/lib/src/poll/interactor/poll_footer.dart deleted file mode 100644 index 807e5071e0..0000000000 --- a/packages/stream_chat_flutter/lib/src/poll/interactor/poll_footer.dart +++ /dev/null @@ -1,178 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/theme/poll_interactor_theme.dart'; -import 'package:stream_chat_flutter/src/utils/utils.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// {@template pollFooter} -/// A widget used as the footer of a poll. -/// -/// Used in [StreamPollInteractor] to display various actions the user can take -/// on the poll. -/// {@endtemplate} -class PollFooter extends StatelessWidget { - /// {@macro pollFooter} - const PollFooter({ - super.key, - required this.poll, - required this.currentUser, - this.visibleOptionCount, - this.onEndVote, - this.onAddComment, - this.onViewComments, - this.onViewResults, - this.onSuggestOption, - this.onSeeMoreOptions, - }); - - /// The poll the footer is for. - final Poll poll; - - /// The current user interacting with the poll. - final User currentUser; - - /// The number of visible options in the poll. - /// - /// Used to determine if the user can see more options button. - final int? visibleOptionCount; - - /// Callback invoked when the user wants to end the voting. - /// - /// This is only available if the [currentUser] is the creator of the poll. - final VoidCallback? onEndVote; - - /// Callback invoked when the user wants to add a comment. - /// - /// This is only available if the poll is not closed and allows answers. - final VoidCallback? onAddComment; - - /// Callback invoked when the user wants to view all the comments. - /// - /// This is only available if the poll is not closed and has answers. - final VoidCallback? onViewComments; - - /// Callback invoked when the user wants to view the poll results. - final VoidCallback? onViewResults; - - /// Callback invoked when the user wants to suggest an option. - /// - /// This is only available if the poll is not closed and allows user - /// suggested options. - final VoidCallback? onSuggestOption; - - /// Callback invoked when the user wants to see more options. - /// - /// This is only available if the poll has more options than the - /// [visibleOptionCount]. - final VoidCallback? onSeeMoreOptions; - - bool get _shouldShowEndPollButton { - if (poll.isClosed) return false; - - // Only the creator of the poll can end it. - return poll.createdBy?.id == currentUser.id; - } - - bool get _shouldShowAddCommentButton { - if (poll.isClosed || !poll.allowAnswers) return false; - - // If the user has already commented, don't show the button. - if (poll.ownAnswers.isNotEmpty) return false; - - return true; - } - - bool get _shouldShowViewCommentsButton { - // If the poll has no answers, don't show the button. - return poll.answersCount > 0; - } - - bool get _shouldShowSuggestionsButton { - if (poll.isClosed) return false; - - // Only show the button if the poll allows user suggested options. - return poll.allowUserSuggestedOptions; - } - - bool get _shouldEnableViewResultsButton { - // Disable the button if the poll haven't got any votes yet. - if (poll.voteCount < 1) return false; - - return true; - } - - @override - Widget build(BuildContext context) { - final translations = context.translations; - - return Column( - spacing: 2, - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - if (visibleOptionCount case final count? - when count < poll.options.length) - PollFooterButton( - title: translations.seeAllOptionsLabel(count: poll.options.length), - onPressed: onSeeMoreOptions, - ), - if (_shouldShowSuggestionsButton) - PollFooterButton( - title: translations.suggestAnOptionLabel, - onPressed: onSuggestOption, - ), - if (_shouldShowAddCommentButton) - PollFooterButton( - title: translations.addACommentLabel, - onPressed: onAddComment, - ), - if (_shouldShowViewCommentsButton) - PollFooterButton( - title: translations.viewCommentsLabel, - onPressed: onViewComments, - ), - PollFooterButton( - title: translations.viewResultsLabel, - onPressed: _shouldEnableViewResultsButton ? onViewResults : null, - ), - if (_shouldShowEndPollButton) - PollFooterButton( - title: translations.endVoteLabel, - onPressed: onEndVote, - ), - ], - ); - } -} - -/// {@template pollFooterButton} -/// A button used in [PollFooter]. -/// -/// Displays the title and invokes the [onPressed] callback when pressed. -/// {@endtemplate} -class PollFooterButton extends StatelessWidget { - /// {@macro pollFooterButton} - const PollFooterButton({ - super.key, - required this.title, - this.onPressed, - }); - - /// The title of the button. - final String title; - - /// Callback invoked when the button is pressed. - final VoidCallback? onPressed; - - @override - Widget build(BuildContext context) { - final theme = StreamPollInteractorTheme.of(context); - - return TextButton( - onPressed: onPressed, - // Consume long press to avoid the parent long press. - onLongPress: onPressed != null ? () {} : null, - style: theme.pollActionButtonStyle, - child: Text(title), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/poll/interactor/poll_header.dart b/packages/stream_chat_flutter/lib/src/poll/interactor/poll_header.dart deleted file mode 100644 index 0e32a1e59f..0000000000 --- a/packages/stream_chat_flutter/lib/src/poll/interactor/poll_header.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/theme/poll_interactor_theme.dart'; -import 'package:stream_chat_flutter/src/utils/extensions.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// {@template pollHeader} -/// A widget used as the header of a poll. -/// -/// Used in [StreamPollInteractor] to display the poll question and voting mode. -/// {@endtemplate} -class PollHeader extends StatelessWidget { - /// {@macro pollHeader} - const PollHeader({ - super.key, - required this.poll, - }); - - /// The poll the header is for. - final Poll poll; - - @override - Widget build(BuildContext context) { - final theme = StreamPollInteractorTheme.of(context); - - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - poll.name, - style: theme.pollTitleStyle, - ), - Text( - context.translations.pollVotingModeLabel(poll.votingMode), - style: theme.pollSubtitleStyle, - ), - ], - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/poll/interactor/poll_options_list_view.dart b/packages/stream_chat_flutter/lib/src/poll/interactor/poll_options_list_view.dart deleted file mode 100644 index 950119ce66..0000000000 --- a/packages/stream_chat_flutter/lib/src/poll/interactor/poll_options_list_view.dart +++ /dev/null @@ -1,362 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/avatars/user_avatar.dart'; -import 'package:stream_chat_flutter/src/theme/poll_interactor_theme.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// {@template pollOptionsListView} -/// A widget that displays the list of poll options. -/// -/// Used in [StreamPollInteractor] to display the poll options to interact with. -/// {@endtemplate} -class PollOptionsListView extends StatelessWidget { - /// {@macro pollOptionsListView} - const PollOptionsListView({ - super.key, - required this.poll, - this.visibleOptionCount, - this.showProgressBar = false, - this.onCastVote, - this.onRemoveVote, - }); - - /// The poll to display the options for. - final Poll poll; - - /// The number of visible options in the poll. - /// - /// If null, all options will be visible. - final int? visibleOptionCount; - - /// Whether to show the voting progress bar. - /// - /// Note: This is only used when the poll is public. - final bool showProgressBar; - - /// Callback invoked when the user wants to cast a vote. - /// - /// The [PollOption] parameter is the option the user wants to vote for. - final ValueChanged? onCastVote; - - /// Callback invoked when the user wants to remove a vote. - /// - /// The [PollVote] parameter is the vote the user wants to remove. - final ValueChanged? onRemoveVote; - - void _handleVoteRemoval(PollOption option) { - final vote = poll.currentUserVoteFor(option); - if (vote == null) return; - - return onRemoveVote?.call(vote); - } - - void _handleVoteAction( - PollOption option, { - required bool checked, - }) { - if (checked) return onCastVote?.call(option); - return _handleVoteRemoval(option); - } - - @override - Widget build(BuildContext context) { - final options = switch (visibleOptionCount) { - final count? => poll.options.take(count), - _ => poll.options, - }; - - return ListView.separated( - shrinkWrap: true, - itemCount: options.length, - physics: const NeverScrollableScrollPhysics(), - separatorBuilder: (_, __) => const SizedBox(height: 8), - itemBuilder: (context, index) { - final option = options.elementAt(index); - return PollOptionItem( - key: ValueKey(option.id), - poll: poll, - option: option, - showProgressBar: showProgressBar, - onChanged: (checked) { - if (checked == null) return; - - // Handle voting based on the voting mode. - poll.votingMode.when( - disabled: () {}, // Do nothing - all: () => _handleVoteAction(option, checked: checked), - // Note: We don't need to remove the other votes in the unique - // voting mode as the backend handles it. - unique: () => _handleVoteAction(option, checked: checked), - limited: (count) => _handleVoteAction( - option, - checked: checked && poll.ownVotes.length < count, - ), - ); - }, - ); - }, - ); - } -} - -/// {@template pollOptionItem} -/// A widget that displays a poll option. -/// -/// Used in [PollOptionsListView] to display the poll options to interact with. -/// -/// This widget is used to display the poll option and the number of votes it -/// has received. Also shows the voters if the poll is public. -/// {@endtemplate} -class PollOptionItem extends StatelessWidget { - /// {@macro pollOptionItem} - const PollOptionItem({ - super.key, - required this.poll, - required this.option, - this.showProgressBar = true, - this.onChanged, - }); - - /// The poll the option belongs to. - final Poll poll; - - /// The poll option the user can interact with. - final PollOption option; - - /// Whether to show the progress bar. - /// - /// Note: This is only used when the poll is public. - final bool showProgressBar; - - /// Callback invoked when the user interacts with the option. - /// - /// The [bool] parameter is the new value of the option. - final ValueChanged? onChanged; - - @override - Widget build(BuildContext context) { - final theme = StreamPollInteractorTheme.of(context); - - final pollClosed = poll.isClosed; - final isOptionSelected = poll.hasCurrentUserVotedFor(option); - - final control = ExcludeFocus( - child: Checkbox( - value: isOptionSelected, - onChanged: pollClosed ? null : onChanged, - checkColor: theme.pollOptionCheckboxCheckColor, - shape: theme.pollOptionCheckboxShape, - side: theme.pollOptionCheckboxBorderSide, - activeColor: theme.pollOptionCheckboxActiveColor, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - visualDensity: const VisualDensity( - vertical: VisualDensity.minimumDensity, - horizontal: VisualDensity.minimumDensity, - ), - ), - ); - - return InkWell( - onTap: pollClosed ? null : () => onChanged?.call(!isOptionSelected), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: Row( - spacing: 4, - children: [ - if (pollClosed case false) control, - Expanded( - child: Column( - spacing: 4, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - spacing: 4, - children: [ - Expanded( - child: Text( - option.text, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: theme.pollOptionTextStyle, - ), - ), - // Show voters only if the poll is public. - if (poll.votingVisibility == VotingVisibility.public) - OptionVoters( - // We only show the latest 3 voters. - voters: [ - ...?poll.latestVotesByOption[option.id], - ].map((it) => it.user).whereType().take(3), - ), - Text( - poll.voteCountFor(option).toString(), - style: theme.pollOptionVoteCountTextStyle, - ), - ], - ), - if (showProgressBar) - OptionVotesProgressBar( - value: poll.voteRatioFor(option), - borderRadius: - theme.pollOptionVotesProgressBarBorderRadius ?? - BorderRadius.circular(4), - trackColor: theme.pollOptionVotesProgressBarTrackColor, - valueColor: switch (poll.isOptionWinner(option)) { - true => theme.pollOptionVotesProgressBarWinnerColor, - false => theme.pollOptionVotesProgressBarValueColor, - }, - ), - ], - ), - ) - ], - ), - ), - ); - } -} - -/// {@template optionVoters} -/// A widget that displays the voters of an option. -/// -/// Used in [PollOptionItem] to display the voters of a poll option. -/// {@endtemplate} -class OptionVoters extends StatelessWidget { - /// {@macro optionVoters} - const OptionVoters({ - super.key, - this.radius = 10, - this.overlap = 0.5, - required this.voters, - }) : assert( - overlap >= 0 && overlap <= 1, - 'Overlap must be between 0 and 1', - ); - - /// The radius of the avatars. - final double radius; - - /// The overlap between the avatars. - /// - /// The default value is 1/2 i.e. 50%. - final double overlap; - - /// The list of voters to display. - final Iterable voters; - - @override - Widget build(BuildContext context) { - if (voters.isEmpty) return const SizedBox.shrink(); - - final theme = StreamChatTheme.of(context); - - final diameter = radius * 2; - final width = diameter + (voters.length * diameter * overlap); - - var overlapPadding = 0.0; - - return SizedBox.fromSize( - size: Size(width, diameter), - child: Stack( - children: [ - ...voters.map( - (user) { - overlapPadding += diameter * overlap; - return Positioned( - right: overlapPadding - (diameter * overlap), - bottom: 0, - top: 0, - child: Container( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: theme.colorTheme.barsBg, - ), - padding: const EdgeInsets.all(1), - child: StreamUserAvatar( - user: user, - constraints: BoxConstraints.tight(Size.fromRadius(radius)), - showOnlineStatus: false, - ), - ), - ); - }, - ), - ], - ), - ); - } -} - -/// {@template optionVotesProgressBar} -/// A widget that displays the progress of the votes for an option. -/// -/// Used in [PollOptionItem] to display the progress of the votes for a -/// particular option. -/// {@endtemplate} -class OptionVotesProgressBar extends StatelessWidget { - /// {@macro optionVotesProgressBar} - const OptionVotesProgressBar({ - super.key, - required this.value, - this.minHeight = 4, - this.trackColor, - this.valueColor, - this.borderRadius = BorderRadius.zero, - }); - - /// The value of the progress bar. - final double value; - - /// The minimum height of the progress bar. - final double minHeight; - - /// The color of the track. - final Color? trackColor; - - /// The color of the value. - final Color? valueColor; - - /// The border radius of the progress bar. - /// - /// Defaults to [BorderRadius.zero]. - final BorderRadiusGeometry borderRadius; - - @override - Widget build(BuildContext context) { - final shape = RoundedRectangleBorder(borderRadius: borderRadius); - return Container( - constraints: BoxConstraints( - minWidth: double.infinity, - minHeight: minHeight, - ), - decoration: ShapeDecoration( - shape: shape, - color: trackColor, - ), - child: LayoutBuilder( - builder: (context, constraints) { - final size = constraints.constrain(Size.zero); - - final textDirection = Directionality.of(context); - final alignment = switch (textDirection) { - TextDirection.ltr => Alignment.centerLeft, - TextDirection.rtl => Alignment.centerRight, - }; - - return Align( - alignment: alignment, - child: AnimatedContainer( - height: size.height, - width: size.width * value, - duration: Durations.medium2, - decoration: ShapeDecoration( - shape: shape, - color: valueColor, - ), - ), - ); - }, - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/poll/interactor/poll_suggest_option_dialog.dart b/packages/stream_chat_flutter/lib/src/poll/interactor/poll_suggest_option_dialog.dart deleted file mode 100644 index fd4c02f934..0000000000 --- a/packages/stream_chat_flutter/lib/src/poll/interactor/poll_suggest_option_dialog.dart +++ /dev/null @@ -1,104 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/poll/stream_poll_text_field.dart'; -import 'package:stream_chat_flutter/src/theme/poll_interactor_theme.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter/src/utils/extensions.dart'; - -/// {@template showPollSuggestOptionDialog} -/// Shows a dialog that allows the user to suggest an option for a poll. -/// -/// Optionally, you can provide an [initialOption] to pre-fill the text field. -/// {@endtemplate} -Future showPollSuggestOptionDialog({ - required BuildContext context, - String initialOption = '', -}) => - showDialog( - context: context, - barrierDismissible: false, - builder: (_) => PollSuggestOptionDialog( - initialOption: initialOption, - ), - ); - -/// {@template pollSuggestOptionDialog} -/// A dialog that allows the user to suggest an option for a poll. -/// -/// Optionally, you can provide an [initialOption] to pre-fill the text field. -/// {@endtemplate} -class PollSuggestOptionDialog extends StatefulWidget { - /// {@macro pollSuggestOptionDialog} - const PollSuggestOptionDialog({ - super.key, - this.initialOption = '', - }); - - /// Initial option to be displayed in the text field. - /// - /// Defaults to an empty string. - final String initialOption; - - @override - State createState() => - _PollSuggestOptionDialogState(); -} - -class _PollSuggestOptionDialogState extends State { - late String _option = widget.initialOption; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - final pollInteractorTheme = StreamPollInteractorTheme.of(context); - - final actions = [ - TextButton( - onPressed: Navigator.of(context).pop, - style: TextButton.styleFrom( - textStyle: theme.textTheme.headlineBold, - foregroundColor: theme.colorTheme.accentPrimary, - disabledForegroundColor: theme.colorTheme.disabled, - ), - child: Text(context.translations.cancelLabel.toUpperCase()), - ), - TextButton( - onPressed: switch (_option == widget.initialOption) { - true => null, - false => () => Navigator.of(context).pop(_option), - }, - style: TextButton.styleFrom( - textStyle: theme.textTheme.headlineBold, - foregroundColor: theme.colorTheme.accentPrimary, - disabledForegroundColor: theme.colorTheme.disabled, - ), - child: Text(context.translations.sendLabel.toUpperCase()), - ), - ]; - - return AlertDialog( - title: Text( - context.translations.suggestAnOptionLabel, - style: pollInteractorTheme.pollActionDialogTitleStyle, - ), - actions: actions, - titlePadding: const EdgeInsets.symmetric(vertical: 14, horizontal: 16), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), - contentPadding: const EdgeInsets.all(16), - actionsPadding: const EdgeInsets.all(8), - backgroundColor: theme.colorTheme.appBg, - content: StreamPollTextField( - autoFocus: true, - initialValue: _option, - hintText: context.translations.enterANewOptionLabel, - contentPadding: const EdgeInsets.symmetric( - vertical: 12, - horizontal: 16, - ), - style: pollInteractorTheme.pollActionDialogTextFieldStyle, - fillColor: pollInteractorTheme.pollActionDialogTextFieldFillColor, - borderRadius: pollInteractorTheme.pollActionDialogTextFieldBorderRadius, - onChanged: (value) => setState(() => _option = value), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/poll/interactor/stream_poll_interactor.dart b/packages/stream_chat_flutter/lib/src/poll/interactor/stream_poll_interactor.dart deleted file mode 100644 index 035ea76e1e..0000000000 --- a/packages/stream_chat_flutter/lib/src/poll/interactor/stream_poll_interactor.dart +++ /dev/null @@ -1,136 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/poll/interactor/poll_footer.dart'; -import 'package:stream_chat_flutter/src/poll/interactor/poll_header.dart'; -import 'package:stream_chat_flutter/src/poll/interactor/poll_options_list_view.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// {@template streamPollInteractor} -/// A widget that allows users to interact with a poll. -/// -/// The widget provides various callbacks to interact with the poll. -/// - [onCastVote] is called when the user wants to cast a vote. -/// - [onRemoveVote] is called when the user wants to remove a vote. -/// - [onEndVote] is called when the user wants to end the voting. -/// - [onAddComment] is called when the user wants to add a comment. -/// - [onViewComments] is called when the user wants to view all the comments. -/// - [onViewResults] is called when the user wants to view the poll results. -/// - [onSuggestOption] is called when the user wants to suggest an option. -/// - [onSeeMoreOptions] is called when the user wants to see more options. -/// -/// The widget also provides a [visibleOptionCount] parameter to control the -/// number of visible options in the poll. If null, all options will be visible. -/// {@endtemplate} -class StreamPollInteractor extends StatelessWidget { - /// {@macro streamPollInteractor} - const StreamPollInteractor({ - super.key, - required this.poll, - required this.currentUser, - this.padding = const EdgeInsets.symmetric( - vertical: 12, - horizontal: 10, - ), - this.visibleOptionCount, - this.onCastVote, - this.onRemoveVote, - this.onEndVote, - this.onAddComment, - this.onViewComments, - this.onViewResults, - this.onSuggestOption, - this.onSeeMoreOptions, - }); - - /// The poll to interact with. - final Poll poll; - - /// The current user interacting with the poll. - final User currentUser; - - /// The padding to apply to the interactor. - final EdgeInsetsGeometry padding; - - /// The number of visible options in the poll. - /// - /// If null, all options will be visible. - final int? visibleOptionCount; - - /// Callback invoked when the user wants to cast a vote. - /// - /// The [PollOption] parameter is the option the user wants to vote for. - final ValueChanged? onCastVote; - - /// Callback invoked when the user wants to remove a vote. - /// - /// The [PollVote] parameter is the vote the user wants to remove. - final ValueChanged? onRemoveVote; - - /// Callback invoked when the user wants to end the voting. - /// - /// This is only invoked if the [currentUser] is the creator of the poll. - final VoidCallback? onEndVote; - - /// Callback invoked when the user wants to add a comment. - /// - /// This is only invoked if the poll allows adding answers. - final VoidCallback? onAddComment; - - /// Callback invoked when the user wants to view all the comments. - /// - /// This is only invoked if the poll contains answers. - final VoidCallback? onViewComments; - - /// Callback invoked when the user wants to view the poll results. - final VoidCallback? onViewResults; - - /// Callback invoked when the user wants to suggest an option. - /// - /// This is only invoked if the poll allows user suggested options. - final VoidCallback? onSuggestOption; - - /// Callback invoked when the user wants to see more options. - /// - /// This is only invoked if the poll has more options than - /// [visibleOptionCount]. - final VoidCallback? onSeeMoreOptions; - - @override - Widget build(BuildContext context) { - return Padding( - padding: padding, - child: Column( - spacing: 8, - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - PollHeader(poll: poll), - MediaQuery.removePadding( - context: context, - // Workaround for the bottom padding issue. - // Link: https://github.com/flutter/flutter/issues/156149 - removeTop: true, - removeBottom: true, - child: PollOptionsListView( - poll: poll, - showProgressBar: true, - visibleOptionCount: visibleOptionCount, - onCastVote: onCastVote, - onRemoveVote: onRemoveVote, - ), - ), - PollFooter( - poll: poll, - currentUser: currentUser, - visibleOptionCount: visibleOptionCount, - onEndVote: onEndVote, - onAddComment: onAddComment, - onViewComments: onViewComments, - onViewResults: onViewResults, - onSuggestOption: onSuggestOption, - onSeeMoreOptions: onSeeMoreOptions, - ), - ], - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/poll/stream_poll_comments_dialog.dart b/packages/stream_chat_flutter/lib/src/poll/stream_poll_comments_dialog.dart deleted file mode 100644 index 0a42012626..0000000000 --- a/packages/stream_chat_flutter/lib/src/poll/stream_poll_comments_dialog.dart +++ /dev/null @@ -1,161 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/poll/interactor/poll_add_comment_dialog.dart'; -import 'package:stream_chat_flutter/src/scroll_view/poll_vote_scroll_view/stream_poll_vote_list_view.dart'; -import 'package:stream_chat_flutter/src/theme/poll_comments_dialog_theme.dart'; -import 'package:stream_chat_flutter/src/utils/extensions.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// {@template showStreamPollCommentsDialog} -/// Displays an interactive dialog to show all the comments for a poll. -/// -/// The comments are paginated and get's loaded as the user scrolls. -/// -/// The dialog also allows the user to update their comment. -/// {@endtemplate} -Future showStreamPollCommentsDialog({ - required BuildContext context, - required ValueListenable messageNotifier, -}) { - final navigator = Navigator.of(context); - return navigator.push( - MaterialPageRoute( - fullscreenDialog: true, - builder: (_) => StreamChannel( - channel: StreamChannel.of(context).channel, - child: ValueListenableBuilder( - valueListenable: messageNotifier, - builder: (context, message, child) { - final poll = message.poll; - if (poll == null) return const SizedBox.shrink(); - - final channel = StreamChannel.of(context).channel; - - Future onUpdateComment() async { - final commentText = await showPollAddCommentDialog( - context: context, - // We use the first answer as the initial value because the - // user can only add one comment per poll. - initialValue: poll.ownAnswers.firstOrNull?.answerText ?? '', - ); - - if (commentText == null) return; - channel.addPollAnswer(message, poll, answerText: commentText); - } - - return StreamPollCommentsDialog( - poll: poll, - onUpdateComment: onUpdateComment, - ); - }, - ), - ), - ), - ); -} - -/// {@template streamPollCommentsDialog} -/// A dialog that displays all the comments for a poll. -/// -/// The comments are paginated and get's loaded as the user scrolls. -/// -/// Provides a callback to update the user's comment. -/// {@endtemplate} -class StreamPollCommentsDialog extends StatefulWidget { - /// {@macro streamPollCommentsDialog} - const StreamPollCommentsDialog({ - super.key, - required this.poll, - this.onUpdateComment, - }); - - /// The poll to display the options for. - final Poll poll; - - /// Callback invoked when the user wants to cast a vote. - final VoidCallback? onUpdateComment; - - @override - State createState() => - _StreamPollCommentsDialogState(); -} - -class _StreamPollCommentsDialogState extends State { - late StreamPollVoteListController _controller; - - @override - void initState() { - super.initState(); - _initializeController(); - } - - @override - void didUpdateWidget(covariant StreamPollCommentsDialog oldWidget) { - super.didUpdateWidget(oldWidget); - if (oldWidget.poll.id != widget.poll.id) { - _controller.dispose(); // Dispose the old controller. - _initializeController(); // Initialize a new controller. - } - } - - void _initializeController() { - _controller = StreamPollVoteListController( - pollId: widget.poll.id, - channel: StreamChannel.of(context).channel, - filter: Filter.equal('is_answer', true), - ); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final theme = StreamPollCommentsDialogTheme.of(context); - - return Scaffold( - backgroundColor: theme.backgroundColor, - appBar: AppBar( - elevation: theme.appBarElevation, - backgroundColor: theme.appBarBackgroundColor, - title: Text( - context.translations.pollCommentsLabel, - style: theme.appBarTitleTextStyle, - ), - ), - body: RefreshIndicator.adaptive( - onRefresh: _controller.refresh, - child: StreamPollVoteListView( - controller: _controller, - padding: const EdgeInsets.all(16), - itemBuilder: (context, _, __, defaultWidget) { - return defaultWidget.copyWith( - showAnswerText: true, - contentPadding: const EdgeInsets.all(16), - borderRadius: theme.pollCommentItemBorderRadius, - tileColor: theme.pollCommentItemBackgroundColor, - ); - }, - ), - ), - bottomNavigationBar: widget.poll.isClosed - ? null - : SafeArea( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: FilledButton.tonal( - onPressed: widget.onUpdateComment, - style: theme.updateYourCommentButtonStyle, - child: Text(switch (widget.poll.ownAnswers.isEmpty) { - true => context.translations.addACommentLabel, - false => context.translations.updateYourCommentLabel, - }), - ), - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/poll/stream_poll_option_votes_dialog.dart b/packages/stream_chat_flutter/lib/src/poll/stream_poll_option_votes_dialog.dart deleted file mode 100644 index a68e43e043..0000000000 --- a/packages/stream_chat_flutter/lib/src/poll/stream_poll_option_votes_dialog.dart +++ /dev/null @@ -1,173 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/icons/stream_svg_icon.dart'; -import 'package:stream_chat_flutter/src/scroll_view/poll_vote_scroll_view/stream_poll_vote_list_view.dart'; -import 'package:stream_chat_flutter/src/theme/poll_option_votes_dialog_theme.dart'; -import 'package:stream_chat_flutter/src/utils/extensions.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// {@template showStreamPollOptionVotesDialog} -/// Displays an interactive dialog to show all the votes for a poll option. -/// -/// The votes are paginated and get's loaded as the user scrolls. -/// {@endtemplate} -Future showStreamPollOptionVotesDialog({ - required BuildContext context, - required ValueListenable messageNotifier, - required PollOption option, -}) { - final navigator = Navigator.of(context); - return navigator.push( - MaterialPageRoute( - fullscreenDialog: true, - builder: (_) => StreamChannel( - channel: StreamChannel.of(context).channel, - child: ValueListenableBuilder( - valueListenable: messageNotifier, - builder: (context, message, child) { - final poll = message.poll; - if (poll == null) return const SizedBox.shrink(); - if (option.id == null) return const SizedBox.shrink(); - - return StreamPollOptionVotesDialog( - poll: poll, - option: option, - pollVotesCount: poll.voteCountsByOption[option.id], - ); - }, - ), - ), - ), - ); -} - -/// {@template streamPollOptionVotesDialog} -/// A dialog that displays all the votes for a poll option. -/// -/// The votes are paginated and get's loaded as the user scrolls. -/// {@endtemplate} -class StreamPollOptionVotesDialog extends StatefulWidget { - /// {@macro streamPollOptionVotesDialog} - const StreamPollOptionVotesDialog({ - super.key, - required this.poll, - required this.option, - required this.pollVotesCount, - }); - - /// The poll for which the votes are being displayed. - final Poll poll; - - /// The option for which the votes are being displayed. - final PollOption option; - - /// The total number of votes for the option. - final int? pollVotesCount; - - @override - State createState() => - _StreamPollOptionVotesDialogState(); -} - -class _StreamPollOptionVotesDialogState - extends State { - late StreamPollVoteListController _controller; - - @override - void initState() { - super.initState(); - _initializeController(); - } - - @override - void didUpdateWidget(covariant StreamPollOptionVotesDialog oldWidget) { - super.didUpdateWidget(oldWidget); - if (oldWidget.poll.id != widget.poll.id || - oldWidget.option.id != widget.option.id) { - _controller.dispose(); // Dispose the old controller. - _initializeController(); // Initialize a new controller. - } - } - - void _initializeController() { - _controller = StreamPollVoteListController( - pollId: widget.poll.id, - channel: StreamChannel.of(context).channel, - filter: Filter.equal('option_id', widget.option.id!), - ); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final theme = StreamPollOptionVotesDialogTheme.of(context); - - final isOptionWinner = widget.poll.isOptionWithMaximumVotes(widget.option); - - return Scaffold( - backgroundColor: theme.backgroundColor, - appBar: AppBar( - elevation: theme.appBarElevation, - backgroundColor: theme.appBarBackgroundColor, - title: Text( - widget.option.text, - maxLines: 2, - style: theme.appBarTitleTextStyle, - ), - ), - body: Padding( - padding: const EdgeInsets.all(16), - child: Column( - spacing: 16, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - if (isOptionWinner) ...[ - StreamSvgIcon( - icon: StreamSvgIcons.award, - color: theme.pollOptionWinnerVoteCountTextStyle?.color, - ), - const SizedBox(width: 8), - ], - Text( - context.translations.voteCountLabel( - count: widget.pollVotesCount, - ), - style: isOptionWinner - ? theme.pollOptionWinnerVoteCountTextStyle - : theme.pollOptionVoteCountTextStyle, - ), - ], - ), - Expanded( - child: MediaQuery.removePadding( - context: context, - removeTop: true, - removeBottom: true, - child: RefreshIndicator.adaptive( - onRefresh: _controller.refresh, - child: StreamPollVoteListView( - controller: _controller, - itemBuilder: (context, _, __, defaultWidget) { - return defaultWidget.copyWith( - contentPadding: const EdgeInsets.all(16), - borderRadius: theme.pollOptionVoteItemBorderRadius, - tileColor: theme.pollOptionVoteItemBackgroundColor, - ); - }, - ), - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/poll/stream_poll_options_dialog.dart b/packages/stream_chat_flutter/lib/src/poll/stream_poll_options_dialog.dart deleted file mode 100644 index b5933c2819..0000000000 --- a/packages/stream_chat_flutter/lib/src/poll/stream_poll_options_dialog.dart +++ /dev/null @@ -1,151 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/poll/interactor/poll_options_list_view.dart'; -import 'package:stream_chat_flutter/src/theme/poll_options_dialog_theme.dart'; -import 'package:stream_chat_flutter/src/utils/extensions.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// {@template showStreamPollOptionsDialog} -/// Displays an interactive dialog to show all the available options for a poll. -/// -/// The dialog allows the user to cast a vote or remove a vote. -/// {@endtemplate} -Future showStreamPollOptionsDialog({ - required BuildContext context, - required ValueListenable messageNotifier, -}) { - final navigator = Navigator.of(context); - return navigator.push( - MaterialPageRoute( - fullscreenDialog: true, - builder: (_) => StreamChannel( - channel: StreamChannel.of(context).channel, - child: ValueListenableBuilder( - valueListenable: messageNotifier, - builder: (context, message, child) { - final poll = message.poll; - if (poll == null) return const SizedBox.shrink(); - - final channel = StreamChannel.of(context).channel; - - void onCastVote(PollOption option) { - channel.castPollVote(message, poll, option); - } - - void onRemoveVote(PollVote vote) { - channel.removePollVote(message, poll, vote); - } - - return StreamPollOptionsDialog( - poll: poll, - onCastVote: onCastVote, - onRemoveVote: onRemoveVote, - ); - }, - ), - ), - ), - ); -} - -/// {@template streamPollOptionsDialog} -/// A dialog that displays all the available options for a poll. -/// -/// Provides callbacks when a vote has been cast or removed from the poll. -/// {@endtemplate} -class StreamPollOptionsDialog extends StatelessWidget { - /// {@macro streamPollOptionsDialog} - const StreamPollOptionsDialog({ - super.key, - required this.poll, - this.onCastVote, - this.onRemoveVote, - }); - - /// The poll to display the options for. - final Poll poll; - - /// Callback invoked when the user wants to cast a vote. - /// - /// The [PollOption] parameter is the option the user wants to vote for. - final ValueChanged? onCastVote; - - /// Callback invoked when the user wants to remove a vote. - /// - /// The [PollVote] parameter is the vote the user wants to remove. - final ValueChanged? onRemoveVote; - - @override - Widget build(BuildContext context) { - final theme = StreamPollOptionsDialogTheme.of(context); - - return Scaffold( - backgroundColor: theme.backgroundColor, - appBar: AppBar( - elevation: theme.appBarElevation, - backgroundColor: theme.appBarBackgroundColor, - title: Text( - context.translations.pollOptionsLabel, - style: theme.appBarTitleTextStyle, - ), - ), - body: ListView( - padding: const EdgeInsets.all(16), - children: [ - PollOptionsQuestion( - question: poll.name, - ), - Container( - padding: const EdgeInsets.symmetric( - vertical: 12, - horizontal: 16, - ), - decoration: theme.pollOptionsListViewDecoration, - child: MediaQuery.removePadding( - context: context, - removeTop: true, - removeBottom: true, - child: PollOptionsListView( - poll: poll, - onCastVote: onCastVote, - onRemoveVote: onRemoveVote, - ), - ), - ), - ].insertBetween(const SizedBox(height: 32)), - ), - ); - } -} - -/// {@template pollOptionsQuestion} -/// A widget that displays the question of a poll. -/// {@endtemplate} -class PollOptionsQuestion extends StatelessWidget { - /// {@macro pollOptionsQuestion} - const PollOptionsQuestion({ - super.key, - required this.question, - }); - - /// The question of the poll. - final String question; - - @override - Widget build(BuildContext context) { - final theme = StreamPollOptionsDialogTheme.of(context); - - return Container( - padding: const EdgeInsets.all(16), - decoration: theme.pollTitleDecoration, - constraints: const BoxConstraints( - minHeight: 56, - minWidth: double.infinity, - ), - child: Text( - question, - style: theme.pollTitleTextStyle, - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/poll/stream_poll_results_dialog.dart b/packages/stream_chat_flutter/lib/src/poll/stream_poll_results_dialog.dart deleted file mode 100644 index 10719a6092..0000000000 --- a/packages/stream_chat_flutter/lib/src/poll/stream_poll_results_dialog.dart +++ /dev/null @@ -1,333 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/icons/stream_svg_icon.dart'; -import 'package:stream_chat_flutter/src/poll/stream_poll_option_votes_dialog.dart'; -import 'package:stream_chat_flutter/src/scroll_view/poll_vote_scroll_view/stream_poll_vote_list_tile.dart'; -import 'package:stream_chat_flutter/src/theme/poll_results_dialog_theme.dart'; -import 'package:stream_chat_flutter/src/utils/extensions.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// {@template showStreamPollResultsDialog} -/// Displays an interactive dialog to show the results of a poll. -/// -/// The dialog allows the user to see the results of the poll. The results are -/// displayed in a list of options with the number of votes each option has -/// received and the users who have voted for that option. -/// -/// By default, only the first 5 votes are shown for each option. The user can -/// see all the votes for an option by pressing the "Show all votes" button. -/// -/// The dialog is updated in real-time as new votes are cast. -/// -/// {@endtemplate} -Future showStreamPollResultsDialog({ - required BuildContext context, - required ValueListenable messageNotifier, -}) { - final navigator = Navigator.of(context); - return navigator.push( - MaterialPageRoute( - fullscreenDialog: true, - builder: (_) => StreamChannel( - channel: StreamChannel.of(context).channel, - child: ValueListenableBuilder( - valueListenable: messageNotifier, - builder: (context, message, child) { - final poll = message.poll; - if (poll == null) return const SizedBox.shrink(); - - void onShowAllVotesPressed(PollOption option) { - showStreamPollOptionVotesDialog( - context: context, - messageNotifier: messageNotifier, - option: option, - ); - } - - return StreamPollResultsDialog( - poll: poll, - visibleVotesCount: 5, - onShowAllVotesPressed: onShowAllVotesPressed, - ); - }, - ), - ), - ), - ); -} - -/// {@template streamPollResultsDialog} -/// A dialog that displays the results of a poll. -/// -/// The results are displayed in a list of options with the number of votes each -/// option has received and the users who have voted for that option. -/// -/// By default, only the latest votes are shown for each option. The user can -/// see all the votes for an option by pressing the "Show all votes" button. -/// -/// The dialog is updated in real-time as new votes are cast. -/// {@endtemplate} -class StreamPollResultsDialog extends StatelessWidget { - /// {@macro streamPollResultsDialog} - const StreamPollResultsDialog({ - super.key, - required this.poll, - this.visibleVotesCount, - this.onShowAllVotesPressed, - }); - - /// The poll to display the results for. - final Poll poll; - - /// The number of votes to show for each option. - final int? visibleVotesCount; - - /// Callback invoked when the "Show all votes" button is pressed. - final ValueSetter? onShowAllVotesPressed; - - @override - Widget build(BuildContext context) { - final theme = StreamPollResultsDialogTheme.of(context); - - return Scaffold( - backgroundColor: theme.backgroundColor, - appBar: AppBar( - elevation: theme.appBarElevation, - backgroundColor: theme.appBarBackgroundColor, - title: Text( - context.translations.pollResultsLabel, - style: theme.appBarTitleTextStyle, - ), - ), - body: ListView( - padding: const EdgeInsets.all(16), - children: [ - PollResultsQuestion(question: poll.name), - PollVotesByOptionListView( - poll: poll, - visibleVotesCount: visibleVotesCount, - onShowAllVotesPressed: onShowAllVotesPressed, - ), - ].insertBetween(const SizedBox(height: 32)), - ), - ); - } -} - -/// {@template pollResultsQuestion} -/// A widget that displays the question of a poll. -/// {@endtemplate} -class PollResultsQuestion extends StatelessWidget { - /// {@macro pollResultsQuestion} - const PollResultsQuestion({ - super.key, - required this.question, - }); - - /// The question of the poll. - final String question; - - @override - Widget build(BuildContext context) { - final theme = StreamPollResultsDialogTheme.of(context); - - return Container( - padding: const EdgeInsets.all(16), - decoration: theme.pollTitleDecoration, - constraints: const BoxConstraints( - minHeight: 56, - minWidth: double.infinity, - ), - child: Text( - question, - style: theme.pollTitleTextStyle, - ), - ); - } -} - -/// {@template pollVotesByOptionListView} -/// A list of poll options with the latest votes for each option. -/// -/// Displays a button with a callback [onShowAllVotesPressed] to show all votes -/// for an option if there are more votes than the [visibleVotesCount]. -/// -/// By default, The options are sorted by the number of votes they have -/// received in descending order. -/// {@endtemplate} -class PollVotesByOptionListView extends StatelessWidget { - /// {@macro pollVotesByOptionListView} - const PollVotesByOptionListView({ - super.key, - required this.poll, - this.visibleVotesCount, - this.onShowAllVotesPressed, - }); - - /// The poll the options are for. - final Poll poll; - - /// The number of votes to show for each option. - /// - /// If the number of votes for an option is greater than this value, a button - /// is displayed to show all votes for that option. - final int? visibleVotesCount; - - /// Callback invoked when the "Show all votes" button is pressed. - final ValueSetter? onShowAllVotesPressed; - - @override - Widget build(BuildContext context) { - final latestVotesByOption = poll.latestVotesByOption; - final voteCountsByOption = poll.voteCountsByOption; - final pollOptions = poll.options.sorted((a, b) { - final optionAVoteCounts = voteCountsByOption[a.id] ?? 0; - final optionBVoteCounts = voteCountsByOption[b.id] ?? 0; - return optionBVoteCounts.compareTo(optionAVoteCounts); - }); - - return ListView.separated( - shrinkWrap: true, - itemCount: pollOptions.length, - physics: const NeverScrollableScrollPhysics(), - separatorBuilder: (context, index) => const SizedBox(height: 8), - itemBuilder: (context, index) { - final option = pollOptions.elementAt(index); - final latestPollVotes = latestVotesByOption[option.id] ?? []; - final pollVotesCount = voteCountsByOption[option.id] ?? 0; - - return PollVotesByOptionItem( - option: option, - pollVotesCount: pollVotesCount, - latestPollVotes: latestPollVotes, - visibleVotesCount: visibleVotesCount, - isOptionWinner: poll.isOptionWithMaximumVotes(option), - onShowAllVotesPressed: switch (onShowAllVotesPressed) { - final onShowAllVotesPressed? => () => onShowAllVotesPressed(option), - _ => null, - }, - ); - }, - ); - } -} - -/// {@template pollVotesByOptionItem} -/// A widget that displays the votes for a poll option. -/// -/// The widget is used in [PollVotesByOptionListView] to display the votes for -/// each option in a poll. -/// -/// Displays a award icon next to the option if [isOptionWinner] is true. -/// {@endtemplate} -class PollVotesByOptionItem extends StatelessWidget { - /// {@macro pollVotesByOptionItem} - const PollVotesByOptionItem({ - super.key, - required this.option, - required this.latestPollVotes, - required this.pollVotesCount, - this.isOptionWinner = false, - this.visibleVotesCount, - this.onShowAllVotesPressed, - }); - - /// The option to display the votes for. - final PollOption option; - - /// The available latest votes for the option. - final List latestPollVotes; - - /// The total number of votes for the option. - final int pollVotesCount; - - /// Whether the option is the winner of the poll. - final bool isOptionWinner; - - /// The number of votes to show for the option. - /// - /// If this is less than the [pollVotesCount] a button is displayed to show - /// all votes for the option. - final int? visibleVotesCount; - - /// Callback invoked when the "Show all votes" button is pressed. - final VoidCallback? onShowAllVotesPressed; - - @override - Widget build(BuildContext context) { - final theme = StreamPollResultsDialogTheme.of(context); - - final votes = switch (visibleVotesCount) { - final visibleVotesCount? => latestPollVotes.take(visibleVotesCount), - _ => latestPollVotes, - }; - - return Container( - padding: const EdgeInsets.symmetric( - vertical: 12, - horizontal: 16, - ), - decoration: isOptionWinner - ? theme.pollOptionsWinnerDecoration - : theme.pollOptionsDecoration, - child: Column( - spacing: 16, - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Row( - children: [ - Expanded( - child: Text( - option.text, - style: isOptionWinner - ? theme.pollOptionsWinnerTextStyle - : theme.pollOptionsTextStyle, - ), - ), - const SizedBox(width: 8), - if (isOptionWinner) ...[ - StreamSvgIcon( - icon: StreamSvgIcons.award, - color: theme.pollOptionsWinnerVoteCountTextStyle?.color, - ), - const SizedBox(width: 8), - ], - Text( - context.translations.voteCountLabel(count: pollVotesCount), - style: isOptionWinner - ? theme.pollOptionsWinnerVoteCountTextStyle - : theme.pollOptionsVoteCountTextStyle, - ), - ], - ), - if (votes.isNotEmpty) - Flexible( - child: ListView.separated( - shrinkWrap: true, - itemCount: votes.length, - physics: const NeverScrollableScrollPhysics(), - separatorBuilder: (_, __) => const SizedBox(height: 16), - itemBuilder: (context, index) { - final pollVote = votes.elementAt(index); - return StreamPollVoteListTile( - pollVote: pollVote, - contentPadding: const EdgeInsets.symmetric(vertical: 6), - ); - }, - ), - ), - if (votes.length < latestPollVotes.length) - TextButton( - onPressed: onShowAllVotesPressed, - style: theme.pollOptionsShowAllVotesButtonStyle, - child: Text( - context.translations.showAllVotesLabel(count: pollVotesCount), - ), - ), - ], - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/poll/stream_poll_text_field.dart b/packages/stream_chat_flutter/lib/src/poll/stream_poll_text_field.dart deleted file mode 100644 index a620656d2d..0000000000 --- a/packages/stream_chat_flutter/lib/src/poll/stream_poll_text_field.dart +++ /dev/null @@ -1,267 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -const _kTransitionDuration = Duration(milliseconds: 167); - -/// {@template streamPollTextField} -/// A widget that represents a text field for poll input. -/// {@endtemplate} -class StreamPollTextField extends StatefulWidget { - /// {@macro streamPollTextField} - const StreamPollTextField({ - super.key, - this.initialValue, - this.style, - this.enabled = true, - this.hintText, - this.fillColor, - this.errorText, - this.errorStyle, - this.contentPadding = const EdgeInsets.symmetric( - vertical: 18, - horizontal: 16, - ), - this.borderRadius, - this.focusNode, - this.keyboardType, - this.autoFocus = false, - this.onChanged, - }); - - /// The initial value of the text field. - /// - /// If `null`, the text field will be empty. - final String? initialValue; - - /// The style to use for the text field. - final TextStyle? style; - - /// Whether the text field is enabled. - final bool enabled; - - /// The hint text to be displayed in the text field. - final String? hintText; - - /// The fill color of the text field. - final Color? fillColor; - - /// The error text to be displayed below the text field. - /// - /// If `null`, no error text will be displayed. - final String? errorText; - - /// The style to use for the error text. - final TextStyle? errorStyle; - - /// The padding around the text field content. - final EdgeInsetsGeometry contentPadding; - - /// The border radius of the text field. - final BorderRadius? borderRadius; - - /// The keyboard type of the text field. - final TextInputType? keyboardType; - - /// Whether the text field should autofocus. - final bool autoFocus; - - /// The focus node of the text field. - final FocusNode? focusNode; - - /// Callback called when the text field value is changed. - final ValueChanged? onChanged; - - @override - State createState() => _StreamPollTextFieldState(); -} - -class _StreamPollTextFieldState extends State { - late final _controller = TextEditingController(text: widget.initialValue); - - @override - void didUpdateWidget(covariant StreamPollTextField oldWidget) { - super.didUpdateWidget(oldWidget); - // Update the controller value if the updated initial value is different - // from the current value. - final currValue = _controller.text; - final newValue = widget.initialValue; - if (currValue != newValue) { - _controller.value = switch (newValue) { - final value? => TextEditingValue( - text: value, - selection: TextSelection.collapsed(offset: value.length), - ), - _ => TextEditingValue.empty, - }; - } - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - - // Reduce vertical padding if there is an error text. - var contentPadding = widget.contentPadding; - final verticalPadding = contentPadding.vertical; - final horizontalPadding = contentPadding.horizontal; - if (widget.errorText != null) { - contentPadding = contentPadding.subtract( - EdgeInsets.symmetric(vertical: verticalPadding / 4), - ); - } - - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - PollTextFieldError( - padding: EdgeInsets.only( - top: verticalPadding / 4, - left: horizontalPadding / 2, - right: horizontalPadding / 2, - ), - errorText: widget.errorText, - errorStyle: widget.errorStyle ?? - theme.textTheme.footnote.copyWith( - color: theme.colorTheme.accentError, - ), - ), - TextField( - autocorrect: false, - controller: _controller, - focusNode: widget.focusNode, - onChanged: widget.onChanged, - style: widget.style ?? theme.textTheme.headline, - keyboardType: widget.keyboardType, - autofocus: widget.autoFocus, - decoration: InputDecoration( - filled: true, - isCollapsed: true, - enabled: widget.enabled, - fillColor: widget.fillColor, - hintText: widget.hintText, - hintStyle: (widget.style ?? theme.textTheme.headline).copyWith( - color: theme.colorTheme.textLowEmphasis, - ), - contentPadding: contentPadding, - border: OutlineInputBorder( - borderRadius: widget.borderRadius ?? BorderRadius.circular(12), - borderSide: BorderSide.none, - ), - ), - ), - ], - ); - } -} - -/// {@template pollTextFieldError} -/// A widget that displays an error text around a text field with a fade -/// transition. -/// -/// Usually used with [StreamPollTextField]. -/// {@endtemplate} -class PollTextFieldError extends StatefulWidget { - /// {@macro pollTextFieldError} - const PollTextFieldError({ - super.key, - this.errorText, - this.errorStyle, - this.errorMaxLines, - this.textAlign, - this.padding, - }); - - /// The error text to be displayed. - final String? errorText; - - /// The maximum number of lines for the error text. - final int? errorMaxLines; - - /// The alignment of the error text. - final TextAlign? textAlign; - - /// The style of the error text. - final TextStyle? errorStyle; - - /// The padding around the error text. - final EdgeInsetsGeometry? padding; - - @override - State createState() => _PollTextFieldErrorState(); -} - -class _PollTextFieldErrorState extends State - with SingleTickerProviderStateMixin { - late AnimationController _controller; - - @override - void initState() { - super.initState(); - _controller = AnimationController( - duration: _kTransitionDuration, - vsync: this, - )..addListener(() => setState(() {})); - - if (widget.errorText != null) { - _controller.value = 1.0; - } - } - - @override - void didUpdateWidget(covariant PollTextFieldError oldWidget) { - super.didUpdateWidget(oldWidget); - // Animate the error text if the error text state has changed. - final newError = widget.errorText; - final currError = oldWidget.errorText; - final errorTextStateChanged = (newError != null) != (currError != null); - if (errorTextStateChanged) { - if (newError != null) { - _controller.forward(); - } else { - _controller.reverse(); - } - } - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final errorText = widget.errorText; - if (errorText == null) return const SizedBox.shrink(); - - return Container( - padding: widget.padding, - child: Semantics( - container: true, - child: FadeTransition( - opacity: _controller, - child: FractionalTranslation( - translation: Tween( - begin: const Offset(0, 0.25), - end: Offset.zero, - ).evaluate(_controller.view), - child: Text( - errorText, - style: widget.errorStyle, - textAlign: widget.textAlign, - overflow: TextOverflow.ellipsis, - maxLines: widget.errorMaxLines, - ), - ), - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_grid_tile.dart b/packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_grid_tile.dart deleted file mode 100644 index dc685d09ae..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_grid_tile.dart +++ /dev/null @@ -1,91 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// A widget that displays a user. -/// -/// This widget is intended to be used as a Tile in -/// [StreamChannelGridView]. -/// -/// It shows the user's avatar and name. -/// -/// See also: -/// * [StreamChannelGridView] -/// * [StreamUserAvatar] -class StreamChannelGridTile extends StatelessWidget { - /// Creates a new instance of [StreamChannelGridTile] widget. - const StreamChannelGridTile({ - super.key, - required this.channel, - this.child, - this.footer, - this.onTap, - this.onLongPress, - }); - - /// The channel to display. - final Channel channel; - - /// The widget to display in the body of the tile. - final Widget? child; - - /// The widget to display in the footer of the tile. - final Widget? footer; - - /// Called when the user taps this grid tile. - final GestureTapCallback? onTap; - - /// Called when the user long-presses on this grid tile. - final GestureLongPressCallback? onLongPress; - - /// Creates a copy of this tile but with the given fields replaced with - /// the new values. - StreamChannelGridTile copyWith({ - Key? key, - Channel? channel, - Widget? child, - Widget? footer, - GestureTapCallback? onTap, - GestureLongPressCallback? onLongPress, - }) => - StreamChannelGridTile( - key: key ?? this.key, - channel: channel ?? this.channel, - footer: footer ?? this.footer, - onTap: onTap ?? this.onTap, - onLongPress: onLongPress ?? this.onLongPress, - child: child ?? this.child, - ); - - @override - Widget build(BuildContext context) { - final channelPreviewTheme = StreamChannelPreviewTheme.of(context); - - final child = this.child ?? - StreamChannelAvatar( - channel: channel, - borderRadius: BorderRadius.circular(32), - constraints: const BoxConstraints.tightFor( - height: 64, - width: 64, - ), - ); - - final footer = this.footer ?? - StreamChannelName( - channel: channel, - textStyle: channelPreviewTheme.titleStyle, - ); - - return InkWell( - onTap: onTap, - onLongPress: onLongPress, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - child, - footer, - ], - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_grid_view.dart b/packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_grid_view.dart deleted file mode 100644 index e391451647..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_grid_view.dart +++ /dev/null @@ -1,396 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_error_widget.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_error.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_indicator.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_loading_widget.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Default grid delegate for [StreamChannelGridView]. -const defaultChannelGridViewDelegate = - SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 4); - -/// Signature for the item builder that creates the children of the -/// [StreamChannelGridView]. -typedef StreamChannelGridViewIndexedWidgetBuilder - = StreamScrollViewIndexedWidgetBuilder; - -/// A [GridView] that shows a grid of [User]s, -/// it uses [StreamChannelGridTile] as a default item. -/// -/// Example: -/// -/// ```dart -/// StreamChannelGridView( -/// controller: controller, -/// onChannelTap: (channel) { -/// // Handle channel tap event -/// }, -/// onChannelLongPress: (channel) { -/// // Handle channel long press event -/// }, -/// ) -/// ``` -/// -/// See also: -/// * [StreamChannelGridTile] -/// * [StreamChannelListController] -class StreamChannelGridView extends StatelessWidget { - /// Creates a new instance of [StreamChannelGridView]. - const StreamChannelGridView({ - super.key, - required this.controller, - this.gridDelegate = defaultChannelGridViewDelegate, - this.itemBuilder, - this.emptyBuilder, - this.loadMoreErrorBuilder, - this.loadMoreIndicatorBuilder, - this.loadingBuilder, - this.errorBuilder, - this.onChannelTap, - this.onChannelLongPress, - this.loadMoreTriggerIndex = 3, - this.scrollDirection = Axis.vertical, - this.reverse = false, - this.scrollController, - this.primary, - this.physics, - this.shrinkWrap = false, - this.padding, - this.addAutomaticKeepAlives = true, - this.addRepaintBoundaries = true, - this.addSemanticIndexes = true, - this.cacheExtent, - this.semanticChildCount, - this.dragStartBehavior = DragStartBehavior.start, - this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, - this.restorationId, - this.clipBehavior = Clip.hardEdge, - }); - - /// The [StreamUserListController] used to control the grid of users. - final StreamChannelListController controller; - - /// A delegate that controls the layout of the children within - /// the [PagedValueGridView]. - final SliverGridDelegate gridDelegate; - - /// A builder that is called to build items in the [PagedValueGridView]. - /// - /// The `value` parameter is the [Channel] at this position in the grid. - final StreamChannelGridViewIndexedWidgetBuilder? itemBuilder; - - /// A builder that is called to build the empty state of the grid. - final WidgetBuilder? emptyBuilder; - - /// A builder that is called to build the load more error state of the grid. - final PagedValueScrollViewLoadMoreErrorBuilder? loadMoreErrorBuilder; - - /// A builder that is called to build the load more indicator of the grid. - final WidgetBuilder? loadMoreIndicatorBuilder; - - /// A builder that is called to build the loading state of the grid. - final WidgetBuilder? loadingBuilder; - - /// A builder that is called to build the error state of the grid. - final Widget Function(BuildContext, StreamChatError)? errorBuilder; - - /// Called when the user taps this grid tile. - final void Function(Channel)? onChannelTap; - - /// Called when the user long-presses on this grid tile. - final void Function(Channel)? onChannelLongPress; - - /// The index to take into account when triggering [controller.loadMore]. - final int loadMoreTriggerIndex; - - /// {@template flutter.widgets.scroll_view.scrollDirection} - /// The axis along which the scroll view scrolls. - /// - /// Defaults to [Axis.vertical]. - /// {@endtemplate} - final Axis scrollDirection; - - /// {@template flutter.widgets.scroll_view.reverse} - /// Whether the scroll view scrolls in the reading direction. - /// - /// For example, if the reading direction is left-to-right and - /// [scrollDirection] is [Axis.horizontal], then the scroll view scrolls from - /// left to right when [reverse] is false and from right to left when - /// [reverse] is true. - /// - /// Similarly, if [scrollDirection] is [Axis.vertical], then the scroll view - /// scrolls from top to bottom when [reverse] is false and from bottom to top - /// when [reverse] is true. - /// - /// Defaults to false. - /// {@endtemplate} - final bool reverse; - - /// {@template flutter.widgets.scroll_view.controller} - /// An object that can be used to control the position to which this scroll - /// view is scrolled. - /// - /// Must be null if [primary] is true. - /// - /// A [ScrollController] serves several purposes. It can be used to control - /// the initial scroll position (see [ScrollController.initialScrollOffset]). - /// It can be used to control whether the scroll view should automatically - /// save and restore its scroll position in the [PageStorage] (see - /// [ScrollController.keepScrollOffset]). It can be used to read the current - /// scroll position (see [ScrollController.offset]), or change it (see - /// [ScrollController.animateTo]). - /// {@endtemplate} - final ScrollController? scrollController; - - /// {@template flutter.widgets.scroll_view.primary} - /// Whether this is the primary scroll view associated with the parent - /// [PrimaryScrollController]. - /// - /// When this is true, the scroll view is scrollable even if it does not have - /// sufficient content to actually scroll. Otherwise, by default the user can - /// only scroll the view if it has sufficient content. See [physics]. - /// - /// Also when true, the scroll view is used for default [ScrollAction]s. If a - /// ScrollAction is not handled by - /// an otherwise focused part of the application, - /// the ScrollAction will be evaluated using this scroll view, for example, - /// when executing [Shortcuts] key events like page up and down. - /// - /// On iOS, this also identifies the scroll view that will scroll to top in - /// response to a tap in the status bar. - /// {@endtemplate} - /// - /// Defaults to true when [scrollDirection] is [Axis.vertical] and - /// [controller] is null. - final bool? primary; - - /// {@template flutter.widgets.scroll_view.physics} - /// How the scroll view should respond to user input. - /// - /// For example, determines how the scroll view continues to animate after the - /// user stops dragging the scroll view. - /// - /// Defaults to matching platform conventions. Furthermore, if [primary] is - /// false, then the user cannot scroll if there is insufficient content to - /// scroll, while if [primary] is true, they can always attempt to scroll. - /// - /// To force the scroll view to always be scrollable even if there is - /// insufficient content, as if [primary] was true but without necessarily - /// setting it to true, provide an [AlwaysScrollableScrollPhysics] physics - /// object, as in: - /// - /// ```dart - /// physics: const AlwaysScrollableScrollPhysics(), - /// ``` - /// - /// To force the scroll view to use the default platform conventions and not - /// be scrollable if there is insufficient content, regardless of the value of - /// [primary], provide an explicit [ScrollPhysics] object, as in: - /// - /// ```dart - /// physics: const ScrollPhysics(), - /// ``` - /// - /// The physics can be changed dynamically (by providing a new object in a - /// subsequent build), but new physics will only take effect if the _class_ of - /// the provided object changes. Merely constructing a new instance with a - /// different configuration is insufficient to cause the physics to be - /// reapplied. (This is because the final object used is generated - /// dynamically, which can be relatively expensive, and it would be - /// inefficient to speculatively create this object each frame to see if the - /// physics should be updated.) - /// {@endtemplate} - /// - /// If an explicit [ScrollBehavior] is provided to [scrollBehavior], the - /// [ScrollPhysics] provided by that behavior will take precedence after - /// [physics]. - final ScrollPhysics? physics; - - /// {@template flutter.widgets.scroll_view.shrinkWrap} - /// Whether the extent of the scroll view in the [scrollDirection] should be - /// determined by the contents being viewed. - /// - /// If the scroll view does not shrink wrap, then the scroll view will expand - /// to the maximum allowed size in the [scrollDirection]. If the scroll view - /// has unbounded constraints in the [scrollDirection], then [shrinkWrap] must - /// be true. - /// - /// Shrink wrapping the content of the scroll view is significantly more - /// expensive than expanding to the maximum allowed size because the content - /// can expand and contract during scrolling, which means the size of the - /// scroll view needs to be recomputed whenever the scroll position changes. - /// - /// Defaults to false. - /// {@endtemplate} - final bool shrinkWrap; - - /// The amount of space by which to inset the children. - final EdgeInsetsGeometry? padding; - - /// Whether to wrap each child in an [AutomaticKeepAlive]. - /// - /// Typically, children in lazy list are wrapped in [AutomaticKeepAlive] - /// widgets so that children can use [KeepAliveNotification]s to preserve - /// their state when they would otherwise be garbage collected off-screen. - /// - /// This feature (and [addRepaintBoundaries]) must be disabled if the children - /// are going to manually maintain their [KeepAlive] state. It may also be - /// more efficient to disable this feature if it is known ahead of time that - /// none of the children will ever try to keep themselves alive. - /// - /// Defaults to true. - final bool addAutomaticKeepAlives; - - /// Whether to wrap each child in a [RepaintBoundary]. - /// - /// Typically, children in a scrolling container are wrapped in repaint - /// boundaries so that they do not need to be repainted as the list scrolls. - /// If the children are easy to repaint (e.g., solid color blocks or a short - /// snippet of text), it might be more efficient to not add a repaint boundary - /// and simply repaint the children during scrolling. - /// - /// Defaults to true. - final bool addRepaintBoundaries; - - /// Whether to wrap each child in an [IndexedSemantics]. - /// - /// Typically, children in a scrolling container must be annotated with a - /// semantic index in order to generate the correct accessibility - /// announcements. This should only be set to false if the indexes have - /// already been provided by an [IndexedSemantics] widget. - /// - /// Defaults to true. - /// - /// See also: - /// - /// * [IndexedSemantics], for an explanation of how to manually - /// provide semantic indexes. - final bool addSemanticIndexes; - - /// {@macro flutter.rendering.RenderViewportBase.cacheExtent} - final double? cacheExtent; - - /// The number of children that will contribute semantic information. - /// - /// Some subtypes of [ScrollView] can infer this value automatically. For - /// example [ListView] will use the number of widgets in the child list, - /// while the [ListView.separated] constructor will use half that amount. - /// - /// For [CustomScrollView] and other types which do not receive a builder - /// or list of widgets, the child count must be explicitly provided. If the - /// number is unknown or unbounded this should be left unset or set to null. - /// - /// See also: - /// - /// * [SemanticsConfiguration.scrollChildCount], - /// the corresponding semantics property. - final int? semanticChildCount; - - /// {@macro flutter.widgets.scrollable.dragStartBehavior} - final DragStartBehavior dragStartBehavior; - - /// {@template flutter.widgets.scroll_view.keyboardDismissBehavior} - /// [ScrollViewKeyboardDismissBehavior] the defines how this [ScrollView] will - /// dismiss the keyboard automatically. - /// {@endtemplate} - final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior; - - /// {@macro flutter.widgets.scrollable.restorationId} - final String? restorationId; - - /// {@macro flutter.material.Material.clipBehavior} - /// - /// Defaults to [Clip.hardEdge]. - final Clip clipBehavior; - - @override - Widget build(BuildContext context) { - return PagedValueGridView( - scrollDirection: scrollDirection, - reverse: reverse, - controller: controller, - primary: primary, - physics: physics, - shrinkWrap: shrinkWrap, - padding: padding, - scrollController: scrollController, - addAutomaticKeepAlives: addAutomaticKeepAlives, - addRepaintBoundaries: addRepaintBoundaries, - addSemanticIndexes: addSemanticIndexes, - cacheExtent: cacheExtent, - semanticChildCount: semanticChildCount, - dragStartBehavior: dragStartBehavior, - keyboardDismissBehavior: keyboardDismissBehavior, - restorationId: restorationId, - clipBehavior: clipBehavior, - gridDelegate: gridDelegate, - itemBuilder: (context, channels, index) { - final channel = channels[index]; - final onTap = onChannelTap; - final onLongPress = onChannelLongPress; - - final streamChannelGridTile = StreamChannelGridTile( - channel: channel, - onTap: onTap == null ? null : () => onTap(channel), - onLongPress: onLongPress == null ? null : () => onLongPress(channel), - ); - - return itemBuilder?.call( - context, - channels, - index, - streamChannelGridTile, - ) ?? - streamChannelGridTile; - }, - emptyBuilder: (context) { - final chatThemeData = StreamChatTheme.of(context); - return emptyBuilder?.call(context) ?? - Center( - child: Padding( - padding: const EdgeInsets.all(8), - child: StreamScrollViewEmptyWidget( - emptyIcon: StreamSvgIcon( - size: 148, - icon: StreamSvgIcons.message, - color: chatThemeData.colorTheme.disabled, - ), - emptyTitle: Text( - context.translations.letsStartChattingLabel, - style: chatThemeData.textTheme.headline, - ), - ), - ), - ); - }, - loadMoreErrorBuilder: (context, error) => - StreamScrollViewLoadMoreError.grid( - onTap: controller.retry, - error: Text( - context.translations.loadingChannelsError, - textAlign: TextAlign.center, - ), - ), - loadMoreIndicatorBuilder: (context) => const Center( - child: Padding( - padding: EdgeInsets.all(16), - child: StreamScrollViewLoadMoreIndicator(), - ), - ), - loadingBuilder: (context) => - loadingBuilder?.call(context) ?? - const Center( - child: StreamScrollViewLoadingWidget(), - ), - errorBuilder: (context, error) => - errorBuilder?.call(context, error) ?? - Center( - child: StreamScrollViewErrorWidget( - errorTitle: Text(context.translations.loadingChannelsError), - onRetryPressed: controller.refresh, - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_list_tile.dart b/packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_list_tile.dart deleted file mode 100644 index 3d38d46570..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_list_tile.dart +++ /dev/null @@ -1,403 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/message_widget/sending_indicator_builder.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// A widget that displays a channel preview. -/// -/// This widget is intended to be used as a Tile in [StreamChannelListView] -/// -/// It shows the last message of the channel, the last message time, the unread -/// message count, the typing indicator, the sending indicator and the channel -/// avatar. -/// -/// See also: -/// * [StreamChannelAvatar] -/// * [StreamChannelName] -class StreamChannelListTile extends StatelessWidget { - /// Creates a new instance of [StreamChannelListTile] widget. - StreamChannelListTile({ - super.key, - required this.channel, - this.leading, - this.title, - this.subtitle, - this.trailing, - this.onTap, - this.onLongPress, - this.tileColor, - this.visualDensity = VisualDensity.compact, - this.contentPadding = const EdgeInsets.symmetric(horizontal: 8), - this.unreadIndicatorBuilder, - this.sendingIndicatorBuilder, - this.selected = false, - this.selectedTileColor, - }) : assert( - channel.state != null, - 'Channel ${channel.id} is not initialized', - ); - - /// The channel to display. - final Channel channel; - - /// A widget to display before the title. - final Widget? leading; - - /// The primary content of the list tile. - final Widget? title; - - /// Additional content displayed below the title. - final Widget? subtitle; - - /// A widget to display at the end of tile. - final Widget? trailing; - - /// Called when the user taps this list tile. - final GestureTapCallback? onTap; - - /// Called when the user long-presses on this list tile. - final GestureLongPressCallback? onLongPress; - - /// {@template flutter.material.ListTile.tileColor} - /// Defines the background color of `ListTile`. - /// - /// When the value is null, - /// the `tileColor` is set to [ListTileTheme.tileColor] - /// if it's not null and to [Colors.transparent] if it's null. - /// {@endtemplate} - final Color? tileColor; - - /// Defines how compact the list tile's layout will be. - /// - /// {@macro flutter.material.themedata.visualDensity} - /// - /// See also: - /// - /// * [ThemeData.visualDensity], which specifies the [visualDensity] for all - /// widgets within a [Theme]. - final VisualDensity visualDensity; - - /// The tile's internal padding. - /// - /// Insets a [ListTile]'s contents: its [leading], [title], [subtitle], - /// and [trailing] widgets. - /// - /// If null, `EdgeInsets.symmetric(horizontal: 16.0)` is used. - final EdgeInsetsGeometry contentPadding; - - /// The widget builder for the unread indicator. - final WidgetBuilder? unreadIndicatorBuilder; - - /// The widget builder for the sending indicator. - /// - /// `Message` is the last message in the channel, Use it to determine the - /// status using [Message.state]. - final Widget Function(BuildContext, Message)? sendingIndicatorBuilder; - - /// True if the tile is in a selected state. - final bool selected; - - /// The color of the tile in selected state. - final Color? selectedTileColor; - - /// Creates a copy of this tile but with the given fields replaced with - /// the new values. - StreamChannelListTile copyWith({ - Key? key, - Channel? channel, - Widget? leading, - Widget? title, - Widget? subtitle, - VoidCallback? onTap, - VoidCallback? onLongPress, - VisualDensity? visualDensity, - EdgeInsetsGeometry? contentPadding, - bool? selected, - Widget Function(BuildContext, Message)? sendingIndicatorBuilder, - Color? tileColor, - Color? selectedTileColor, - WidgetBuilder? unreadIndicatorBuilder, - Widget? trailing, - }) { - return StreamChannelListTile( - key: key ?? this.key, - channel: channel ?? this.channel, - leading: leading ?? this.leading, - title: title ?? this.title, - subtitle: subtitle ?? this.subtitle, - onTap: onTap ?? this.onTap, - onLongPress: onLongPress ?? this.onLongPress, - visualDensity: visualDensity ?? this.visualDensity, - contentPadding: contentPadding ?? this.contentPadding, - sendingIndicatorBuilder: - sendingIndicatorBuilder ?? this.sendingIndicatorBuilder, - tileColor: tileColor ?? this.tileColor, - trailing: trailing ?? this.trailing, - unreadIndicatorBuilder: - unreadIndicatorBuilder ?? this.unreadIndicatorBuilder, - selected: selected ?? this.selected, - selectedTileColor: selectedTileColor ?? this.selectedTileColor, - ); - } - - @override - Widget build(BuildContext context) { - final channelState = channel.state!; - final currentUser = channel.client.state.currentUser!; - - final channelPreviewTheme = StreamChannelPreviewTheme.of(context); - final streamChatTheme = StreamChatTheme.of(context); - final streamChat = StreamChat.of(context); - - final leading = this.leading ?? - StreamChannelAvatar( - channel: channel, - ); - - final title = this.title ?? - StreamChannelName( - channel: channel, - textStyle: channelPreviewTheme.titleStyle, - ); - - final subtitle = this.subtitle ?? - ChannelListTileSubtitle( - channel: channel, - textStyle: channelPreviewTheme.subtitleStyle, - ); - - final trailing = this.trailing ?? - ChannelLastMessageDate( - channel: channel, - textStyle: channelPreviewTheme.lastMessageAtStyle, - ); - - return BetterStreamBuilder( - stream: channel.isMutedStream, - initialData: channel.isMuted, - builder: (context, isMuted) => AnimatedOpacity( - opacity: isMuted ? 0.5 : 1, - duration: const Duration(milliseconds: 300), - child: ListTile( - onTap: onTap, - onLongPress: onLongPress, - visualDensity: visualDensity, - contentPadding: contentPadding, - leading: leading, - tileColor: tileColor, - selected: selected, - selectedTileColor: selectedTileColor ?? - StreamChatTheme.of(context).colorTheme.borders, - title: Row( - children: [ - Expanded(child: title), - BetterStreamBuilder>( - stream: channelState.membersStream, - initialData: channelState.members, - comparator: const ListEquality().equals, - builder: (context, members) { - if (members.isEmpty) { - return const Offstage(); - } - return unreadIndicatorBuilder?.call(context) ?? - StreamUnreadIndicator.channels(cid: channel.cid); - }, - ), - ], - ), - subtitle: Row( - children: [ - Expanded( - child: Align( - alignment: Alignment.centerLeft, - child: subtitle, - ), - ), - BetterStreamBuilder>( - stream: channelState.messagesStream, - initialData: channelState.messages, - comparator: const ListEquality().equals, - builder: (context, messages) { - final lastMessage = messages.lastWhereOrNull( - (m) => !m.shadowed && !m.isDeleted, - ); - - if (lastMessage == null || - (lastMessage.user?.id != currentUser.id)) { - return const Offstage(); - } - - final hasNonUrlAttachments = lastMessage.attachments - .any((it) => it.type != AttachmentType.urlPreview); - - return Padding( - padding: const EdgeInsets.only(right: 4), - child: - sendingIndicatorBuilder?.call(context, lastMessage) ?? - SendingIndicatorBuilder( - messageTheme: streamChatTheme.ownMessageTheme, - message: lastMessage, - hasNonUrlAttachments: hasNonUrlAttachments, - streamChat: streamChat, - streamChatTheme: streamChatTheme, - channel: channel, - ), - ); - }, - ), - trailing, - ], - ), - ), - ), - ); - } -} - -/// A widget that displays the channel last message date. -class ChannelLastMessageDate extends StatelessWidget { - /// Creates a new instance of the [ChannelLastMessageDate] widget. - ChannelLastMessageDate({ - super.key, - required this.channel, - this.textStyle, - }) : assert( - channel.state != null, - 'Channel ${channel.id} is not initialized', - ); - - /// The channel to display the last message date for. - final Channel channel; - - /// The style of the text displayed - final TextStyle? textStyle; - - @override - Widget build(BuildContext context) => BetterStreamBuilder( - stream: channel.lastMessageAtStream, - initialData: channel.lastMessageAt, - builder: (context, data) { - final lastMessageAt = data.toLocal(); - - String stringDate; - final now = DateTime.now(); - - final startOfDay = DateTime(now.year, now.month, now.day); - - if (lastMessageAt.millisecondsSinceEpoch >= - startOfDay.millisecondsSinceEpoch) { - stringDate = Jiffy.parseFromDateTime(lastMessageAt.toLocal()).jm; - } else if (lastMessageAt.millisecondsSinceEpoch >= - startOfDay - .subtract(const Duration(days: 1)) - .millisecondsSinceEpoch) { - stringDate = context.translations.yesterdayLabel; - } else if (startOfDay.difference(lastMessageAt).inDays < 7) { - stringDate = Jiffy.parseFromDateTime(lastMessageAt.toLocal()).EEEE; - } else { - stringDate = Jiffy.parseFromDateTime(lastMessageAt.toLocal()).yMd; - } - - return Text( - stringDate, - style: textStyle, - ); - }, - ); -} - -/// A widget that displays the subtitle for [StreamChannelListTile]. -class ChannelListTileSubtitle extends StatelessWidget { - /// Creates a new instance of [StreamChannelListTileSubtitle] widget. - ChannelListTileSubtitle({ - super.key, - required this.channel, - this.textStyle, - }) : assert( - channel.state != null, - 'Channel ${channel.id} is not initialized', - ); - - /// The channel to create the subtitle from. - final Channel channel; - - /// The style of the text displayed - final TextStyle? textStyle; - - @override - Widget build(BuildContext context) { - if (channel.isMuted) { - return Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - const StreamSvgIcon(size: 16, icon: StreamSvgIcons.mute), - Expanded( - child: Text( - ' ${context.translations.channelIsMutedText}', - style: textStyle, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - ], - ); - } - return StreamTypingIndicator( - channel: channel, - style: textStyle, - alternativeWidget: ChannelLastMessageText( - channel: channel, - textStyle: textStyle, - ), - ); - } -} - -/// A widget that displays the last message of a channel. -class ChannelLastMessageText extends StatefulWidget { - /// Creates a new instance of [ChannelLastMessageText] widget. - ChannelLastMessageText({ - super.key, - required this.channel, - this.textStyle, - }) : assert( - channel.state != null, - 'Channel ${channel.id} is not initialized', - ); - - /// The channel to display the last message of. - final Channel channel; - - /// The style of the text displayed - final TextStyle? textStyle; - - @override - State createState() => _ChannelLastMessageTextState(); -} - -class _ChannelLastMessageTextState extends State { - Message? _lastMessage; - - @override - Widget build(BuildContext context) => BetterStreamBuilder>( - stream: widget.channel.state!.messagesStream, - initialData: widget.channel.state!.messages, - builder: (context, messages) { - final lastMessage = messages.lastWhereOrNull( - (m) => !m.shadowed && !m.isDeleted, - ); - - if (widget.channel.state?.isUpToDate == true) { - _lastMessage = lastMessage; - } - - if (_lastMessage == null) return const Offstage(); - - return StreamMessagePreviewText( - message: _lastMessage!, - textStyle: widget.textStyle, - language: widget.channel.client.state.currentUser?.language, - ); - }, - ); -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_list_view.dart b/packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_list_view.dart deleted file mode 100644 index 3541963b3f..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_list_view.dart +++ /dev/null @@ -1,384 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_error_widget.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_error.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_indicator.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_loading_widget.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Default separator builder for [StreamChannelListView]. -Widget defaultChannelListViewSeparatorBuilder( - BuildContext context, - List items, - int index, -) => - const StreamChannelListSeparator(); - -/// Signature for the item builder that creates the children of the -/// [StreamChannelListView]. -typedef StreamChannelListViewIndexedWidgetBuilder - = StreamScrollViewIndexedWidgetBuilder; - -/// A [ListView] that shows a list of [Channel]s, -/// it uses [StreamChannelListTile] as a default item. -/// -/// This is the new version of [StreamChannelListView] that uses -/// [StreamChannelListController]. -/// -/// Example: -/// -/// ```dart -/// StreamChannelListView( -/// controller: controller, -/// onChannelTap: (channel) { -/// // Handle channel tap event -/// }, -/// onChannelLongPress: (channel) { -/// // Handle channel long press event -/// }, -/// ) -/// ``` -/// -/// See also: -/// * [StreamChannelListTile] -/// * [StreamChannelListController] -class StreamChannelListView extends StatelessWidget { - /// Creates a new instance of [StreamChannelListView]. - const StreamChannelListView({ - super.key, - required this.controller, - this.itemBuilder, - this.separatorBuilder = defaultChannelListViewSeparatorBuilder, - this.emptyBuilder, - this.loadingBuilder, - this.errorBuilder, - this.onChannelTap, - this.onChannelLongPress, - this.loadMoreTriggerIndex = 3, - this.scrollDirection = Axis.vertical, - this.reverse = false, - this.scrollController, - this.primary, - this.physics, - this.shrinkWrap = false, - this.padding, - this.addAutomaticKeepAlives = true, - this.addRepaintBoundaries = true, - this.addSemanticIndexes = true, - this.cacheExtent, - this.dragStartBehavior = DragStartBehavior.start, - this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, - this.restorationId, - this.clipBehavior = Clip.hardEdge, - }); - - /// The [StreamChannelListController] used to control the list of channels. - final StreamChannelListController controller; - - /// A builder that is called to build items in the [ListView]. - final StreamChannelListViewIndexedWidgetBuilder? itemBuilder; - - /// A builder that is called to build the list separator. - final PagedValueScrollViewIndexedWidgetBuilder separatorBuilder; - - /// A builder that is called to build the empty state of the list. - final WidgetBuilder? emptyBuilder; - - /// A builder that is called to build the loading state of the list. - final WidgetBuilder? loadingBuilder; - - /// A builder that is called to build the error state of the list. - /// - /// If not provided, [StreamChannelListErrorWidget] will be used. - final Widget Function(BuildContext, StreamChatError)? errorBuilder; - - /// Called when the user taps this list tile. - final void Function(Channel)? onChannelTap; - - /// Called when the user long-presses on this list tile. - final void Function(Channel)? onChannelLongPress; - - /// The index to take into account when triggering [controller.loadMore]. - final int loadMoreTriggerIndex; - - /// {@template flutter.widgets.scroll_view.scrollDirection} - /// The axis along which the scroll view scrolls. - /// - /// Defaults to [Axis.vertical]. - /// {@endtemplate} - final Axis scrollDirection; - - /// The amount of space by which to inset the children. - final EdgeInsetsGeometry? padding; - - /// Whether to wrap each child in an [AutomaticKeepAlive]. - /// - /// Typically, children in lazy list are wrapped in [AutomaticKeepAlive] - /// widgets so that children can use [KeepAliveNotification]s to preserve - /// their state when they would otherwise be garbage collected off-screen. - /// - /// This feature (and [addRepaintBoundaries]) must be disabled if the children - /// are going to manually maintain their [KeepAlive] state. It may also be - /// more efficient to disable this feature if it is known ahead of time that - /// none of the children will ever try to keep themselves alive. - /// - /// Defaults to true. - final bool addAutomaticKeepAlives; - - /// Whether to wrap each child in a [RepaintBoundary]. - /// - /// Typically, children in a scrolling container are wrapped in repaint - /// boundaries so that they do not need to be repainted as the list scrolls. - /// If the children are easy to repaint (e.g., solid color blocks or a short - /// snippet of text), it might be more efficient to not add a repaint boundary - /// and simply repaint the children during scrolling. - /// - /// Defaults to true. - final bool addRepaintBoundaries; - - /// Whether to wrap each child in an [IndexedSemantics]. - /// - /// Typically, children in a scrolling container must be annotated with a - /// semantic index in order to generate the correct accessibility - /// announcements. This should only be set to false if the indexes have - /// already been provided by an [IndexedSemantics] widget. - /// - /// Defaults to true. - /// - /// See also: - /// - /// * [IndexedSemantics], for an explanation of how to manually - /// provide semantic indexes. - final bool addSemanticIndexes; - - /// {@template flutter.widgets.scroll_view.reverse} - /// Whether the scroll view scrolls in the reading direction. - /// - /// For example, if [scrollDirection] is [Axis.vertical], then the scroll view - /// scrolls from top to bottom when [reverse] is false and from bottom to top - /// when [reverse] is true. - /// - /// Defaults to false. - /// {@endtemplate} - final bool reverse; - - /// {@template flutter.widgets.scroll_view.controller} - /// An object that can be used to control the position to which this scroll - /// view is scrolled. - /// - /// Must be null if [primary] is true. - /// - /// A [ScrollController] serves several purposes. It can be used to control - /// the initial scroll position (see [ScrollController.initialScrollOffset]). - /// It can be used to control whether the scroll view should automatically - /// save and restore its scroll position in the [PageStorage] (see - /// [ScrollController.keepScrollOffset]). It can be used to read the current - /// scroll position (see [ScrollController.offset]), or change it (see - /// [ScrollController.animateTo]). - /// {@endtemplate} - final ScrollController? scrollController; - - /// {@template flutter.widgets.scroll_view.primary} - /// Whether this is the primary scroll view associated with the parent - /// [PrimaryScrollController]. - /// - /// When this is true, the scroll view is scrollable even if it does not have - /// sufficient content to actually scroll. Otherwise, by default the user can - /// only scroll the view if it has sufficient content. See [physics]. - /// - /// Also when true, the scroll view is used for default [ScrollAction]s. If a - /// ScrollAction is not handled by an otherwise focused part of the - /// application, the ScrollAction will be evaluated using this scroll view, - /// for example, when executing [Shortcuts] key events like page up and down. - /// - /// On iOS, this also identifies the scroll view that will scroll to top in - /// response to a tap in the status bar. - /// {@endtemplate} - /// - /// Defaults to true when [scrollController] is null. - final bool? primary; - - /// {@template flutter.widgets.scroll_view.shrinkWrap} - /// Whether the extent of the scroll view in the [scrollDirection] should be - /// determined by the contents being viewed. - /// - /// If the scroll view does not shrink wrap, then the scroll view will expand - /// to the maximum allowed size in the [scrollDirection]. If the scroll view - /// has unbounded constraints in the [scrollDirection], then [shrinkWrap] must - /// be true. - /// - /// Shrink wrapping the content of the scroll view is significantly more - /// expensive than expanding to the maximum allowed size because the content - /// can expand and contract during scrolling, which means the size of the - /// scroll view needs to be recomputed whenever the scroll position changes. - /// - /// Defaults to false. - /// {@endtemplate} - final bool shrinkWrap; - - /// {@template flutter.widgets.scroll_view.physics} - /// How the scroll view should respond to user input. - /// - /// For example, determines how the scroll view continues to animate after the - /// user stops dragging the scroll view. - /// - /// Defaults to matching platform conventions. Furthermore, if [primary] is - /// false, then the user cannot scroll if there is insufficient content to - /// scroll, while if [primary] is true, they can always attempt to scroll. - /// - /// To force the scroll view to always be scrollable even if there is - /// insufficient content, as if [primary] was true but without necessarily - /// setting it to true, provide an [AlwaysScrollableScrollPhysics] physics - /// object, as in: - /// - /// ```dart - /// physics: const AlwaysScrollableScrollPhysics(), - /// ``` - /// - /// To force the scroll view to use the default platform conventions and not - /// be scrollable if there is insufficient content, regardless of the value of - /// [primary], provide an explicit [ScrollPhysics] object, as in: - /// - /// ```dart - /// physics: const ScrollPhysics(), - /// ``` - /// - /// The physics can be changed dynamically (by providing a new object in a - /// subsequent build), but new physics will only take effect if the _class_ of - /// the provided object changes. Merely constructing a new instance with a - /// different configuration is insufficient to cause the physics to be - /// reapplied. (This is because the final object used is generated - /// dynamically, which can be relatively expensive, and it would be - /// inefficient to speculatively create this object each frame to see if the - /// physics should be updated.) - /// {@endtemplate} - /// - /// If an explicit [ScrollBehavior] is provided to [scrollBehavior], the - /// [ScrollPhysics] provided by that behavior will take precedence after - /// [physics]. - final ScrollPhysics? physics; - - /// {@macro flutter.rendering.RenderViewportBase.cacheExtent} - final double? cacheExtent; - - /// {@macro flutter.widgets.scrollable.dragStartBehavior} - final DragStartBehavior dragStartBehavior; - - /// {@template flutter.widgets.scroll_view.keyboardDismissBehavior} - /// [ScrollViewKeyboardDismissBehavior] the defines how this [ScrollView] will - /// dismiss the keyboard automatically. - /// {@endtemplate} - final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior; - - /// {@macro flutter.widgets.scrollable.restorationId} - final String? restorationId; - - /// {@macro flutter.material.Material.clipBehavior} - /// - /// Defaults to [Clip.hardEdge]. - final Clip clipBehavior; - - @override - Widget build(BuildContext context) { - return PagedValueListView( - scrollDirection: scrollDirection, - padding: padding, - physics: physics, - reverse: reverse, - controller: controller, - scrollController: scrollController, - primary: primary, - shrinkWrap: shrinkWrap, - addAutomaticKeepAlives: addAutomaticKeepAlives, - addRepaintBoundaries: addRepaintBoundaries, - addSemanticIndexes: addSemanticIndexes, - keyboardDismissBehavior: keyboardDismissBehavior, - restorationId: restorationId, - dragStartBehavior: dragStartBehavior, - cacheExtent: cacheExtent, - clipBehavior: clipBehavior, - loadMoreTriggerIndex: loadMoreTriggerIndex, - separatorBuilder: separatorBuilder, - itemBuilder: (context, channels, index) { - final channel = channels[index]; - final onTap = onChannelTap; - final onLongPress = onChannelLongPress; - - final streamChannelListTile = StreamChannelListTile( - channel: channel, - onTap: onTap == null ? null : () => onTap(channel), - onLongPress: onLongPress == null ? null : () => onLongPress(channel), - ); - - return itemBuilder?.call( - context, - channels, - index, - streamChannelListTile, - ) ?? - streamChannelListTile; - }, - emptyBuilder: (context) { - final chatThemeData = StreamChatTheme.of(context); - return emptyBuilder?.call(context) ?? - Center( - child: Padding( - padding: const EdgeInsets.all(8), - child: StreamScrollViewEmptyWidget( - emptyIcon: StreamSvgIcon( - size: 148, - icon: StreamSvgIcons.message, - color: chatThemeData.colorTheme.disabled, - ), - emptyTitle: Text( - context.translations.letsStartChattingLabel, - style: chatThemeData.textTheme.headline, - ), - ), - ), - ); - }, - loadMoreErrorBuilder: (context, error) => - StreamScrollViewLoadMoreError.list( - onTap: controller.retry, - error: Text(context.translations.loadingChannelsError), - ), - loadMoreIndicatorBuilder: (context) => const Center( - child: Padding( - padding: EdgeInsets.all(16), - child: StreamScrollViewLoadMoreIndicator(), - ), - ), - loadingBuilder: (context) => - loadingBuilder?.call(context) ?? - const Center( - child: StreamScrollViewLoadingWidget(), - ), - errorBuilder: (context, error) => - errorBuilder?.call(context, error) ?? - Center( - child: StreamScrollViewErrorWidget( - errorTitle: Text(context.translations.loadingChannelsError), - onRetryPressed: controller.refresh, - ), - ), - ); - } -} - -/// A widget that is used to display a separator between -/// [StreamChannelListTile] items. -class StreamChannelListSeparator extends StatelessWidget { - /// Creates a new instance of [StreamChannelListSeparator]. - const StreamChannelListSeparator({super.key}); - - @override - Widget build(BuildContext context) { - final effect = StreamChatTheme.of(context).colorTheme.borderBottom; - return Container( - height: 1, - // ignore: deprecated_member_use - color: effect.color!.withOpacity(effect.alpha ?? 1.0), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/member_scroll_view/stream_member_grid_view.dart b/packages/stream_chat_flutter/lib/src/scroll_view/member_scroll_view/stream_member_grid_view.dart deleted file mode 100644 index adf97a7860..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/member_scroll_view/stream_member_grid_view.dart +++ /dev/null @@ -1,397 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_error_widget.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_error.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_indicator.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_loading_widget.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Default grid delegate for [StreamMemberGridView]. -const defaultMemberGridViewDelegate = - SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 4); - -/// Signature for the item builder that creates the children of the -/// [StreamMemberGridView]. -typedef StreamMemberGridViewIndexedWidgetBuilder - = StreamScrollViewIndexedWidgetBuilder; - -/// Signature for the member grid tile, currently equal to [StreamUserGridTile]. -typedef StreamMemberGridTile = StreamUserGridTile; - -/// A [GridView] that shows a grid of [Member]s, -/// it uses [StreamMemberGridTile] as a default item. -/// -/// Example: -/// -/// ```dart -/// StreamMemberGridView( -/// controller: controller, -/// onMemberTap: (member) { -/// // Handle member tap event -/// }, -/// onMemberLongPress: (member) { -/// // Handle member long press event -/// }, -/// ) -/// ``` -/// -/// See also: -/// * [StreamMemberGridTile] -/// * [StreamMemberListController] -class StreamMemberGridView extends StatelessWidget { - /// Creates a new instance of [StreamMemberGridView]. - const StreamMemberGridView({ - super.key, - required this.controller, - this.gridDelegate = defaultMemberGridViewDelegate, - this.itemBuilder, - this.emptyBuilder, - this.loadMoreErrorBuilder, - this.loadMoreIndicatorBuilder, - this.loadingBuilder, - this.errorBuilder, - this.onMemberTap, - this.onMemberLongPress, - this.loadMoreTriggerIndex = 3, - this.scrollDirection = Axis.vertical, - this.reverse = false, - this.scrollController, - this.primary, - this.physics, - this.shrinkWrap = false, - this.padding, - this.addAutomaticKeepAlives = true, - this.addRepaintBoundaries = true, - this.addSemanticIndexes = true, - this.cacheExtent, - this.semanticChildCount, - this.dragStartBehavior = DragStartBehavior.start, - this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, - this.restorationId, - this.clipBehavior = Clip.hardEdge, - }); - - /// The [StreamMemberListController] used to control the grid of members. - final StreamMemberListController controller; - - /// A delegate that controls the layout of the children within - /// the [PagedValueGridView]. - final SliverGridDelegate gridDelegate; - - /// A builder that is called to build items in the [PagedValueGridView]. - final StreamMemberGridViewIndexedWidgetBuilder? itemBuilder; - - /// A builder that is called to build the empty state of the grid. - final WidgetBuilder? emptyBuilder; - - /// A builder that is called to build the load more error state of the grid. - final PagedValueScrollViewLoadMoreErrorBuilder? loadMoreErrorBuilder; - - /// A builder that is called to build the load more indicator of the grid. - final WidgetBuilder? loadMoreIndicatorBuilder; - - /// A builder that is called to build the loading state of the grid. - final WidgetBuilder? loadingBuilder; - - /// A builder that is called to build the error state of the grid. - final Widget Function(BuildContext, StreamChatError)? errorBuilder; - - /// Called when the member taps this grid tile. - final void Function(Member)? onMemberTap; - - /// Called when the member long-presses on this grid tile. - final void Function(Member)? onMemberLongPress; - - /// The index to take into account when triggering [controller.loadMore]. - final int loadMoreTriggerIndex; - - /// {@template flutter.widgets.scroll_view.scrollDirection} - /// The axis along which the scroll view scrolls. - /// - /// Defaults to [Axis.vertical]. - /// {@endtemplate} - final Axis scrollDirection; - - /// {@template flutter.widgets.scroll_view.reverse} - /// Whether the scroll view scrolls in the reading direction. - /// - /// For example, if the reading direction is left-to-right and - /// [scrollDirection] is [Axis.horizontal], then the scroll view scrolls from - /// left to right when [reverse] is false and from right to left when - /// [reverse] is true. - /// - /// Similarly, if [scrollDirection] is [Axis.vertical], then the scroll view - /// scrolls from top to bottom when [reverse] is false and from bottom to top - /// when [reverse] is true. - /// - /// Defaults to false. - /// {@endtemplate} - final bool reverse; - - /// {@template flutter.widgets.scroll_view.controller} - /// An object that can be used to control the position to which this scroll - /// view is scrolled. - /// - /// Must be null if [primary] is true. - /// - /// A [ScrollController] serves several purposes. It can be used to control - /// the initial scroll position (see [ScrollController.initialScrollOffset]). - /// It can be used to control whether the scroll view should automatically - /// save and restore its scroll position in the [PageStorage] (see - /// [ScrollController.keepScrollOffset]). It can be used to read the current - /// scroll position (see [ScrollController.offset]), or change it (see - /// [ScrollController.animateTo]). - /// {@endtemplate} - final ScrollController? scrollController; - - /// {@template flutter.widgets.scroll_view.primary} - /// Whether this is the primary scroll view associated with the parent - /// [PrimaryScrollController]. - /// - /// When this is true, the scroll view is scrollable even if it does not have - /// sufficient content to actually scroll. Otherwise, by default the user can - /// only scroll the view if it has sufficient content. See [physics]. - /// - /// Also when true, the scroll view is used for default [ScrollAction]s. If a - /// ScrollAction is not handled by - /// an otherwise focused part of the application, - /// the ScrollAction will be evaluated using this scroll view, for example, - /// when executing [Shortcuts] key events like page up and down. - /// - /// On iOS, this also identifies the scroll view that will scroll to top in - /// response to a tap in the status bar. - /// {@endtemplate} - /// - /// Defaults to true when [scrollDirection] is [Axis.vertical] and - /// [controller] is null. - final bool? primary; - - /// {@template flutter.widgets.scroll_view.physics} - /// How the scroll view should respond to user input. - /// - /// For example, determines how the scroll view continues to animate after the - /// user stops dragging the scroll view. - /// - /// Defaults to matching platform conventions. Furthermore, if [primary] is - /// false, then the user cannot scroll if there is insufficient content to - /// scroll, while if [primary] is true, they can always attempt to scroll. - /// - /// To force the scroll view to always be scrollable even if there is - /// insufficient content, as if [primary] was true but without necessarily - /// setting it to true, provide an [AlwaysScrollableScrollPhysics] physics - /// object, as in: - /// - /// ```dart - /// physics: const AlwaysScrollableScrollPhysics(), - /// ``` - /// - /// To force the scroll view to use the default platform conventions and not - /// be scrollable if there is insufficient content, regardless of the value of - /// [primary], provide an explicit [ScrollPhysics] object, as in: - /// - /// ```dart - /// physics: const ScrollPhysics(), - /// ``` - /// - /// The physics can be changed dynamically (by providing a new object in a - /// subsequent build), but new physics will only take effect if the _class_ of - /// the provided object changes. Merely constructing a new instance with a - /// different configuration is insufficient to cause the physics to be - /// reapplied. (This is because the final object used is generated - /// dynamically, which can be relatively expensive, and it would be - /// inefficient to speculatively create this object each frame to see if the - /// physics should be updated.) - /// {@endtemplate} - /// - /// If an explicit [ScrollBehavior] is provided to [scrollBehavior], the - /// [ScrollPhysics] provided by that behavior will take precedence after - /// [physics]. - final ScrollPhysics? physics; - - /// {@template flutter.widgets.scroll_view.shrinkWrap} - /// Whether the extent of the scroll view in the [scrollDirection] should be - /// determined by the contents being viewed. - /// - /// If the scroll view does not shrink wrap, then the scroll view will expand - /// to the maximum allowed size in the [scrollDirection]. If the scroll view - /// has unbounded constraints in the [scrollDirection], then [shrinkWrap] must - /// be true. - /// - /// Shrink wrapping the content of the scroll view is significantly more - /// expensive than expanding to the maximum allowed size because the content - /// can expand and contract during scrolling, which means the size of the - /// scroll view needs to be recomputed whenever the scroll position changes. - /// - /// Defaults to false. - /// {@endtemplate} - final bool shrinkWrap; - - /// The amount of space by which to inset the children. - final EdgeInsetsGeometry? padding; - - /// Whether to wrap each child in an [AutomaticKeepAlive]. - /// - /// Typically, children in lazy list are wrapped in [AutomaticKeepAlive] - /// widgets so that children can use [KeepAliveNotification]s to preserve - /// their state when they would otherwise be garbage collected off-screen. - /// - /// This feature (and [addRepaintBoundaries]) must be disabled if the children - /// are going to manually maintain their [KeepAlive] state. It may also be - /// more efficient to disable this feature if it is known ahead of time that - /// none of the children will ever try to keep themselves alive. - /// - /// Defaults to true. - final bool addAutomaticKeepAlives; - - /// Whether to wrap each child in a [RepaintBoundary]. - /// - /// Typically, children in a scrolling container are wrapped in repaint - /// boundaries so that they do not need to be repainted as the list scrolls. - /// If the children are easy to repaint (e.g., solid color blocks or a short - /// snippet of text), it might be more efficient to not add a repaint boundary - /// and simply repaint the children during scrolling. - /// - /// Defaults to true. - final bool addRepaintBoundaries; - - /// Whether to wrap each child in an [IndexedSemantics]. - /// - /// Typically, children in a scrolling container must be annotated with a - /// semantic index in order to generate the correct accessibility - /// announcements. This should only be set to false if the indexes have - /// already been provided by an [IndexedSemantics] widget. - /// - /// Defaults to true. - /// - /// See also: - /// - /// * [IndexedSemantics], for an explanation of how to manually - /// provide semantic indexes. - final bool addSemanticIndexes; - - /// {@macro flutter.rendering.RenderViewportBase.cacheExtent} - final double? cacheExtent; - - /// The number of children that will contribute semantic information. - /// - /// Some subtypes of [ScrollView] can infer this value automatically. For - /// example [ListView] will use the number of widgets in the child list, - /// while the [ListView.separated] constructor will use half that amount. - /// - /// For [CustomScrollView] and other types which do not receive a builder - /// or list of widgets, the child count must be explicitly provided. If the - /// number is unknown or unbounded this should be left unset or set to null. - /// - /// See also: - /// - /// * [SemanticsConfiguration.scrollChildCount], - /// the corresponding semantics property. - final int? semanticChildCount; - - /// {@macro flutter.widgets.scrollable.dragStartBehavior} - final DragStartBehavior dragStartBehavior; - - /// {@template flutter.widgets.scroll_view.keyboardDismissBehavior} - /// [ScrollViewKeyboardDismissBehavior] the defines how this [ScrollView] will - /// dismiss the keyboard automatically. - /// {@endtemplate} - final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior; - - /// {@macro flutter.widgets.scrollable.restorationId} - final String? restorationId; - - /// {@macro flutter.material.Material.clipBehavior} - /// - /// Defaults to [Clip.hardEdge]. - final Clip clipBehavior; - - @override - Widget build(BuildContext context) { - return PagedValueGridView( - scrollDirection: scrollDirection, - reverse: reverse, - controller: controller, - primary: primary, - physics: physics, - shrinkWrap: shrinkWrap, - padding: padding, - scrollController: scrollController, - addAutomaticKeepAlives: addAutomaticKeepAlives, - addRepaintBoundaries: addRepaintBoundaries, - addSemanticIndexes: addSemanticIndexes, - cacheExtent: cacheExtent, - semanticChildCount: semanticChildCount, - dragStartBehavior: dragStartBehavior, - keyboardDismissBehavior: keyboardDismissBehavior, - restorationId: restorationId, - clipBehavior: clipBehavior, - gridDelegate: gridDelegate, - itemBuilder: (context, members, index) { - final member = members[index]; - final onTap = onMemberTap; - final onLongPress = onMemberLongPress; - - final streamMemberGridTile = StreamMemberGridTile( - user: member.user!, - onTap: onTap == null ? null : () => onTap(member), - onLongPress: onLongPress == null ? null : () => onLongPress(member), - ); - - return itemBuilder?.call( - context, - members, - index, - streamMemberGridTile, - ) ?? - streamMemberGridTile; - }, - emptyBuilder: (context) { - final chatThemeData = StreamChatTheme.of(context); - return emptyBuilder?.call(context) ?? - Center( - child: Padding( - padding: const EdgeInsets.all(8), - child: StreamScrollViewEmptyWidget( - emptyIcon: StreamSvgIcon( - size: 148, - icon: StreamSvgIcons.user, - color: chatThemeData.colorTheme.disabled, - ), - emptyTitle: Text( - context.translations.noUsersLabel, - style: chatThemeData.textTheme.headline, - ), - ), - ), - ); - }, - loadMoreErrorBuilder: (context, error) => - StreamScrollViewLoadMoreError.grid( - onTap: controller.retry, - error: Text( - context.translations.loadingUsersError, - textAlign: TextAlign.center, - ), - ), - loadMoreIndicatorBuilder: (context) => const Center( - child: Padding( - padding: EdgeInsets.all(16), - child: StreamScrollViewLoadMoreIndicator(), - ), - ), - loadingBuilder: (context) => - loadingBuilder?.call(context) ?? - const Center( - child: StreamScrollViewLoadingWidget(), - ), - errorBuilder: (context, error) => - errorBuilder?.call(context, error) ?? - Center( - child: StreamScrollViewErrorWidget( - errorTitle: Text(context.translations.loadingUsersError), - onRetryPressed: controller.refresh, - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/member_scroll_view/stream_member_list_view.dart b/packages/stream_chat_flutter/lib/src/scroll_view/member_scroll_view/stream_member_list_view.dart deleted file mode 100644 index fcdad5379e..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/member_scroll_view/stream_member_list_view.dart +++ /dev/null @@ -1,363 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_error_widget.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_error.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_indicator.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_loading_widget.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Default separator builder for [StreamMemberListView]. -Widget defaultMemberListViewSeparatorBuilder( - BuildContext context, - List members, - int index, -) => - const StreamUserListSeparator(); - -/// Signature for the item builder that creates the children of the -/// [StreamMemberListView]. -typedef StreamMemberListViewIndexedWidgetBuilder - = StreamScrollViewIndexedWidgetBuilder; - -/// Signature for the member grid tile, currently equal to [StreamUserListTile]. -typedef StreamMemberListTile = StreamUserListTile; - -/// A [ListView] that shows a list of [Member]s, -/// it uses [StreamMemberListTile] as a default item. -/// -/// Example: -/// -/// ```dart -/// StreamMemberListView( -/// controller: controller, -/// onMemberTap: (member) { -/// // Handle member tap event -/// }, -/// onMemberLongPress: (member) { -/// // Handle member long press event -/// }, -/// ) -/// ``` -/// -/// See also: -/// * [StreamMemberListTile] -/// * [StreamMemberListController] -class StreamMemberListView extends StatelessWidget { - /// Creates a new instance of [StreamMemberListView]. - const StreamMemberListView({ - super.key, - required this.controller, - this.itemBuilder, - this.separatorBuilder = defaultMemberListViewSeparatorBuilder, - this.emptyBuilder, - this.loadingBuilder, - this.errorBuilder, - this.onMemberTap, - this.onMemberLongPress, - this.loadMoreTriggerIndex = 3, - this.scrollDirection = Axis.vertical, - this.reverse = false, - this.scrollController, - this.primary, - this.physics, - this.shrinkWrap = false, - this.padding, - this.addAutomaticKeepAlives = true, - this.addRepaintBoundaries = true, - this.addSemanticIndexes = true, - this.cacheExtent, - this.dragStartBehavior = DragStartBehavior.start, - this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, - this.restorationId, - this.clipBehavior = Clip.hardEdge, - }); - - /// The [StreamMemberListController] used to control the list of members. - final StreamMemberListController controller; - - /// A builder that is called to build items in the [ListView]. - final StreamMemberListViewIndexedWidgetBuilder? itemBuilder; - - /// A builder that is called to build the list separator. - final PagedValueScrollViewIndexedWidgetBuilder separatorBuilder; - - /// A builder that is called to build the empty state of the list. - final WidgetBuilder? emptyBuilder; - - /// A builder that is called to build the loading state of the list. - final WidgetBuilder? loadingBuilder; - - /// A builder that is called to build the error state of the list. - final Widget Function(BuildContext, StreamChatError)? errorBuilder; - - /// Called when the member taps this list tile. - final void Function(Member)? onMemberTap; - - /// Called when the member long-presses on this list tile. - final void Function(Member)? onMemberLongPress; - - /// The index to take into account when triggering [controller.loadMore]. - final int loadMoreTriggerIndex; - - /// {@template flutter.widgets.scroll_view.scrollDirection} - /// The axis along which the scroll view scrolls. - /// - /// Defaults to [Axis.vertical]. - /// {@endtemplate} - final Axis scrollDirection; - - /// The amount of space by which to inset the children. - final EdgeInsetsGeometry? padding; - - /// Whether to wrap each child in an [AutomaticKeepAlive]. - /// - /// Typically, children in lazy list are wrapped in [AutomaticKeepAlive] - /// widgets so that children can use [KeepAliveNotification]s to preserve - /// their state when they would otherwise be garbage collected off-screen. - /// - /// This feature (and [addRepaintBoundaries]) must be disabled if the children - /// are going to manually maintain their [KeepAlive] state. It may also be - /// more efficient to disable this feature if it is known ahead of time that - /// none of the children will ever try to keep themselves alive. - /// - /// Defaults to true. - final bool addAutomaticKeepAlives; - - /// Whether to wrap each child in a [RepaintBoundary]. - /// - /// Typically, children in a scrolling container are wrapped in repaint - /// boundaries so that they do not need to be repainted as the list scrolls. - /// If the children are easy to repaint (e.g., solid color blocks or a short - /// snippet of text), it might be more efficient to not add a repaint boundary - /// and simply repaint the children during scrolling. - /// - /// Defaults to true. - final bool addRepaintBoundaries; - - /// Whether to wrap each child in an [IndexedSemantics]. - /// - /// Typically, children in a scrolling container must be annotated with a - /// semantic index in order to generate the correct accessibility - /// announcements. This should only be set to false if the indexes have - /// already been provided by an [IndexedSemantics] widget. - /// - /// Defaults to true. - /// - /// See also: - /// - /// * [IndexedSemantics], for an explanation of how to manually - /// provide semantic indexes. - final bool addSemanticIndexes; - - /// {@template flutter.widgets.scroll_view.reverse} - /// Whether the scroll view scrolls in the reading direction. - /// - /// For example, if [scrollDirection] is [Axis.vertical], then the scroll view - /// scrolls from top to bottom when [reverse] is false and from bottom to top - /// when [reverse] is true. - /// - /// Defaults to false. - /// {@endtemplate} - final bool reverse; - - /// {@template flutter.widgets.scroll_view.controller} - /// An object that can be used to control the position to which this scroll - /// view is scrolled. - /// - /// Must be null if [primary] is true. - /// - /// A [ScrollController] serves several purposes. It can be used to control - /// the initial scroll position (see [ScrollController.initialScrollOffset]). - /// It can be used to control whether the scroll view should automatically - /// save and restore its scroll position in the [PageStorage] (see - /// [ScrollController.keepScrollOffset]). It can be used to read the current - /// scroll position (see [ScrollController.offset]), or change it (see - /// [ScrollController.animateTo]). - /// {@endtemplate} - final ScrollController? scrollController; - - /// {@template flutter.widgets.scroll_view.primary} - /// Whether this is the primary scroll view associated with the parent - /// [PrimaryScrollController]. - /// - /// When this is true, the scroll view is scrollable even if it does not have - /// sufficient content to actually scroll. Otherwise, by default the user can - /// only scroll the view if it has sufficient content. See [physics]. - /// - /// Also when true, the scroll view is used for default [ScrollAction]s. If a - /// ScrollAction is not handled by an otherwise focused part of the - /// application, the ScrollAction will be evaluated using this scroll view, - /// for example, when executing [Shortcuts] key events like page up and down. - /// - /// On iOS, this also identifies the scroll view that will scroll to top in - /// response to a tap in the status bar. - /// {@endtemplate} - /// - /// Defaults to true when [scrollController] is null. - final bool? primary; - - /// {@template flutter.widgets.scroll_view.shrinkWrap} - /// Whether the extent of the scroll view in the [scrollDirection] should be - /// determined by the contents being viewed. - /// - /// If the scroll view does not shrink wrap, then the scroll view will expand - /// to the maximum allowed size in the [scrollDirection]. If the scroll view - /// has unbounded constraints in the [scrollDirection], then [shrinkWrap] must - /// be true. - /// - /// Shrink wrapping the content of the scroll view is significantly more - /// expensive than expanding to the maximum allowed size because the content - /// can expand and contract during scrolling, which means the size of the - /// scroll view needs to be recomputed whenever the scroll position changes. - /// - /// Defaults to false. - /// {@endtemplate} - final bool shrinkWrap; - - /// {@template flutter.widgets.scroll_view.physics} - /// How the scroll view should respond to user input. - /// - /// For example, determines how the scroll view continues to animate after the - /// user stops dragging the scroll view. - /// - /// Defaults to matching platform conventions. Furthermore, if [primary] is - /// false, then the user cannot scroll if there is insufficient content to - /// scroll, while if [primary] is true, they can always attempt to scroll. - /// - /// To force the scroll view to always be scrollable even if there is - /// insufficient content, as if [primary] was true but without necessarily - /// setting it to true, provide an [AlwaysScrollableScrollPhysics] physics - /// object, as in: - /// - /// ```dart - /// physics: const AlwaysScrollableScrollPhysics(), - /// ``` - /// - /// To force the scroll view to use the default platform conventions and not - /// be scrollable if there is insufficient content, regardless of the value of - /// [primary], provide an explicit [ScrollPhysics] object, as in: - /// - /// ```dart - /// physics: const ScrollPhysics(), - /// ``` - /// - /// The physics can be changed dynamically (by providing a new object in a - /// subsequent build), but new physics will only take effect if the _class_ of - /// the provided object changes. Merely constructing a new instance with a - /// different configuration is insufficient to cause the physics to be - /// reapplied. (This is because the final object used is generated - /// dynamically, which can be relatively expensive, and it would be - /// inefficient to speculatively create this object each frame to see if the - /// physics should be updated.) - /// {@endtemplate} - /// - /// If an explicit [ScrollBehavior] is provided to [scrollBehavior], the - /// [ScrollPhysics] provided by that behavior will take precedence after - /// [physics]. - final ScrollPhysics? physics; - - /// {@macro flutter.rendering.RenderViewportBase.cacheExtent} - final double? cacheExtent; - - /// {@macro flutter.widgets.scrollable.dragStartBehavior} - final DragStartBehavior dragStartBehavior; - - /// {@template flutter.widgets.scroll_view.keyboardDismissBehavior} - /// [ScrollViewKeyboardDismissBehavior] the defines how this [ScrollView] will - /// dismiss the keyboard automatically. - /// {@endtemplate} - final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior; - - /// {@macro flutter.widgets.scrollable.restorationId} - final String? restorationId; - - /// {@macro flutter.material.Material.clipBehavior} - /// - /// Defaults to [Clip.hardEdge]. - final Clip clipBehavior; - - @override - Widget build(BuildContext context) => PagedValueListView( - scrollDirection: scrollDirection, - padding: padding, - physics: physics, - reverse: reverse, - controller: controller, - scrollController: scrollController, - primary: primary, - shrinkWrap: shrinkWrap, - addAutomaticKeepAlives: addAutomaticKeepAlives, - addRepaintBoundaries: addRepaintBoundaries, - addSemanticIndexes: addSemanticIndexes, - keyboardDismissBehavior: keyboardDismissBehavior, - restorationId: restorationId, - dragStartBehavior: dragStartBehavior, - cacheExtent: cacheExtent, - clipBehavior: clipBehavior, - loadMoreTriggerIndex: loadMoreTriggerIndex, - separatorBuilder: separatorBuilder, - itemBuilder: (context, members, index) { - final member = members[index]; - final onTap = onMemberTap; - final onLongPress = onMemberLongPress; - - final streamUserListTile = StreamMemberListTile( - user: member.user!, - onTap: onTap == null ? null : () => onTap(member), - onLongPress: onLongPress == null ? null : () => onLongPress(member), - ); - - return itemBuilder?.call( - context, - members, - index, - streamUserListTile, - ) ?? - streamUserListTile; - }, - emptyBuilder: (context) { - final chatThemeData = StreamChatTheme.of(context); - return emptyBuilder?.call(context) ?? - Center( - child: Padding( - padding: const EdgeInsets.all(8), - child: StreamScrollViewEmptyWidget( - emptyIcon: StreamSvgIcon( - size: 148, - icon: StreamSvgIcons.user, - color: chatThemeData.colorTheme.disabled, - ), - emptyTitle: Text( - context.translations.noUsersLabel, - style: chatThemeData.textTheme.headline, - ), - ), - ), - ); - }, - loadMoreErrorBuilder: (context, error) => - StreamScrollViewLoadMoreError.list( - onTap: controller.retry, - error: Text(context.translations.loadingUsersError), - ), - loadMoreIndicatorBuilder: (context) => const Center( - child: Padding( - padding: EdgeInsets.all(16), - child: StreamScrollViewLoadMoreIndicator(), - ), - ), - loadingBuilder: (context) => - loadingBuilder?.call(context) ?? - const Center( - child: StreamScrollViewLoadingWidget(), - ), - errorBuilder: (context, error) => - errorBuilder?.call(context, error) ?? - Center( - child: StreamScrollViewErrorWidget( - errorTitle: Text(context.translations.loadingUsersError), - onRetryPressed: controller.refresh, - ), - ), - ); -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/message_search_scroll_view/stream_message_search_grid_view.dart b/packages/stream_chat_flutter/lib/src/scroll_view/message_search_scroll_view/stream_message_search_grid_view.dart deleted file mode 100644 index e7a939f942..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/message_search_scroll_view/stream_message_search_grid_view.dart +++ /dev/null @@ -1,364 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_error_widget.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_error.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_indicator.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_loading_widget.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Default grid delegate for [StreamMessageSearchGridView]. -const defaultMessageSearchGridViewDelegate = - SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 4); - -/// Signature for the item builder that creates the children of the -/// [StreamMessageSearchGridView]. -typedef StreamMessageSearchGridViewIndexedWidgetBuilder - = PagedValueScrollViewIndexedWidgetBuilder; - -/// A [GridView] that shows a grid of [GetMessageResponse]s, -/// it uses [StreamMessageSearchGridTile] as a default item. -/// -/// Example: -/// -/// ```dart -/// StreamMessageSearchGridView( -/// controller: controller, -/// itemBuilder: (context, messageResponses, index) { -/// return GridTile(message: messageResponses[index]); -/// }, -/// ) -/// ``` -/// -/// See also: -/// * [StreamUserListTile] -/// * [StreamUserListController] -class StreamMessageSearchGridView extends StatelessWidget { - /// Creates a new instance of [StreamMessageSearchGridView]. - const StreamMessageSearchGridView({ - super.key, - required this.controller, - required this.itemBuilder, - this.gridDelegate = defaultMessageSearchGridViewDelegate, - this.emptyBuilder, - this.loadMoreErrorBuilder, - this.loadMoreIndicatorBuilder, - this.loadingBuilder, - this.errorBuilder, - this.loadMoreTriggerIndex = 3, - this.scrollDirection = Axis.vertical, - this.reverse = false, - this.scrollController, - this.primary, - this.physics, - this.shrinkWrap = false, - this.padding, - this.addAutomaticKeepAlives = true, - this.addRepaintBoundaries = true, - this.addSemanticIndexes = true, - this.cacheExtent, - this.semanticChildCount, - this.dragStartBehavior = DragStartBehavior.start, - this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, - this.restorationId, - this.clipBehavior = Clip.hardEdge, - }); - - /// The [StreamUserListController] used to control the grid of users. - final StreamMessageSearchListController controller; - - /// A delegate that controls the layout of the children within - /// the [PagedValueGridView]. - final SliverGridDelegate gridDelegate; - - /// A builder that is called to build items in the [PagedValueGridView]. - /// - /// The `value` parameter is the [GetMessageBuilder] - /// at this position in the grid. - final StreamMessageSearchGridViewIndexedWidgetBuilder itemBuilder; - - /// A builder that is called to build the empty state of the grid. - final WidgetBuilder? emptyBuilder; - - /// A builder that is called to build the load more error state of the grid. - final PagedValueScrollViewLoadMoreErrorBuilder? loadMoreErrorBuilder; - - /// A builder that is called to build the load more indicator of the grid. - final WidgetBuilder? loadMoreIndicatorBuilder; - - /// A builder that is called to build the loading state of the grid. - final WidgetBuilder? loadingBuilder; - - /// A builder that is called to build the error state of the grid. - final Widget Function(BuildContext, StreamChatError)? errorBuilder; - - /// The index to take into account when triggering [controller.loadMore]. - final int loadMoreTriggerIndex; - - /// {@template flutter.widgets.scroll_view.scrollDirection} - /// The axis along which the scroll view scrolls. - /// - /// Defaults to [Axis.vertical]. - /// {@endtemplate} - final Axis scrollDirection; - - /// {@template flutter.widgets.scroll_view.reverse} - /// Whether the scroll view scrolls in the reading direction. - /// - /// For example, if the reading direction is left-to-right and - /// [scrollDirection] is [Axis.horizontal], then the scroll view scrolls from - /// left to right when [reverse] is false and from right to left when - /// [reverse] is true. - /// - /// Similarly, if [scrollDirection] is [Axis.vertical], then the scroll view - /// scrolls from top to bottom when [reverse] is false and from bottom to top - /// when [reverse] is true. - /// - /// Defaults to false. - /// {@endtemplate} - final bool reverse; - - /// {@template flutter.widgets.scroll_view.controller} - /// An object that can be used to control the position to which this scroll - /// view is scrolled. - /// - /// Must be null if [primary] is true. - /// - /// A [ScrollController] serves several purposes. It can be used to control - /// the initial scroll position (see [ScrollController.initialScrollOffset]). - /// It can be used to control whether the scroll view should automatically - /// save and restore its scroll position in the [PageStorage] (see - /// [ScrollController.keepScrollOffset]). It can be used to read the current - /// scroll position (see [ScrollController.offset]), or change it (see - /// [ScrollController.animateTo]). - /// {@endtemplate} - final ScrollController? scrollController; - - /// {@template flutter.widgets.scroll_view.primary} - /// Whether this is the primary scroll view associated with the parent - /// [PrimaryScrollController]. - /// - /// When this is true, the scroll view is scrollable even if it does not have - /// sufficient content to actually scroll. Otherwise, by default the user can - /// only scroll the view if it has sufficient content. See [physics]. - /// - /// Also when true, the scroll view is used for default [ScrollAction]s. If a - /// ScrollAction is not handled by - /// an otherwise focused part of the application, - /// the ScrollAction will be evaluated using this scroll view, for example, - /// when executing [Shortcuts] key events like page up and down. - /// - /// On iOS, this also identifies the scroll view that will scroll to top in - /// response to a tap in the status bar. - /// {@endtemplate} - /// - /// Defaults to true when [scrollDirection] is [Axis.vertical] and - /// [controller] is null. - final bool? primary; - - /// {@template flutter.widgets.scroll_view.physics} - /// How the scroll view should respond to user input. - /// - /// For example, determines how the scroll view continues to animate after the - /// user stops dragging the scroll view. - /// - /// Defaults to matching platform conventions. Furthermore, if [primary] is - /// false, then the user cannot scroll if there is insufficient content to - /// scroll, while if [primary] is true, they can always attempt to scroll. - /// - /// To force the scroll view to always be scrollable even if there is - /// insufficient content, as if [primary] was true but without necessarily - /// setting it to true, provide an [AlwaysScrollableScrollPhysics] physics - /// object, as in: - /// - /// ```dart - /// physics: const AlwaysScrollableScrollPhysics(), - /// ``` - /// - /// To force the scroll view to use the default platform conventions and not - /// be scrollable if there is insufficient content, regardless of the value of - /// [primary], provide an explicit [ScrollPhysics] object, as in: - /// - /// ```dart - /// physics: const ScrollPhysics(), - /// ``` - /// - /// The physics can be changed dynamically (by providing a new object in a - /// subsequent build), but new physics will only take effect if the _class_ of - /// the provided object changes. Merely constructing a new instance with a - /// different configuration is insufficient to cause the physics to be - /// reapplied. (This is because the final object used is generated - /// dynamically, which can be relatively expensive, and it would be - /// inefficient to speculatively create this object each frame to see if the - /// physics should be updated.) - /// {@endtemplate} - /// - /// If an explicit [ScrollBehavior] is provided to [scrollBehavior], the - /// [ScrollPhysics] provided by that behavior will take precedence after - /// [physics]. - final ScrollPhysics? physics; - - /// {@template flutter.widgets.scroll_view.shrinkWrap} - /// Whether the extent of the scroll view in the [scrollDirection] should be - /// determined by the contents being viewed. - /// - /// If the scroll view does not shrink wrap, then the scroll view will expand - /// to the maximum allowed size in the [scrollDirection]. If the scroll view - /// has unbounded constraints in the [scrollDirection], then [shrinkWrap] must - /// be true. - /// - /// Shrink wrapping the content of the scroll view is significantly more - /// expensive than expanding to the maximum allowed size because the content - /// can expand and contract during scrolling, which means the size of the - /// scroll view needs to be recomputed whenever the scroll position changes. - /// - /// Defaults to false. - /// {@endtemplate} - final bool shrinkWrap; - - /// The amount of space by which to inset the children. - final EdgeInsetsGeometry? padding; - - /// Whether to wrap each child in an [AutomaticKeepAlive]. - /// - /// Typically, children in lazy list are wrapped in [AutomaticKeepAlive] - /// widgets so that children can use [KeepAliveNotification]s to preserve - /// their state when they would otherwise be garbage collected off-screen. - /// - /// This feature (and [addRepaintBoundaries]) must be disabled if the children - /// are going to manually maintain their [KeepAlive] state. It may also be - /// more efficient to disable this feature if it is known ahead of time that - /// none of the children will ever try to keep themselves alive. - /// - /// Defaults to true. - final bool addAutomaticKeepAlives; - - /// Whether to wrap each child in a [RepaintBoundary]. - /// - /// Typically, children in a scrolling container are wrapped in repaint - /// boundaries so that they do not need to be repainted as the list scrolls. - /// If the children are easy to repaint (e.g., solid color blocks or a short - /// snippet of text), it might be more efficient to not add a repaint boundary - /// and simply repaint the children during scrolling. - /// - /// Defaults to true. - final bool addRepaintBoundaries; - - /// Whether to wrap each child in an [IndexedSemantics]. - /// - /// Typically, children in a scrolling container must be annotated with a - /// semantic index in order to generate the correct accessibility - /// announcements. This should only be set to false if the indexes have - /// already been provided by an [IndexedSemantics] widget. - /// - /// Defaults to true. - /// - /// See also: - /// - /// * [IndexedSemantics], for an explanation of how to manually - /// provide semantic indexes. - final bool addSemanticIndexes; - - /// {@macro flutter.rendering.RenderViewportBase.cacheExtent} - final double? cacheExtent; - - /// The number of children that will contribute semantic information. - /// - /// Some subtypes of [ScrollView] can infer this value automatically. For - /// example [ListView] will use the number of widgets in the child list, - /// while the [ListView.separated] constructor will use half that amount. - /// - /// For [CustomScrollView] and other types which do not receive a builder - /// or list of widgets, the child count must be explicitly provided. If the - /// number is unknown or unbounded this should be left unset or set to null. - /// - /// See also: - /// - /// * [SemanticsConfiguration.scrollChildCount], - /// the corresponding semantics property. - final int? semanticChildCount; - - /// {@macro flutter.widgets.scrollable.dragStartBehavior} - final DragStartBehavior dragStartBehavior; - - /// {@template flutter.widgets.scroll_view.keyboardDismissBehavior} - /// [ScrollViewKeyboardDismissBehavior] the defines how this [ScrollView] will - /// dismiss the keyboard automatically. - /// {@endtemplate} - final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior; - - /// {@macro flutter.widgets.scrollable.restorationId} - final String? restorationId; - - /// {@macro flutter.material.Material.clipBehavior} - /// - /// Defaults to [Clip.hardEdge]. - final Clip clipBehavior; - - @override - Widget build(BuildContext context) { - return PagedValueGridView( - scrollDirection: scrollDirection, - reverse: reverse, - controller: controller, - primary: primary, - physics: physics, - shrinkWrap: shrinkWrap, - padding: padding, - scrollController: scrollController, - addAutomaticKeepAlives: addAutomaticKeepAlives, - addRepaintBoundaries: addRepaintBoundaries, - addSemanticIndexes: addSemanticIndexes, - cacheExtent: cacheExtent, - semanticChildCount: semanticChildCount, - dragStartBehavior: dragStartBehavior, - keyboardDismissBehavior: keyboardDismissBehavior, - restorationId: restorationId, - clipBehavior: clipBehavior, - gridDelegate: gridDelegate, - itemBuilder: itemBuilder, - emptyBuilder: (context) { - final chatThemeData = StreamChatTheme.of(context); - return emptyBuilder?.call(context) ?? - Center( - child: Padding( - padding: const EdgeInsets.all(8), - child: StreamScrollViewEmptyWidget( - emptyIcon: StreamSvgIcon( - size: 148, - icon: StreamSvgIcons.message, - color: chatThemeData.colorTheme.disabled, - ), - emptyTitle: Text( - context.translations.emptyMessagesText, - style: chatThemeData.textTheme.headline, - ), - ), - ), - ); - }, - loadMoreErrorBuilder: (context, error) => - StreamScrollViewLoadMoreError.grid( - onTap: controller.retry, - error: Text(context.translations.loadingMessagesError), - ), - loadMoreIndicatorBuilder: (context) => const Center( - child: Padding( - padding: EdgeInsets.all(16), - child: StreamScrollViewLoadMoreIndicator(), - ), - ), - loadingBuilder: (context) => - loadingBuilder?.call(context) ?? - const Center( - child: StreamScrollViewLoadingWidget(), - ), - errorBuilder: (context, error) => - errorBuilder?.call(context, error) ?? - Center( - child: StreamScrollViewErrorWidget( - onRetryPressed: controller.refresh, - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/message_search_scroll_view/stream_message_search_list_tile.dart b/packages/stream_chat_flutter/lib/src/scroll_view/message_search_scroll_view/stream_message_search_list_tile.dart deleted file mode 100644 index 3eb03aec1e..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/message_search_scroll_view/stream_message_search_list_tile.dart +++ /dev/null @@ -1,240 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// A widget that displays a message search item. -/// -/// This widget is intended to be used as a -/// Tile in [StreamMessageSearchListView]. -/// -/// It displays the message's text, channel, sender, and timestamp. -/// -/// See also: -/// * [StreamMessageSearchListView] -/// * [StreamUserAvatar] -class StreamMessageSearchListTile extends StatelessWidget { - /// Creates a new instance of [StreamMessageSearchListTile]. - const StreamMessageSearchListTile({ - super.key, - required this.messageResponse, - this.leading, - this.title, - this.subtitle, - this.trailing, - this.onTap, - this.onLongPress, - this.tileColor, - this.visualDensity = VisualDensity.compact, - this.contentPadding = const EdgeInsets.symmetric(horizontal: 8), - }); - - /// The message response to display. - final GetMessageResponse messageResponse; - - /// A widget to display before the title. - final Widget? leading; - - /// The primary content of the list tile. - final Widget? title; - - /// Additional content displayed below the title. - final Widget? subtitle; - - /// A widget to display at the end of tile. - final Widget? trailing; - - /// Called when the user taps this list tile. - final GestureTapCallback? onTap; - - /// Called when the user long-presses on this list tile. - final GestureLongPressCallback? onLongPress; - - /// {@template flutter.material.ListTile.tileColor} - /// Defines the background color of `ListTile`. - /// - /// When the value is null, - /// the `tileColor` is set to [ListTileTheme.tileColor] - /// if it's not null and to [Colors.transparent] if it's null. - /// {@endtemplate} - final Color? tileColor; - - /// Defines how compact the list tile's layout will be. - /// - /// {@macro flutter.material.themedata.visualDensity} - /// - /// See also: - /// - /// * [ThemeData.visualDensity], which specifies the [visualDensity] for all - /// widgets within a [Theme]. - final VisualDensity visualDensity; - - /// The tile's internal padding. - /// - /// Insets a [ListTile]'s contents: its [leading], [title], [subtitle], - /// and [trailing] widgets. - /// - /// If null, `EdgeInsets.symmetric(horizontal: 16.0)` is used. - final EdgeInsetsGeometry contentPadding; - - /// Creates a copy of this tile but with the given fields replaced with - /// the new values. - StreamMessageSearchListTile copyWith({ - Key? key, - GetMessageResponse? messageResponse, - Widget? leading, - Widget? title, - Widget? subtitle, - Widget? trailing, - GestureTapCallback? onTap, - GestureLongPressCallback? onLongPress, - Color? tileColor, - VisualDensity? visualDensity, - EdgeInsetsGeometry? contentPadding, - }) => - StreamMessageSearchListTile( - key: key ?? this.key, - messageResponse: messageResponse ?? this.messageResponse, - leading: leading ?? this.leading, - title: title ?? this.title, - subtitle: subtitle ?? this.subtitle, - trailing: trailing ?? this.trailing, - onTap: onTap ?? this.onTap, - onLongPress: onLongPress ?? this.onLongPress, - tileColor: tileColor ?? this.tileColor, - visualDensity: visualDensity ?? this.visualDensity, - contentPadding: contentPadding ?? this.contentPadding, - ); - - @override - Widget build(BuildContext context) { - final message = messageResponse.message; - final user = message.user!; - final channelPreviewTheme = StreamChannelPreviewTheme.of(context); - - final leading = this.leading ?? - StreamUserAvatar( - user: user, - constraints: const BoxConstraints.tightFor( - height: 40, - width: 40, - ), - ); - - final title = this.title ?? - MessageSearchListTileTitle( - messageResponse: messageResponse, - textStyle: channelPreviewTheme.titleStyle - ?.copyWith(overflow: TextOverflow.ellipsis), - ); - - final subtitle = this.subtitle ?? - Row( - children: [ - Expanded( - child: StreamMessagePreviewText( - message: message, - textStyle: channelPreviewTheme.subtitleStyle, - ), - ), - const SizedBox(width: 16), - MessageSearchTileMessageDate( - message: message, - textStyle: channelPreviewTheme.lastMessageAtStyle, - ), - ], - ); - - return ListTile( - onTap: onTap, - onLongPress: onLongPress, - visualDensity: visualDensity, - contentPadding: contentPadding, - tileColor: tileColor, - leading: leading, - trailing: trailing, - title: title, - subtitle: subtitle, - ); - } -} - -/// A widget that displays the title of a [StreamMessageSearchListTile]. -class MessageSearchListTileTitle extends StatelessWidget { - /// Creates a new [MessageSearchListTileTitle] instance. - const MessageSearchListTileTitle({ - super.key, - required this.messageResponse, - this.textStyle, - }); - - /// The message response for the tile. - final GetMessageResponse messageResponse; - - /// The style to use for the title. - final TextStyle? textStyle; - - @override - Widget build(BuildContext context) { - final user = messageResponse.message.user!; - final channel = messageResponse.channel; - final channelName = channel?.extraData['name']; - - return Text.rich( - TextSpan( - children: [ - TextSpan( - text: user.id == StreamChat.of(context).currentUser?.id - ? context.translations.youText - : user.name, - ), - if (channelName != null) ...[ - TextSpan( - text: ' ${context.translations.inText} ', - style: textStyle?.copyWith( - fontWeight: FontWeight.normal, - ), - ), - TextSpan( - text: channelName as String, - ), - ], - ], - ), - style: textStyle, - ); - } -} - -/// A widget which shows formatted created date of the passed [message]. -class MessageSearchTileMessageDate extends StatelessWidget { - /// Creates a new instance of [MessageSearchTileMessageDate]. - const MessageSearchTileMessageDate({ - super.key, - required this.message, - this.textStyle, - }); - - /// The searched message response. - final Message message; - - /// The text style to use for the date. - final TextStyle? textStyle; - - @override - Widget build(BuildContext context) { - final createdAt = message.createdAt; - String stringDate; - final now = DateTime.now(); - if (now.year != createdAt.year || - now.month != createdAt.month || - now.day != createdAt.day) { - stringDate = Jiffy.parseFromDateTime(createdAt.toLocal()).yMd; - } else { - stringDate = Jiffy.parseFromDateTime(createdAt.toLocal()).jm; - } - - return Text( - stringDate, - style: textStyle, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/message_search_scroll_view/stream_message_search_list_view.dart b/packages/stream_chat_flutter/lib/src/scroll_view/message_search_scroll_view/stream_message_search_list_view.dart deleted file mode 100644 index 2f4c75468f..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/message_search_scroll_view/stream_message_search_list_view.dart +++ /dev/null @@ -1,386 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_error_widget.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_error.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_indicator.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_loading_widget.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Default separator builder for [StreamMessageSearchListView]. -Widget defaultMessageSearchListViewSeparatorBuilder( - BuildContext context, - List responses, - int index, -) => - const StreamMessageSearchListSeparator(); - -/// Signature for the item builder that creates the children of the -/// [StreamMessageSearchListView]. -typedef StreamMessageSearchListViewIndexedWidgetBuilder - = StreamScrollViewIndexedWidgetBuilder; - -/// A [ListView] that shows a list of [GetMessageResponse]s, -/// it uses [StreamMessageSearchListTile] as a default item. -/// -/// This is the new version of [MessageSearchListView] that uses -/// [StreamMessageSearchListController]. -/// -/// Example: -/// -/// ```dart -/// StreamMessageSearchListView( -/// controller: controller, -/// onMessageTap: (user) { -/// // Handle user tap event -/// }, -/// onMessageLongPress: (user) { -/// // Handle user long press event -/// }, -/// ) -/// ``` -/// -/// See also: -/// * [StreamMessageSearchListTile] -/// * [StreamMessageSearchListController] -class StreamMessageSearchListView extends StatelessWidget { - /// Creates a new instance of [StreamMessageSearchListView]. - const StreamMessageSearchListView({ - super.key, - required this.controller, - this.itemBuilder, - this.separatorBuilder = defaultMessageSearchListViewSeparatorBuilder, - this.emptyBuilder, - this.loadingBuilder, - this.errorBuilder, - this.onMessageTap, - this.onMessageLongPress, - this.loadMoreTriggerIndex = 3, - this.scrollDirection = Axis.vertical, - this.reverse = false, - this.scrollController, - this.primary, - this.physics, - this.shrinkWrap = false, - this.padding, - this.addAutomaticKeepAlives = true, - this.addRepaintBoundaries = true, - this.addSemanticIndexes = true, - this.cacheExtent, - this.dragStartBehavior = DragStartBehavior.start, - this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, - this.restorationId, - this.clipBehavior = Clip.hardEdge, - }); - - /// The [StreamUserListController] used to control the list of - /// searched messages. - final StreamMessageSearchListController controller; - - /// A builder that is called to build items in the [ListView]. - final StreamMessageSearchListViewIndexedWidgetBuilder? itemBuilder; - - /// A builder that is called to build the list separator. - final PagedValueScrollViewIndexedWidgetBuilder - separatorBuilder; - - /// A builder that is called to build the empty state of the list. - final WidgetBuilder? emptyBuilder; - - /// A builder that is called to build the loading state of the list. - final WidgetBuilder? loadingBuilder; - - /// A builder that is called to build the error state of the list. - final Widget Function(BuildContext, StreamChatError)? errorBuilder; - - /// Called when the user taps this list tile. - final void Function(GetMessageResponse)? onMessageTap; - - /// Called when the user long-presses on this list tile. - final void Function(GetMessageResponse)? onMessageLongPress; - - /// The index to take into account when triggering [controller.loadMore]. - final int loadMoreTriggerIndex; - - /// {@template flutter.widgets.scroll_view.scrollDirection} - /// The axis along which the scroll view scrolls. - /// - /// Defaults to [Axis.vertical]. - /// {@endtemplate} - final Axis scrollDirection; - - /// The amount of space by which to inset the children. - final EdgeInsetsGeometry? padding; - - /// Whether to wrap each child in an [AutomaticKeepAlive]. - /// - /// Typically, children in lazy list are wrapped in [AutomaticKeepAlive] - /// widgets so that children can use [KeepAliveNotification]s to preserve - /// their state when they would otherwise be garbage collected off-screen. - /// - /// This feature (and [addRepaintBoundaries]) must be disabled if the children - /// are going to manually maintain their [KeepAlive] state. It may also be - /// more efficient to disable this feature if it is known ahead of time that - /// none of the children will ever try to keep themselves alive. - /// - /// Defaults to true. - final bool addAutomaticKeepAlives; - - /// Whether to wrap each child in a [RepaintBoundary]. - /// - /// Typically, children in a scrolling container are wrapped in repaint - /// boundaries so that they do not need to be repainted as the list scrolls. - /// If the children are easy to repaint (e.g., solid color blocks or a short - /// snippet of text), it might be more efficient to not add a repaint boundary - /// and simply repaint the children during scrolling. - /// - /// Defaults to true. - final bool addRepaintBoundaries; - - /// Whether to wrap each child in an [IndexedSemantics]. - /// - /// Typically, children in a scrolling container must be annotated with a - /// semantic index in order to generate the correct accessibility - /// announcements. This should only be set to false if the indexes have - /// already been provided by an [IndexedSemantics] widget. - /// - /// Defaults to true. - /// - /// See also: - /// - /// * [IndexedSemantics], for an explanation of how to manually - /// provide semantic indexes. - final bool addSemanticIndexes; - - /// {@template flutter.widgets.scroll_view.reverse} - /// Whether the scroll view scrolls in the reading direction. - /// - /// For example, if [scrollDirection] is [Axis.vertical], then the scroll view - /// scrolls from top to bottom when [reverse] is false and from bottom to top - /// when [reverse] is true. - /// - /// Defaults to false. - /// {@endtemplate} - final bool reverse; - - /// {@template flutter.widgets.scroll_view.controller} - /// An object that can be used to control the position to which this scroll - /// view is scrolled. - /// - /// Must be null if [primary] is true. - /// - /// A [ScrollController] serves several purposes. It can be used to control - /// the initial scroll position (see [ScrollController.initialScrollOffset]). - /// It can be used to control whether the scroll view should automatically - /// save and restore its scroll position in the [PageStorage] (see - /// [ScrollController.keepScrollOffset]). It can be used to read the current - /// scroll position (see [ScrollController.offset]), or change it (see - /// [ScrollController.animateTo]). - /// {@endtemplate} - final ScrollController? scrollController; - - /// {@template flutter.widgets.scroll_view.primary} - /// Whether this is the primary scroll view associated with the parent - /// [PrimaryScrollController]. - /// - /// When this is true, the scroll view is scrollable even if it does not have - /// sufficient content to actually scroll. Otherwise, by default the user can - /// only scroll the view if it has sufficient content. See [physics]. - /// - /// Also when true, the scroll view is used for default [ScrollAction]s. If a - /// ScrollAction is not handled by an otherwise focused part of the - /// application, the ScrollAction will be evaluated using this scroll view, - /// for example, when executing [Shortcuts] key events like page up and down. - /// - /// On iOS, this also identifies the scroll view that will scroll to top in - /// response to a tap in the status bar. - /// {@endtemplate} - /// - /// Defaults to true when [scrollController] is null. - final bool? primary; - - /// {@template flutter.widgets.scroll_view.shrinkWrap} - /// Whether the extent of the scroll view in the [scrollDirection] should be - /// determined by the contents being viewed. - /// - /// If the scroll view does not shrink wrap, then the scroll view will expand - /// to the maximum allowed size in the [scrollDirection]. If the scroll view - /// has unbounded constraints in the [scrollDirection], then [shrinkWrap] must - /// be true. - /// - /// Shrink wrapping the content of the scroll view is significantly more - /// expensive than expanding to the maximum allowed size because the content - /// can expand and contract during scrolling, which means the size of the - /// scroll view needs to be recomputed whenever the scroll position changes. - /// - /// Defaults to false. - /// {@endtemplate} - final bool shrinkWrap; - - /// {@template flutter.widgets.scroll_view.physics} - /// How the scroll view should respond to user input. - /// - /// For example, determines how the scroll view continues to animate after the - /// user stops dragging the scroll view. - /// - /// Defaults to matching platform conventions. Furthermore, if [primary] is - /// false, then the user cannot scroll if there is insufficient content to - /// scroll, while if [primary] is true, they can always attempt to scroll. - /// - /// To force the scroll view to always be scrollable even if there is - /// insufficient content, as if [primary] was true but without necessarily - /// setting it to true, provide an [AlwaysScrollableScrollPhysics] physics - /// object, as in: - /// - /// ```dart - /// physics: const AlwaysScrollableScrollPhysics(), - /// ``` - /// - /// To force the scroll view to use the default platform conventions and not - /// be scrollable if there is insufficient content, regardless of the value of - /// [primary], provide an explicit [ScrollPhysics] object, as in: - /// - /// ```dart - /// physics: const ScrollPhysics(), - /// ``` - /// - /// The physics can be changed dynamically (by providing a new object in a - /// subsequent build), but new physics will only take effect if the _class_ of - /// the provided object changes. Merely constructing a new instance with a - /// different configuration is insufficient to cause the physics to be - /// reapplied. (This is because the final object used is generated - /// dynamically, which can be relatively expensive, and it would be - /// inefficient to speculatively create this object each frame to see if the - /// physics should be updated.) - /// {@endtemplate} - /// - /// If an explicit [ScrollBehavior] is provided to [scrollBehavior], the - /// [ScrollPhysics] provided by that behavior will take precedence after - /// [physics]. - final ScrollPhysics? physics; - - /// {@macro flutter.rendering.RenderViewportBase.cacheExtent} - final double? cacheExtent; - - /// {@macro flutter.widgets.scrollable.dragStartBehavior} - final DragStartBehavior dragStartBehavior; - - /// {@template flutter.widgets.scroll_view.keyboardDismissBehavior} - /// [ScrollViewKeyboardDismissBehavior] the defines how this [ScrollView] will - /// dismiss the keyboard automatically. - /// {@endtemplate} - final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior; - - /// {@macro flutter.widgets.scrollable.restorationId} - final String? restorationId; - - /// {@macro flutter.material.Material.clipBehavior} - /// - /// Defaults to [Clip.hardEdge]. - final Clip clipBehavior; - - @override - Widget build(BuildContext context) { - return PagedValueListView( - scrollDirection: scrollDirection, - padding: padding, - physics: physics, - reverse: reverse, - controller: controller, - scrollController: scrollController, - primary: primary, - shrinkWrap: shrinkWrap, - addAutomaticKeepAlives: addAutomaticKeepAlives, - addRepaintBoundaries: addRepaintBoundaries, - addSemanticIndexes: addSemanticIndexes, - keyboardDismissBehavior: keyboardDismissBehavior, - restorationId: restorationId, - dragStartBehavior: dragStartBehavior, - cacheExtent: cacheExtent, - clipBehavior: clipBehavior, - loadMoreTriggerIndex: loadMoreTriggerIndex, - separatorBuilder: separatorBuilder, - itemBuilder: (context, messageResponses, index) { - final messageResponse = messageResponses[index]; - final onTap = onMessageTap; - final onLongPress = onMessageLongPress; - - final streamMessageSearchListTile = StreamMessageSearchListTile( - messageResponse: messageResponse, - onTap: onTap == null ? null : () => onTap(messageResponse), - onLongPress: - onLongPress == null ? null : () => onLongPress(messageResponse), - ); - - return itemBuilder?.call( - context, - messageResponses, - index, - streamMessageSearchListTile, - ) ?? - streamMessageSearchListTile; - }, - emptyBuilder: (context) { - final chatThemeData = StreamChatTheme.of(context); - return emptyBuilder?.call(context) ?? - Center( - child: Padding( - padding: const EdgeInsets.all(8), - child: StreamScrollViewEmptyWidget( - emptyIcon: StreamSvgIcon( - size: 148, - icon: StreamSvgIcons.message, - color: chatThemeData.colorTheme.disabled, - ), - emptyTitle: Text( - context.translations.emptyMessagesText, - style: chatThemeData.textTheme.headline, - ), - ), - ), - ); - }, - loadMoreErrorBuilder: (context, error) => - StreamScrollViewLoadMoreError.list( - onTap: controller.retry, - error: Text(context.translations.loadingMessagesError), - ), - loadMoreIndicatorBuilder: (context) => const Center( - child: Padding( - padding: EdgeInsets.all(16), - child: StreamScrollViewLoadMoreIndicator(), - ), - ), - loadingBuilder: (context) => - loadingBuilder?.call(context) ?? - const Center( - child: StreamScrollViewLoadingWidget(), - ), - errorBuilder: (context, error) => - errorBuilder?.call(context, error) ?? - Center( - child: StreamScrollViewErrorWidget( - errorTitle: Text(context.translations.loadingMessagesError), - onRetryPressed: controller.refresh, - ), - ), - ); - } -} - -/// A widget that is used to display a separator between -/// [StreamMessageSearchListTile] items. -class StreamMessageSearchListSeparator extends StatelessWidget { - /// Creates a new instance of [StreamMessageSearchListSeparator]. - const StreamMessageSearchListSeparator({super.key}); - - @override - Widget build(BuildContext context) { - final effect = StreamChatTheme.of(context).colorTheme.borderBottom; - return Container( - height: 1, - // ignore: deprecated_member_use - color: effect.color!.withOpacity(effect.alpha ?? 1.0), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/photo_gallery/stream_photo_gallery.dart b/packages/stream_chat_flutter/lib/src/scroll_view/photo_gallery/stream_photo_gallery.dart deleted file mode 100644 index dcee2b76a4..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/photo_gallery/stream_photo_gallery.dart +++ /dev/null @@ -1,411 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:photo_manager/photo_manager.dart' - show AssetEntity, ThumbnailFormat, ThumbnailSize; - -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_error_widget.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_error.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_indicator.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_loading_widget.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Default grid delegate for [StreamPhotoGallery]. -const defaultStreamPhotoGalleryDelegate = - SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, - mainAxisSpacing: 2, - crossAxisSpacing: 2, -); - -/// Signature for the item builder that creates the children of the -/// [StreamPhotoGallery]. -typedef StreamPhotoGalleryIndexedWidgetBuilder - = StreamScrollViewIndexedWidgetBuilder; - -/// Widget used to display a gallery of photos in the form of grid. -class StreamPhotoGallery extends StatelessWidget { - /// Creates a [StreamPhotoGallery] widget. - const StreamPhotoGallery({ - super.key, - required this.controller, - this.gridDelegate = defaultStreamPhotoGalleryDelegate, - this.itemBuilder, - this.emptyBuilder, - this.loadMoreErrorBuilder, - this.loadMoreIndicatorBuilder, - this.loadingBuilder, - this.errorBuilder, - this.onMediaTap, - this.onMediaLongPress, - this.loadMoreTriggerIndex = 3, - this.scrollDirection = Axis.vertical, - this.reverse = false, - this.scrollController, - this.primary, - this.physics, - this.shrinkWrap = false, - this.padding, - this.addAutomaticKeepAlives = true, - this.addRepaintBoundaries = true, - this.addSemanticIndexes = true, - this.cacheExtent, - this.semanticChildCount, - this.dragStartBehavior = DragStartBehavior.start, - this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, - this.restorationId, - this.clipBehavior = Clip.hardEdge, - this.thumbnailSize = const ThumbnailSize(400, 400), - this.thumbnailFormat = ThumbnailFormat.jpeg, - this.thumbnailQuality = 100, - this.thumbnailScale = 1, - }); - - /// The [StreamPhotoGalleryController] used to control the grid of users. - final StreamPhotoGalleryController controller; - - /// A delegate that controls the layout of the children within - /// the [PagedValueGridView]. - final SliverGridDelegate gridDelegate; - - /// A builder that is called to build items in the [PagedValueGridView]. - final StreamPhotoGalleryIndexedWidgetBuilder? itemBuilder; - - /// A builder that is called to build the empty state of the grid. - final WidgetBuilder? emptyBuilder; - - /// A builder that is called to build the load more error state of the grid. - final PagedValueScrollViewLoadMoreErrorBuilder? loadMoreErrorBuilder; - - /// A builder that is called to build the load more indicator of the grid. - final WidgetBuilder? loadMoreIndicatorBuilder; - - /// A builder that is called to build the loading state of the grid. - final WidgetBuilder? loadingBuilder; - - /// A builder that is called to build the error state of the grid. - final Widget Function(BuildContext, StreamChatError)? errorBuilder; - - /// Called when the user taps this grid tile. - final void Function(AssetEntity)? onMediaTap; - - /// Called when the user long-presses on this grid tile. - final void Function(AssetEntity)? onMediaLongPress; - - /// The index to take into account when triggering [controller.loadMore]. - final int loadMoreTriggerIndex; - - /// {@template flutter.widgets.scroll_view.scrollDirection} - /// The axis along which the scroll view scrolls. - /// - /// Defaults to [Axis.vertical]. - /// {@endtemplate} - final Axis scrollDirection; - - /// {@template flutter.widgets.scroll_view.reverse} - /// Whether the scroll view scrolls in the reading direction. - /// - /// For example, if the reading direction is left-to-right and - /// [scrollDirection] is [Axis.horizontal], then the scroll view scrolls from - /// left to right when [reverse] is false and from right to left when - /// [reverse] is true. - /// - /// Similarly, if [scrollDirection] is [Axis.vertical], then the scroll view - /// scrolls from top to bottom when [reverse] is false and from bottom to top - /// when [reverse] is true. - /// - /// Defaults to false. - /// {@endtemplate} - final bool reverse; - - /// {@template flutter.widgets.scroll_view.controller} - /// An object that can be used to control the position to which this scroll - /// view is scrolled. - /// - /// Must be null if [primary] is true. - /// - /// A [ScrollController] serves several purposes. It can be used to control - /// the initial scroll position (see [ScrollController.initialScrollOffset]). - /// It can be used to control whether the scroll view should automatically - /// save and restore its scroll position in the [PageStorage] (see - /// [ScrollController.keepScrollOffset]). It can be used to read the current - /// scroll position (see [ScrollController.offset]), or change it (see - /// [ScrollController.animateTo]). - /// {@endtemplate} - final ScrollController? scrollController; - - /// {@template flutter.widgets.scroll_view.primary} - /// Whether this is the primary scroll view associated with the parent - /// [PrimaryScrollController]. - /// - /// When this is true, the scroll view is scrollable even if it does not have - /// sufficient content to actually scroll. Otherwise, by default the user can - /// only scroll the view if it has sufficient content. See [physics]. - /// - /// Also when true, the scroll view is used for default [ScrollAction]s. If a - /// ScrollAction is not handled by - /// an otherwise focused part of the application, - /// the ScrollAction will be evaluated using this scroll view, for example, - /// when executing [Shortcuts] key events like page up and down. - /// - /// On iOS, this also identifies the scroll view that will scroll to top in - /// response to a tap in the status bar. - /// {@endtemplate} - /// - /// Defaults to true when [scrollDirection] is [Axis.vertical] and - /// [controller] is null. - final bool? primary; - - /// {@template flutter.widgets.scroll_view.physics} - /// How the scroll view should respond to user input. - /// - /// For example, determines how the scroll view continues to animate after the - /// user stops dragging the scroll view. - /// - /// Defaults to matching platform conventions. Furthermore, if [primary] is - /// false, then the user cannot scroll if there is insufficient content to - /// scroll, while if [primary] is true, they can always attempt to scroll. - /// - /// To force the scroll view to always be scrollable even if there is - /// insufficient content, as if [primary] was true but without necessarily - /// setting it to true, provide an [AlwaysScrollableScrollPhysics] physics - /// object, as in: - /// - /// ```dart - /// physics: const AlwaysScrollableScrollPhysics(), - /// ``` - /// - /// To force the scroll view to use the default platform conventions and not - /// be scrollable if there is insufficient content, regardless of the value of - /// [primary], provide an explicit [ScrollPhysics] object, as in: - /// - /// ```dart - /// physics: const ScrollPhysics(), - /// ``` - /// - /// The physics can be changed dynamically (by providing a new object in a - /// subsequent build), but new physics will only take effect if the _class_ of - /// the provided object changes. Merely constructing a new instance with a - /// different configuration is insufficient to cause the physics to be - /// reapplied. (This is because the final object used is generated - /// dynamically, which can be relatively expensive, and it would be - /// inefficient to speculatively create this object each frame to see if the - /// physics should be updated.) - /// {@endtemplate} - /// - /// If an explicit [ScrollBehavior] is provided to [scrollBehavior], the - /// [ScrollPhysics] provided by that behavior will take precedence after - /// [physics]. - final ScrollPhysics? physics; - - /// {@template flutter.widgets.scroll_view.shrinkWrap} - /// Whether the extent of the scroll view in the [scrollDirection] should be - /// determined by the contents being viewed. - /// - /// If the scroll view does not shrink wrap, then the scroll view will expand - /// to the maximum allowed size in the [scrollDirection]. If the scroll view - /// has unbounded constraints in the [scrollDirection], then [shrinkWrap] must - /// be true. - /// - /// Shrink wrapping the content of the scroll view is significantly more - /// expensive than expanding to the maximum allowed size because the content - /// can expand and contract during scrolling, which means the size of the - /// scroll view needs to be recomputed whenever the scroll position changes. - /// - /// Defaults to false. - /// {@endtemplate} - final bool shrinkWrap; - - /// The amount of space by which to inset the children. - final EdgeInsetsGeometry? padding; - - /// Whether to wrap each child in an [AutomaticKeepAlive]. - /// - /// Typically, children in lazy list are wrapped in [AutomaticKeepAlive] - /// widgets so that children can use [KeepAliveNotification]s to preserve - /// their state when they would otherwise be garbage collected off-screen. - /// - /// This feature (and [addRepaintBoundaries]) must be disabled if the children - /// are going to manually maintain their [KeepAlive] state. It may also be - /// more efficient to disable this feature if it is known ahead of time that - /// none of the children will ever try to keep themselves alive. - /// - /// Defaults to true. - final bool addAutomaticKeepAlives; - - /// Whether to wrap each child in a [RepaintBoundary]. - /// - /// Typically, children in a scrolling container are wrapped in repaint - /// boundaries so that they do not need to be repainted as the list scrolls. - /// If the children are easy to repaint (e.g., solid color blocks or a short - /// snippet of text), it might be more efficient to not add a repaint boundary - /// and simply repaint the children during scrolling. - /// - /// Defaults to true. - final bool addRepaintBoundaries; - - /// Whether to wrap each child in an [IndexedSemantics]. - /// - /// Typically, children in a scrolling container must be annotated with a - /// semantic index in order to generate the correct accessibility - /// announcements. This should only be set to false if the indexes have - /// already been provided by an [IndexedSemantics] widget. - /// - /// Defaults to true. - /// - /// See also: - /// - /// * [IndexedSemantics], for an explanation of how to manually - /// provide semantic indexes. - final bool addSemanticIndexes; - - /// {@macro flutter.rendering.RenderViewportBase.cacheExtent} - final double? cacheExtent; - - /// The number of children that will contribute semantic information. - /// - /// Some subtypes of [ScrollView] can infer this value automatically. For - /// example [ListView] will use the number of widgets in the child list, - /// while the [ListView.separated] constructor will use half that amount. - /// - /// For [CustomScrollView] and other types which do not receive a builder - /// or list of widgets, the child count must be explicitly provided. If the - /// number is unknown or unbounded this should be left unset or set to null. - /// - /// See also: - /// - /// * [SemanticsConfiguration.scrollChildCount], - /// the corresponding semantics property. - final int? semanticChildCount; - - /// {@macro flutter.widgets.scrollable.dragStartBehavior} - final DragStartBehavior dragStartBehavior; - - /// {@template flutter.widgets.scroll_view.keyboardDismissBehavior} - /// [ScrollViewKeyboardDismissBehavior] the defines how this [ScrollView] will - /// dismiss the keyboard automatically. - /// {@endtemplate} - final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior; - - /// {@macro flutter.widgets.scrollable.restorationId} - final String? restorationId; - - /// {@macro flutter.material.Material.clipBehavior} - /// - /// Defaults to [Clip.hardEdge]. - final Clip clipBehavior; - - /// The thumbnail size. - final ThumbnailSize thumbnailSize; - - /// {@macro photo_manager.ThumbnailFormat} - final ThumbnailFormat thumbnailFormat; - - /// The quality value for the thumbnail. - /// - /// Valid from 1 to 100. - /// Defaults to 100. - final int thumbnailQuality; - - /// Scale of the image. - final double thumbnailScale; - - @override - Widget build(BuildContext context) { - return PagedValueGridView( - scrollDirection: scrollDirection, - reverse: reverse, - controller: controller, - primary: primary, - physics: physics, - shrinkWrap: shrinkWrap, - padding: padding, - scrollController: scrollController, - addAutomaticKeepAlives: addAutomaticKeepAlives, - addRepaintBoundaries: addRepaintBoundaries, - addSemanticIndexes: addSemanticIndexes, - cacheExtent: cacheExtent, - semanticChildCount: semanticChildCount, - dragStartBehavior: dragStartBehavior, - keyboardDismissBehavior: keyboardDismissBehavior, - restorationId: restorationId, - clipBehavior: clipBehavior, - loadMoreTriggerIndex: loadMoreTriggerIndex, - gridDelegate: gridDelegate, - itemBuilder: (context, mediaList, index) { - final media = mediaList[index]; - final onTap = onMediaTap; - final onLongPress = onMediaLongPress; - - final streamPhotoGalleryTile = StreamPhotoGalleryTile( - media: media, - onTap: onTap == null ? null : () => onTap(media), - onLongPress: onLongPress == null ? null : () => onLongPress(media), - thumbnailSize: thumbnailSize, - thumbnailFormat: thumbnailFormat, - thumbnailQuality: thumbnailQuality, - thumbnailScale: thumbnailScale, - ); - - return itemBuilder?.call( - context, - mediaList, - index, - streamPhotoGalleryTile, - ) ?? - streamPhotoGalleryTile; - }, - emptyBuilder: (context) { - final chatThemeData = StreamChatTheme.of(context); - return emptyBuilder?.call(context) ?? - Center( - child: Padding( - padding: const EdgeInsets.all(8), - child: StreamScrollViewEmptyWidget( - emptyIcon: StreamSvgIcon( - size: 148, - icon: StreamSvgIcons.pictures, - color: chatThemeData.colorTheme.disabled, - ), - emptyTitle: Text( - context.translations.noPhotoOrVideoLabel, - style: chatThemeData.textTheme.headline, - ), - ), - ), - ); - }, - loadMoreErrorBuilder: (context, error) { - return StreamScrollViewLoadMoreError.grid( - onTap: controller.retry, - error: Text( - context.translations.genericErrorText, - textAlign: TextAlign.center, - ), - ); - }, - loadMoreIndicatorBuilder: (context) { - return const Center( - child: Padding( - padding: EdgeInsets.all(16), - child: StreamScrollViewLoadMoreIndicator(), - ), - ); - }, - loadingBuilder: (context) { - return loadingBuilder?.call(context) ?? - const Center( - child: StreamScrollViewLoadingWidget(), - ); - }, - errorBuilder: (context, error) { - return errorBuilder?.call(context, error) ?? - Center( - child: StreamScrollViewErrorWidget( - errorTitle: Text(context.translations.genericErrorText), - onRetryPressed: controller.refresh, - ), - ); - }, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/photo_gallery/stream_photo_gallery_controller.dart b/packages/stream_chat_flutter/lib/src/scroll_view/photo_gallery/stream_photo_gallery_controller.dart deleted file mode 100644 index eb9b21bbf9..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/photo_gallery/stream_photo_gallery_controller.dart +++ /dev/null @@ -1,87 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:photo_manager/photo_manager.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// -class StreamPhotoGalleryController - extends PagedValueNotifier { - /// - StreamPhotoGalleryController({ - this.limit = 50, - }) : super(const PagedValue.loading()); - - /// The maximum number of items to load at once. - final int limit; - - Future _getRecentAssetPathList({ - RequestType type = RequestType.common, - FilterOptionGroup? filterOption, - }) { - return PhotoManager.getAssetPathList( - type: type, - onlyAll: true, - filterOption: filterOption, - ).then((it) => it.firstOrNull); - } - - @override - Future doInitialLoad() async { - try { - final assets = await _getRecentAssetPathList(); - - if (assets == null) { - value = const PagedValue(items: []); - return; - } - - final mediaList = await assets.getAssetListPaged( - page: 0, - size: limit, - ); - - final nextKey = mediaList.length < limit ? null : 1; - value = PagedValue( - items: mediaList, - nextPageKey: nextKey, - ); - } on StreamChatError catch (error) { - value = PagedValue.error(error); - } catch (error) { - final chatError = StreamChatError(error.toString()); - value = PagedValue.error(chatError); - } - } - - @override - Future loadMore(int page) async { - final previousValue = value.asSuccess; - - try { - final assets = await _getRecentAssetPathList(); - - if (assets == null) { - const chatError = StreamChatError('No media found'); - value = previousValue.copyWith(error: chatError); - return; - } - - final mediaList = await assets.getAssetListPaged( - page: page, - size: limit, - ); - - final previousItems = previousValue.items; - final newItems = previousItems + mediaList; - final nextKey = mediaList.length < limit ? null : page + 1; - value = PagedValue( - items: newItems, - nextPageKey: nextKey, - ); - } on StreamChatError catch (error) { - value = previousValue.copyWith(error: error); - } catch (error) { - final chatError = StreamChatError(error.toString()); - value = previousValue.copyWith(error: chatError); - } - } -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/photo_gallery/stream_photo_gallery_tile.dart b/packages/stream_chat_flutter/lib/src/scroll_view/photo_gallery/stream_photo_gallery_tile.dart deleted file mode 100644 index 6b7ef7ebdc..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/photo_gallery/stream_photo_gallery_tile.dart +++ /dev/null @@ -1,261 +0,0 @@ -import 'dart:ui' as ui; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:photo_manager/photo_manager.dart'; -import 'package:stream_chat_flutter/src/icons/stream_svg_icon.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -/// Widget that displays a photo or video item from the gallery. -class StreamPhotoGalleryTile extends StatelessWidget { - /// Creates a new instance of [StreamPhotoGalleryTile]. - const StreamPhotoGalleryTile({ - super.key, - required this.media, - this.selected = false, - this.onTap, - this.onLongPress, - this.thumbnailSize = const ThumbnailSize(400, 400), - this.thumbnailFormat = ThumbnailFormat.jpeg, - this.thumbnailQuality = 100, - this.thumbnailScale = 1, - }); - - /// The media item to display. - final AssetEntity media; - - /// Whether the media item is selected. - final bool selected; - - /// Called when the user taps this grid tile. - final GestureTapCallback? onTap; - - /// Called when the user long-presses on this grid tile. - final GestureLongPressCallback? onLongPress; - - /// The thumbnail size. - final ThumbnailSize thumbnailSize; - - /// {@macro photo_manager.ThumbnailFormat} - final ThumbnailFormat thumbnailFormat; - - /// The quality value for the thumbnail. - /// - /// Valid from 1 to 100. - /// Defaults to 100. - final int thumbnailQuality; - - /// Scale of the image. - final double thumbnailScale; - - /// Creates a copy of this tile but with the given fields replaced with - /// the new values. - StreamPhotoGalleryTile copyWith({ - Key? key, - AssetEntity? media, - bool? selected, - GestureTapCallback? onTap, - GestureLongPressCallback? onLongPress, - ThumbnailSize? thumbnailSize, - ThumbnailFormat? thumbnailFormat, - int? thumbnailQuality, - double? thumbnailScale, - }) => - StreamPhotoGalleryTile( - key: key ?? this.key, - media: media ?? this.media, - selected: selected ?? this.selected, - onTap: onTap ?? this.onTap, - onLongPress: onLongPress ?? this.onLongPress, - thumbnailSize: thumbnailSize ?? this.thumbnailSize, - thumbnailFormat: thumbnailFormat ?? this.thumbnailFormat, - thumbnailQuality: thumbnailQuality ?? this.thumbnailQuality, - thumbnailScale: thumbnailScale ?? this.thumbnailScale, - ); - - @override - Widget build(BuildContext context) { - final chatThemeData = StreamChatTheme.of(context); - return Stack( - children: [ - AspectRatio( - aspectRatio: 1, - child: FadeInImage( - fadeInDuration: const Duration(milliseconds: 300), - placeholder: const AssetImage( - 'lib/assets/images/placeholder.png', - package: 'stream_chat_flutter', - ), - fit: BoxFit.cover, - image: MediaThumbnailProvider( - media: media, - size: thumbnailSize, - format: thumbnailFormat, - quality: thumbnailQuality, - scale: thumbnailScale, - ), - ), - ), - Positioned.fill( - child: IgnorePointer( - child: AnimatedOpacity( - duration: const Duration(milliseconds: 300), - opacity: selected ? 1.0 : 0.0, - child: Container( - color: - // ignore: deprecated_member_use - chatThemeData.colorTheme.textHighEmphasis.withOpacity(0.5), - alignment: Alignment.topRight, - padding: const EdgeInsets.only( - top: 8, - right: 8, - ), - child: CircleAvatar( - radius: 12, - backgroundColor: chatThemeData.colorTheme.barsBg, - child: StreamSvgIcon( - size: 24, - icon: StreamSvgIcons.check, - color: chatThemeData.colorTheme.textHighEmphasis, - ), - ), - ), - ), - ), - ), - if (media.type == AssetType.video) ...[ - const Positioned( - left: 8, - bottom: 10, - child: StreamSvgIcon(icon: StreamSvgIcons.videoCall), - ), - Positioned( - right: 4, - bottom: 10, - child: Text( - media.videoDuration.format(), - style: TextStyle( - color: chatThemeData.colorTheme.barsBg, - ), - ), - ), - ], - // https://stackoverflow.com/a/59317162/10036882 - Positioned.fill( - child: Material( - color: Colors.transparent, - child: InkWell( - onTap: onTap, - onLongPress: onLongPress, - ), - ), - ), - ], - ); - } -} - -extension on Duration { - String format() { - final s = '$this'.split('.')[0].padLeft(8, '0'); - if (s.startsWith('00:')) { - return s.replaceFirst('00:', ''); - } - - return s; - } -} - -/// {@template mediaThumbnailProvider} -/// Builds a thumbnail using [ImageProvider]. -/// {@endtemplate} -class MediaThumbnailProvider extends ImageProvider { - /// {@macro mediaThumbnailProvider} - const MediaThumbnailProvider({ - required this.media, - // TODO: Are these sizes optimal? Consider web/desktop - this.size = const ThumbnailSize(400, 400), - this.format = ThumbnailFormat.jpeg, - this.quality = 100, - this.scale = 1, - }); - - /// Media to load - final AssetEntity media; - - /// The thumbnail size. - final ThumbnailSize size; - - /// {@macro photo_manager.ThumbnailFormat} - final ThumbnailFormat format; - - /// The quality value for the thumbnail. - /// - /// Valid from 1 to 100. - /// Defaults to 100. - final int quality; - - /// Scale of the image. - final double scale; - - @override - Future obtainKey(ImageConfiguration configuration) { - return SynchronousFuture(this); - } - - @override - ImageStreamCompleter loadImage( - MediaThumbnailProvider key, - ImageDecoderCallback decode, - ) { - return MultiFrameImageStreamCompleter( - codec: _loadAsync(key, decode), - scale: key.scale, - informationCollector: () sync* { - yield DiagnosticsProperty( - 'Thumbnail provider: $this \n Thumbnail key: $key', - this, - style: DiagnosticsTreeStyle.errorProperty, - ); - }, - ); - } - - Future _loadAsync( - MediaThumbnailProvider key, - ImageDecoderCallback decode, - ) async { - assert(key == this, '$key is not $this'); - final bytes = await media.thumbnailDataWithSize( - size, - format: format, - quality: quality, - ); - final buffer = await ui.ImmutableBuffer.fromUint8List(bytes!); - return decode(buffer); - } - - @override - bool operator ==(Object other) { - if (other is MediaThumbnailProvider) { - return media == other.media && - size == other.size && - format == other.format && - quality == other.quality && - scale == other.scale; - } - return false; - } - - @override - int get hashCode => Object.hash(media, size, format, quality, scale); - - @override - String toString() => '$runtimeType(' - 'media: $media, ' - 'size: $size, ' - 'format: $format, ' - 'quality: $quality, ' - 'scale: $scale' - ')'; -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/poll_vote_scroll_view/stream_poll_vote_list_tile.dart b/packages/stream_chat_flutter/lib/src/scroll_view/poll_vote_scroll_view/stream_poll_vote_list_tile.dart deleted file mode 100644 index 7820d57223..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/poll_vote_scroll_view/stream_poll_vote_list_tile.dart +++ /dev/null @@ -1,178 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamPollVoteListTile} -/// A widget that displays a poll vote in a list tile. -/// -/// Used in [StreamPollVoteListView] as a list tile for each poll vote. -/// {@endtemplate} -class StreamPollVoteListTile extends StatelessWidget { - /// {@macro streamPollVoteListTile} - const StreamPollVoteListTile({ - super.key, - required this.pollVote, - this.showAnswerText = false, - this.onTap, - this.onLongPress, - this.tileColor, - this.borderRadius, - this.contentPadding, - }); - - /// The poll vote to display the tile for. - final PollVote pollVote; - - /// Whether to show the answer text. - final bool showAnswerText; - - /// Called when the user taps this list tile. - final GestureTapCallback? onTap; - - /// Called when the user long-presses on this list tile. - final GestureLongPressCallback? onLongPress; - - /// Defines the background color of the tile. - final Color? tileColor; - - /// The tile's border radius. - final BorderRadiusGeometry? borderRadius; - - /// The tile's internal padding. - final EdgeInsetsGeometry? contentPadding; - - /// Creates a copy of this tile but with the given fields replaced with - /// the new values. - StreamPollVoteListTile copyWith({ - Key? key, - PollVote? pollVote, - bool? showAnswerText, - GestureTapCallback? onTap, - GestureLongPressCallback? onLongPress, - Color? tileColor, - BorderRadiusGeometry? borderRadius, - EdgeInsetsGeometry? contentPadding, - }) => - StreamPollVoteListTile( - key: key ?? this.key, - pollVote: pollVote ?? this.pollVote, - showAnswerText: showAnswerText ?? this.showAnswerText, - onTap: onTap ?? this.onTap, - onLongPress: onLongPress ?? this.onLongPress, - tileColor: tileColor ?? this.tileColor, - borderRadius: borderRadius ?? this.borderRadius, - contentPadding: contentPadding ?? this.contentPadding, - ); - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - - return InkWell( - onTap: onTap, - onLongPress: onLongPress, - child: Container( - padding: contentPadding, - decoration: BoxDecoration( - color: tileColor, - borderRadius: borderRadius, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (pollVote.answerText case final answerText? - when showAnswerText) ...[ - Text( - answerText, - style: theme.textTheme.headlineBold.copyWith( - color: theme.colorTheme.textHighEmphasis, - ), - ), - const SizedBox(height: 16), - ], - Row( - children: [ - if (pollVote.user case final user?) ...[ - StreamUserAvatar( - user: user, - constraints: - BoxConstraints.tight(const Size.fromRadius(10)), - showOnlineStatus: false, - ), - Expanded( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: Text( - user.name, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: theme.textTheme.body.copyWith( - color: theme.colorTheme.textHighEmphasis, - ), - ), - ), - ), - ], - PollVoteUpdatedAt( - dateTime: pollVote.updatedAt.toLocal(), - ), - ], - ) - ], - ), - ), - ); - } -} - -/// {@template pollVoteUpdatedAt} -/// A widget that displays the last updated time of a poll vote. -/// {@endtemplate} -class PollVoteUpdatedAt extends StatelessWidget { - /// {@macro pollVoteUpdatedAt} - const PollVoteUpdatedAt({ - super.key, - required this.dateTime, - }); - - /// The date and time when the poll vote was last updated. - final DateTime dateTime; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - - final createdAt = Jiffy.parseFromDateTime(dateTime); - final now = Jiffy.parseFromDateTime(DateTime.now()); - - var dayInfo = createdAt.MMMd; - if (createdAt.isSame(now, unit: Unit.day)) { - dayInfo = context.translations.todayLabel; - } else if (createdAt.isSame(now.subtract(days: 1), unit: Unit.day)) { - dayInfo = context.translations.yesterdayLabel; - } else if (createdAt.isAfter(now.subtract(days: 7), unit: Unit.day)) { - dayInfo = createdAt.EEEE; - } else if (createdAt.isAfter(now.subtract(years: 1), unit: Unit.day)) { - dayInfo = createdAt.MMMd; - } - - final timeInfo = createdAt.jm; - - return Row( - children: [ - Text( - dayInfo, - style: theme.textTheme.bodyBold.copyWith( - color: theme.colorTheme.textLowEmphasis, - ), - ), - const SizedBox(width: 8), - Text( - timeInfo, - style: theme.textTheme.body.copyWith( - color: theme.colorTheme.textLowEmphasis, - ), - ), - ], - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/poll_vote_scroll_view/stream_poll_vote_list_view.dart b/packages/stream_chat_flutter/lib/src/scroll_view/poll_vote_scroll_view/stream_poll_vote_list_view.dart deleted file mode 100644 index 13aecdb471..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/poll_vote_scroll_view/stream_poll_vote_list_view.dart +++ /dev/null @@ -1,369 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/icons/stream_svg_icon.dart'; -import 'package:stream_chat_flutter/src/scroll_view/poll_vote_scroll_view/stream_poll_vote_list_tile.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_empty_widget.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_error_widget.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_indexed_widget_builder.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_error.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_indicator.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_loading_widget.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter/src/utils/extensions.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// Default separator builder for [StreamPollVoteListView]. -Widget defaultPollVoteListViewSeparatorBuilder( - BuildContext context, - List pollVotes, - int index, -) => - const SizedBox(height: 8); - -/// Signature for the item builder that creates the children of the -/// [StreamPollVoteListView]. -typedef StreamPollVoteListViewIndexedWidgetBuilder - = StreamScrollViewIndexedWidgetBuilder; - -/// {@template streamPollVoteListView} -/// A [ListView] that shows a list of [PollVote] for a poll. It uses a -/// [StreamPollVoteListController] to load the poll votes in paginated form. -/// -/// Example: -/// -/// ```dart -/// StreamPollVoteListView( -/// controller: controller, -/// onPollVoteTap: (user) { -/// // Handle poll vote tap event -/// }, -/// onPollVoteLongPress: (user) { -/// // Handle poll vote long press event -/// }, -/// ) -/// ``` -/// -/// See also: -/// * [StreamPollVoteListTile] -/// * [StreamPollVoteListController] -/// {@endtemplate} -class StreamPollVoteListView extends StatelessWidget { - /// {@macro streamPollVoteListView} - const StreamPollVoteListView({ - super.key, - required this.controller, - this.itemBuilder, - this.separatorBuilder = defaultPollVoteListViewSeparatorBuilder, - this.emptyBuilder, - this.loadingBuilder, - this.errorBuilder, - this.onPollVoteTap, - this.onPollVoteLongPress, - this.loadMoreTriggerIndex = 3, - this.scrollDirection = Axis.vertical, - this.reverse = false, - this.scrollController, - this.primary, - this.physics, - this.shrinkWrap = false, - this.padding, - this.addAutomaticKeepAlives = true, - this.addRepaintBoundaries = true, - this.addSemanticIndexes = true, - this.cacheExtent, - this.dragStartBehavior = DragStartBehavior.start, - this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, - this.restorationId, - this.clipBehavior = Clip.hardEdge, - }); - - /// The [StreamPollVoteListController] used to control the list of poll votes. - final StreamPollVoteListController controller; - - /// A builder that is called to build items in the [ListView]. - final StreamPollVoteListViewIndexedWidgetBuilder? itemBuilder; - - /// A builder that is called to build the list separator. - final PagedValueScrollViewIndexedWidgetBuilder separatorBuilder; - - /// A builder that is called to build the empty state of the list. - final WidgetBuilder? emptyBuilder; - - /// A builder that is called to build the loading state of the list. - final WidgetBuilder? loadingBuilder; - - /// A builder that is called to build the error state of the list. - final Widget Function(BuildContext, StreamChatError)? errorBuilder; - - /// Called when the list items are tapped. - final void Function(PollVote)? onPollVoteTap; - - /// Called when the list items are long pressed. - final void Function(PollVote)? onPollVoteLongPress; - - /// The index to take into account when triggering [controller.loadMore]. - final int loadMoreTriggerIndex; - - /// {@template flutter.widgets.scroll_view.scrollDirection} - /// The axis along which the scroll view scrolls. - /// - /// Defaults to [Axis.vertical]. - /// {@endtemplate} - final Axis scrollDirection; - - /// The amount of space by which to inset the children. - final EdgeInsetsGeometry? padding; - - /// Whether to wrap each child in an [AutomaticKeepAlive]. - /// - /// Typically, children in lazy list are wrapped in [AutomaticKeepAlive] - /// widgets so that children can use [KeepAliveNotification]s to preserve - /// their state when they would otherwise be garbage collected off-screen. - /// - /// This feature (and [addRepaintBoundaries]) must be disabled if the children - /// are going to manually maintain their [KeepAlive] state. It may also be - /// more efficient to disable this feature if it is known ahead of time that - /// none of the children will ever try to keep themselves alive. - /// - /// Defaults to true. - final bool addAutomaticKeepAlives; - - /// Whether to wrap each child in a [RepaintBoundary]. - /// - /// Typically, children in a scrolling container are wrapped in repaint - /// boundaries so that they do not need to be repainted as the list scrolls. - /// If the children are easy to repaint (e.g., solid color blocks or a short - /// snippet of text), it might be more efficient to not add a repaint boundary - /// and simply repaint the children during scrolling. - /// - /// Defaults to true. - final bool addRepaintBoundaries; - - /// Whether to wrap each child in an [IndexedSemantics]. - /// - /// Typically, children in a scrolling container must be annotated with a - /// semantic index in order to generate the correct accessibility - /// announcements. This should only be set to false if the indexes have - /// already been provided by an [IndexedSemantics] widget. - /// - /// Defaults to true. - /// - /// See also: - /// - /// * [IndexedSemantics], for an explanation of how to manually - /// provide semantic indexes. - final bool addSemanticIndexes; - - /// {@template flutter.widgets.scroll_view.reverse} - /// Whether the scroll view scrolls in the reading direction. - /// - /// For example, if [scrollDirection] is [Axis.vertical], then the scroll view - /// scrolls from top to bottom when [reverse] is false and from bottom to top - /// when [reverse] is true. - /// - /// Defaults to false. - /// {@endtemplate} - final bool reverse; - - /// {@template flutter.widgets.scroll_view.controller} - /// An object that can be used to control the position to which this scroll - /// view is scrolled. - /// - /// Must be null if [primary] is true. - /// - /// A [ScrollController] serves several purposes. It can be used to control - /// the initial scroll position (see [ScrollController.initialScrollOffset]). - /// It can be used to control whether the scroll view should automatically - /// save and restore its scroll position in the [PageStorage] (see - /// [ScrollController.keepScrollOffset]). It can be used to read the current - /// scroll position (see [ScrollController.offset]), or change it (see - /// [ScrollController.animateTo]). - /// {@endtemplate} - final ScrollController? scrollController; - - /// {@template flutter.widgets.scroll_view.primary} - /// Whether this is the primary scroll view associated with the parent - /// [PrimaryScrollController]. - /// - /// When this is true, the scroll view is scrollable even if it does not have - /// sufficient content to actually scroll. Otherwise, by default the user can - /// only scroll the view if it has sufficient content. See [physics]. - /// - /// Also when true, the scroll view is used for default [ScrollAction]s. If a - /// ScrollAction is not handled by an otherwise focused part of the - /// application, the ScrollAction will be evaluated using this scroll view, - /// for example, when executing [Shortcuts] key events like page up and down. - /// - /// On iOS, this also identifies the scroll view that will scroll to top in - /// response to a tap in the status bar. - /// {@endtemplate} - /// - /// Defaults to true when [scrollController] is null. - final bool? primary; - - /// {@template flutter.widgets.scroll_view.shrinkWrap} - /// Whether the extent of the scroll view in the [scrollDirection] should be - /// determined by the contents being viewed. - /// - /// If the scroll view does not shrink wrap, then the scroll view will expand - /// to the maximum allowed size in the [scrollDirection]. If the scroll view - /// has unbounded constraints in the [scrollDirection], then [shrinkWrap] must - /// be true. - /// - /// Shrink wrapping the content of the scroll view is significantly more - /// expensive than expanding to the maximum allowed size because the content - /// can expand and contract during scrolling, which means the size of the - /// scroll view needs to be recomputed whenever the scroll position changes. - /// - /// Defaults to false. - /// {@endtemplate} - final bool shrinkWrap; - - /// {@template flutter.widgets.scroll_view.physics} - /// How the scroll view should respond to user input. - /// - /// For example, determines how the scroll view continues to animate after the - /// user stops dragging the scroll view. - /// - /// Defaults to matching platform conventions. Furthermore, if [primary] is - /// false, then the user cannot scroll if there is insufficient content to - /// scroll, while if [primary] is true, they can always attempt to scroll. - /// - /// To force the scroll view to always be scrollable even if there is - /// insufficient content, as if [primary] was true but without necessarily - /// setting it to true, provide an [AlwaysScrollableScrollPhysics] physics - /// object, as in: - /// - /// ```dart - /// physics: const AlwaysScrollableScrollPhysics(), - /// ``` - /// - /// To force the scroll view to use the default platform conventions and not - /// be scrollable if there is insufficient content, regardless of the value of - /// [primary], provide an explicit [ScrollPhysics] object, as in: - /// - /// ```dart - /// physics: const ScrollPhysics(), - /// ``` - /// - /// The physics can be changed dynamically (by providing a new object in a - /// subsequent build), but new physics will only take effect if the _class_ of - /// the provided object changes. Merely constructing a new instance with a - /// different configuration is insufficient to cause the physics to be - /// reapplied. (This is because the final object used is generated - /// dynamically, which can be relatively expensive, and it would be - /// inefficient to speculatively create this object each frame to see if the - /// physics should be updated.) - /// {@endtemplate} - /// - /// If an explicit [ScrollBehavior] is provided to [scrollBehavior], the - /// [ScrollPhysics] provided by that behavior will take precedence after - /// [physics]. - final ScrollPhysics? physics; - - /// {@macro flutter.rendering.RenderViewportBase.cacheExtent} - final double? cacheExtent; - - /// {@macro flutter.widgets.scrollable.dragStartBehavior} - final DragStartBehavior dragStartBehavior; - - /// {@template flutter.widgets.scroll_view.keyboardDismissBehavior} - /// [ScrollViewKeyboardDismissBehavior] the defines how this [ScrollView] will - /// dismiss the keyboard automatically. - /// {@endtemplate} - final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior; - - /// {@macro flutter.widgets.scrollable.restorationId} - final String? restorationId; - - /// {@macro flutter.material.Material.clipBehavior} - /// - /// Defaults to [Clip.hardEdge]. - final Clip clipBehavior; - - @override - Widget build(BuildContext context) => PagedValueListView( - scrollDirection: scrollDirection, - padding: padding, - physics: physics, - reverse: reverse, - controller: controller, - scrollController: scrollController, - primary: primary, - shrinkWrap: shrinkWrap, - addAutomaticKeepAlives: addAutomaticKeepAlives, - addRepaintBoundaries: addRepaintBoundaries, - addSemanticIndexes: addSemanticIndexes, - keyboardDismissBehavior: keyboardDismissBehavior, - restorationId: restorationId, - dragStartBehavior: dragStartBehavior, - cacheExtent: cacheExtent, - clipBehavior: clipBehavior, - loadMoreTriggerIndex: loadMoreTriggerIndex, - separatorBuilder: separatorBuilder, - itemBuilder: (context, pollVotes, index) { - final pollVote = pollVotes[index]; - final onTap = onPollVoteTap; - final onLongPress = onPollVoteLongPress; - - final streamPollVoteListTile = StreamPollVoteListTile( - pollVote: pollVote, - onTap: onTap == null ? null : () => onTap(pollVote), - onLongPress: - onLongPress == null ? null : () => onLongPress(pollVote), - ); - - return itemBuilder?.call( - context, - pollVotes, - index, - streamPollVoteListTile, - ) ?? - streamPollVoteListTile; - }, - emptyBuilder: (context) { - final chatThemeData = StreamChatTheme.of(context); - return emptyBuilder?.call(context) ?? - Center( - child: Padding( - padding: const EdgeInsets.all(8), - child: StreamScrollViewEmptyWidget( - emptyIcon: StreamSvgIcon( - size: 148, - icon: StreamSvgIcons.polls, - color: chatThemeData.colorTheme.disabled, - ), - emptyTitle: Text( - context.translations.noPollVotesLabel, - style: chatThemeData.textTheme.headline, - ), - ), - ), - ); - }, - loadMoreErrorBuilder: (context, error) => - StreamScrollViewLoadMoreError.list( - onTap: controller.retry, - error: Text(context.translations.loadingPollVotesError), - ), - loadMoreIndicatorBuilder: (context) => const Center( - child: Padding( - padding: EdgeInsets.all(16), - child: StreamScrollViewLoadMoreIndicator(), - ), - ), - loadingBuilder: (context) => - loadingBuilder?.call(context) ?? - const Center( - child: StreamScrollViewLoadingWidget(), - ), - errorBuilder: (context, error) => - errorBuilder?.call(context, error) ?? - Center( - child: StreamScrollViewErrorWidget( - errorTitle: Text(context.translations.loadingPollVotesError), - onRetryPressed: controller.refresh, - ), - ), - ); -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_empty_widget.dart b/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_empty_widget.dart deleted file mode 100644 index 6fe8b8524a..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_empty_widget.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// A widget that shows an empty view when the [StreamScrollView] loads -/// empty data. -class StreamScrollViewEmptyWidget extends StatelessWidget { - /// Creates a new instance of the [StreamScrollViewEmptyWidget]. - const StreamScrollViewEmptyWidget({ - super.key, - required this.emptyIcon, - required this.emptyTitle, - this.emptyTitleStyle, - this.mainAxisSize = MainAxisSize.max, - this.mainAxisAlignment = MainAxisAlignment.center, - this.crossAxisAlignment = CrossAxisAlignment.center, - }); - - /// The title of the empty view. - final Widget emptyTitle; - - /// The style of the title. - final TextStyle? emptyTitleStyle; - - /// The icon of the empty view. - final Widget emptyIcon; - - /// The main axis size of the empty view. - final MainAxisSize mainAxisSize; - - /// The main axis alignment of the empty view. - final MainAxisAlignment mainAxisAlignment; - - /// The cross axis alignment of the empty view. - final CrossAxisAlignment crossAxisAlignment; - - @override - Widget build(BuildContext context) { - final chatThemeData = StreamChatTheme.of(context); - - final emptyIcon = AnimatedSwitcher( - duration: kThemeChangeDuration, - child: this.emptyIcon, - ); - - final emptyTitleText = AnimatedDefaultTextStyle( - style: emptyTitleStyle ?? chatThemeData.textTheme.headline, - duration: kThemeChangeDuration, - child: emptyTitle, - ); - - return Column( - mainAxisSize: mainAxisSize, - mainAxisAlignment: mainAxisAlignment, - crossAxisAlignment: crossAxisAlignment, - children: [ - emptyIcon, - emptyTitleText, - ], - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_error_widget.dart b/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_error_widget.dart deleted file mode 100644 index 3776d8023d..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_error_widget.dart +++ /dev/null @@ -1,91 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// A widget that is displayed when a [StreamScrollView] encounters an error -/// while loading the initial items. -class StreamScrollViewErrorWidget extends StatelessWidget { - /// Creates a new instance of the [StreamScrollViewErrorWidget]. - const StreamScrollViewErrorWidget({ - super.key, - this.errorTitle, - this.errorTitleStyle, - this.errorIcon, - this.retryButtonText, - this.retryButtonTextStyle, - required this.onRetryPressed, - this.mainAxisSize = MainAxisSize.max, - this.mainAxisAlignment = MainAxisAlignment.center, - this.crossAxisAlignment = CrossAxisAlignment.center, - }); - - /// The title of the error. - final Widget? errorTitle; - - /// The style of the title. - final TextStyle? errorTitleStyle; - - /// The icon to display when the list shows error. - final Widget? errorIcon; - - /// The text to display in the retry button. - final Widget? retryButtonText; - - /// The style of the retryButtonText. - final TextStyle? retryButtonTextStyle; - - /// The callback to invoke when the user taps on the retry button. - final VoidCallback onRetryPressed; - - /// The main axis size of the error view. - final MainAxisSize mainAxisSize; - - /// The main axis alignment of the error view. - final MainAxisAlignment mainAxisAlignment; - - /// The cross axis alignment of the error view. - final CrossAxisAlignment crossAxisAlignment; - - @override - Widget build(BuildContext context) { - final chatThemeData = StreamChatTheme.of(context); - - final errorIcon = AnimatedSwitcher( - duration: kThemeChangeDuration, - child: this.errorIcon ?? - Icon( - Icons.error_outline_rounded, - size: 148, - color: chatThemeData.colorTheme.disabled, - ), - ); - - final titleText = AnimatedDefaultTextStyle( - style: errorTitleStyle ?? chatThemeData.textTheme.headline, - duration: kThemeChangeDuration, - child: errorTitle ?? const SizedBox(), - ); - - final retryButtonText = AnimatedDefaultTextStyle( - style: errorTitleStyle ?? - chatThemeData.textTheme.headline.copyWith( - color: Colors.white, - ), - duration: kThemeChangeDuration, - child: this.retryButtonText ?? Text(context.translations.retryLabel), - ); - - return Column( - mainAxisSize: mainAxisSize, - mainAxisAlignment: mainAxisAlignment, - crossAxisAlignment: crossAxisAlignment, - children: [ - errorIcon, - titleText, - ElevatedButton( - onPressed: onRetryPressed, - child: retryButtonText, - ), - ], - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_indexed_widget_builder.dart b/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_indexed_widget_builder.dart deleted file mode 100644 index 0305cd1f20..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_indexed_widget_builder.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:flutter/material.dart'; - -/// Signature for a function that creates a widget for a given index, e.g., in a -/// list, grid. -/// -/// Used by [StreamChannelListView], [StreamMessageSearchListView] -/// and [StreamUserListView]. -typedef StreamScrollViewIndexedWidgetBuilder - = Widget Function( - BuildContext context, - List items, - int index, - WidgetType defaultWidget, -); diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_load_more_error.dart b/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_load_more_error.dart deleted file mode 100644 index d0771d60fd..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_load_more_error.dart +++ /dev/null @@ -1,112 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// A tile that is used to display the error indicator when -/// loading more items fails. -class StreamScrollViewLoadMoreError extends StatelessWidget { - /// Creates a new instance of [StreamScrollViewLoadMoreError.list]. - const StreamScrollViewLoadMoreError.list({ - super.key, - this.error, - this.errorStyle, - this.errorIcon, - this.backgroundColor, - required this.onTap, - this.padding = const EdgeInsets.all(16), - this.mainAxisSize = MainAxisSize.max, - this.mainAxisAlignment = MainAxisAlignment.spaceBetween, - this.crossAxisAlignment = CrossAxisAlignment.center, - }) : _isList = true; - - /// Creates a new instance of [StreamScrollViewLoadMoreError.grid]. - const StreamScrollViewLoadMoreError.grid({ - super.key, - this.error, - this.errorStyle, - this.errorIcon, - this.backgroundColor, - required this.onTap, - this.padding = const EdgeInsets.all(16), - this.mainAxisSize = MainAxisSize.max, - this.mainAxisAlignment = MainAxisAlignment.spaceEvenly, - this.crossAxisAlignment = CrossAxisAlignment.center, - }) : _isList = false; - - /// The error message to display. - final Widget? error; - - /// The style of the error message. - final TextStyle? errorStyle; - - /// The icon to display next to the message. - final Widget? errorIcon; - - /// The background color of the error message. - final Color? backgroundColor; - - /// The callback to invoke when the user taps on the error indicator. - final GestureTapCallback onTap; - - /// The amount of space by which to inset the child. - final EdgeInsetsGeometry padding; - - /// The main axis size of the error view. - final MainAxisSize mainAxisSize; - - /// The main axis alignment of the error view. - final MainAxisAlignment mainAxisAlignment; - - /// The cross axis alignment of the error view. - final CrossAxisAlignment crossAxisAlignment; - - final bool _isList; - - @override - Widget build(BuildContext context) { - final theme = StreamChatTheme.of(context); - - final errorText = AnimatedDefaultTextStyle( - style: errorStyle ?? theme.textTheme.body.copyWith(color: Colors.white), - duration: kThemeChangeDuration, - child: error ?? const SizedBox(), - ); - - final errorIcon = AnimatedSwitcher( - duration: kThemeChangeDuration, - child: this.errorIcon ?? - const StreamSvgIcon( - color: Colors.white, - icon: StreamSvgIcons.retry, - ), - ); - - final backgroundColor = this.backgroundColor ?? - // ignore: deprecated_member_use - theme.colorTheme.textLowEmphasis.withOpacity(0.9); - - final children = [errorText, errorIcon]; - - return InkWell( - onTap: onTap, - child: ColoredBox( - color: backgroundColor, - child: Padding( - padding: padding, - child: _isList - ? Row( - mainAxisSize: mainAxisSize, - mainAxisAlignment: mainAxisAlignment, - crossAxisAlignment: crossAxisAlignment, - children: children, - ) - : Column( - mainAxisSize: mainAxisSize, - mainAxisAlignment: mainAxisAlignment, - crossAxisAlignment: crossAxisAlignment, - children: children, - ), - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_load_more_indicator.dart b/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_load_more_indicator.dart deleted file mode 100644 index 7258522590..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_load_more_indicator.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:flutter/material.dart'; - -/// A widget that shows a loading indicator when the user is near the bottom of -/// the list. -class StreamScrollViewLoadMoreIndicator extends StatelessWidget { - /// Creates a new instance of [StreamScrollViewLoadMoreIndicator]. - const StreamScrollViewLoadMoreIndicator({ - super.key, - this.height = 16, - this.width = 16, - }); - - /// The height of the indicator. - final double height; - - /// The width of the indicator. - final double width; - - @override - Widget build(BuildContext context) => SizedBox( - height: height, - width: width, - child: const CircularProgressIndicator.adaptive(), - ); -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_loading_widget.dart b/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_loading_widget.dart deleted file mode 100644 index 958eb80dc4..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/stream_scroll_view_loading_widget.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flutter/material.dart'; - -/// A widget that is displayed while the [StreamScrollView] is loading. -class StreamScrollViewLoadingWidget extends StatelessWidget { - /// Creates a new instance of [StreamScrollViewLoadingWidget]. - const StreamScrollViewLoadingWidget({ - super.key, - this.height = 42, - this.width = 42, - }); - - /// The height of the indicator. - final double height; - - /// The width of the indicator. - final double width; - - @override - Widget build(BuildContext context) => SizedBox( - height: height, - width: width, - child: const CircularProgressIndicator.adaptive(), - ); -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/thread_scroll_view/stream_thread_list_tile.dart b/packages/stream_chat_flutter/lib/src/scroll_view/thread_scroll_view/stream_thread_list_tile.dart deleted file mode 100644 index 4ad89cfbb4..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/thread_scroll_view/stream_thread_list_tile.dart +++ /dev/null @@ -1,253 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/misc/timestamp.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamThreadListTile} -/// A widget that displays a thread in a list. -/// -/// This widget is used in the [ThreadListView] to display a thread. -/// -/// The widget displays the channel name, the message the thread is replying to, -/// the latest reply, and the unread message count. -/// {@endtemplate} -class StreamThreadListTile extends StatelessWidget { - /// {@macro streamThreadListTile} - const StreamThreadListTile({ - super.key, - required this.thread, - this.currentUser, - this.onTap, - this.onLongPress, - }); - - /// The thread to display. - final Thread thread; - - /// The current user. - final User? currentUser; - - /// Called when the user taps this list tile. - final GestureTapCallback? onTap; - - /// Called when the user long-presses on this list tile. - final GestureLongPressCallback? onLongPress; - - @override - Widget build(BuildContext context) { - final theme = StreamThreadListTileTheme.of(context); - - final language = currentUser?.language; - final unreadMessageCount = thread.read - ?.firstWhereOrNull((read) => read.user.id == currentUser?.id) - ?.unreadMessages; - - return InkWell( - onTap: onTap, - onLongPress: onLongPress, - child: Container( - padding: theme.padding, - color: theme.backgroundColor, - child: Column( - spacing: 6, - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (thread.channel case final channel?) - ThreadTitle( - channelName: channel.formatName(currentUser: currentUser), - ), - Row( - children: [ - if (thread.parentMessage case final parentMessage?) - Expanded( - child: ThreadReplyToContent( - language: language, - prefix: context.translations.repliedToLabel, - parentMessage: parentMessage, - ), - ), - if (unreadMessageCount case final count? when count > 0) - ThreadUnreadCount(unreadCount: count), - ], - ), - if (thread.latestReplies.lastOrNull case final latestReply?) - ThreadLatestReply( - language: language, - latestReply: latestReply, - ), - ], - ), - ), - ); - } -} - -/// {@template threadTitle} -/// A widget that displays the channel name. -/// {@endtemplate} -class ThreadTitle extends StatelessWidget { - /// {@macro threadTitle} - const ThreadTitle({ - super.key, - this.channelName, - }); - - /// The channel name to display. - final String? channelName; - - @override - Widget build(BuildContext context) { - final theme = StreamThreadListTileTheme.of(context); - - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.message_outlined, - size: 16, - color: theme.threadChannelNameStyle?.color, - ), - const SizedBox(width: 4), - Flexible( - child: Text( - channelName ?? context.translations.noTitleText, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: theme.threadChannelNameStyle, - ), - ), - ], - ); - } -} - -/// {@template threadReplyToContent} -/// A widget that displays the message the thread is replying to. -/// {@endtemplate} -class ThreadReplyToContent extends StatelessWidget { - /// {@macro threadReplyToContent} - const ThreadReplyToContent({ - super.key, - this.language, - this.prefix = 'replied to:', - required this.parentMessage, - }); - - /// The prefix to display before the message. - /// - /// Defaults to `replied to:`. - final String prefix; - - /// The language of the message. - final String? language; - - /// The message the thread is replying to. - final Message parentMessage; - - @override - Widget build(BuildContext context) { - final theme = StreamThreadListTileTheme.of(context); - - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - prefix, - style: theme.threadReplyToMessageStyle, - ), - const SizedBox(width: 4), - Flexible( - child: StreamMessagePreviewText( - language: language, - message: parentMessage, - textStyle: theme.threadReplyToMessageStyle, - ), - ), - ], - ); - } -} - -/// {@template threadUnreadCount} -/// A widget that displays the unread message count. -/// {@endtemplate} -class ThreadUnreadCount extends StatelessWidget { - /// {@macro threadUnreadCount} - const ThreadUnreadCount({ - super.key, - required this.unreadCount, - }) : assert(unreadCount > 0, 'unreadCount must be greater than 0'); - - /// The number of unread messages. - final int unreadCount; - - @override - Widget build(BuildContext context) { - final theme = StreamThreadListTileTheme.of(context); - - return Badge( - textStyle: theme.threadUnreadMessageCountStyle, - textColor: theme.threadUnreadMessageCountStyle?.color, - backgroundColor: theme.threadUnreadMessageCountBackgroundColor, - label: Text('$unreadCount'), - ); - } -} - -/// {@template threadLatestReply} -/// A widget that displays the latest reply in the thread. -/// {@endtemplate} -class ThreadLatestReply extends StatelessWidget { - /// {@macro threadLatestReply} - const ThreadLatestReply({ - super.key, - this.language, - required this.latestReply, - }); - - /// The language of the message. - final String? language; - - /// The latest reply in the thread. - final Message latestReply; - - @override - Widget build(BuildContext context) { - final theme = StreamThreadListTileTheme.of(context); - - return Row( - spacing: 8, - children: [ - if (latestReply.user case final user?) StreamUserAvatar(user: user), - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - latestReply.user!.name, - style: theme.threadLatestReplyUsernameStyle, - ), - Row( - children: [ - Expanded( - child: StreamMessagePreviewText( - language: language, - message: latestReply, - textStyle: theme.threadLatestReplyMessageStyle, - ), - ), - StreamTimestamp( - date: latestReply.createdAt.toLocal(), - style: theme.threadLatestReplyTimestampStyle, - ), - ], - ), - ], - ), - ), - ], - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/thread_scroll_view/stream_thread_list_view.dart b/packages/stream_chat_flutter/lib/src/scroll_view/thread_scroll_view/stream_thread_list_view.dart deleted file mode 100644 index b8b2d293e3..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/thread_scroll_view/stream_thread_list_view.dart +++ /dev/null @@ -1,377 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_error_widget.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_error.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_indicator.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_loading_widget.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Default separator builder for [StreamThreadListView]. -Widget defaultThreadListViewSeparatorBuilder( - BuildContext context, - List threads, - int index, -) => - const StreamThreadListSeparator(); - -/// Signature for the item builder that creates the children of the -/// [StreamThreadListView]. -typedef StreamThreadListViewIndexedWidgetBuilder - = StreamScrollViewIndexedWidgetBuilder; - -/// {@template streamThreadListView} -/// A [ListView] that shows a list of [Thread]'s. It uses a -/// [StreamThreadListController] to load the threads in paginated form. -/// -/// Example: -/// -/// ```dart -/// StreamThreadListView( -/// controller: controller, -/// onThreadTap: (thread) { -/// // Handle thread tap event -/// }, -/// onThreadLongPress: (thread) { -/// // Handle thread long press event -/// }, -/// ) -/// ``` -/// -/// See also: -/// * [StreamThreadListTile] -/// * [StreamThreadListController] -/// {@endtemplate} -class StreamThreadListView extends StatelessWidget { - /// {@macro streamThreadListView} - const StreamThreadListView({ - super.key, - required this.controller, - this.itemBuilder, - this.separatorBuilder = defaultThreadListViewSeparatorBuilder, - this.emptyBuilder, - this.loadingBuilder, - this.errorBuilder, - this.onThreadTap, - this.onThreadLongPress, - this.loadMoreTriggerIndex = 3, - this.scrollDirection = Axis.vertical, - this.reverse = false, - this.scrollController, - this.primary, - this.physics, - this.shrinkWrap = false, - this.padding, - this.addAutomaticKeepAlives = true, - this.addRepaintBoundaries = true, - this.addSemanticIndexes = true, - this.cacheExtent, - this.dragStartBehavior = DragStartBehavior.start, - this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, - this.restorationId, - this.clipBehavior = Clip.hardEdge, - }); - - /// The [StreamThreadListController] used to control the threads in the list. - final StreamThreadListController controller; - - /// A builder that is called to build items in the [ListView]. - final StreamThreadListViewIndexedWidgetBuilder? itemBuilder; - - /// A builder that is called to build the list separator. - final PagedValueScrollViewIndexedWidgetBuilder separatorBuilder; - - /// A builder that is called to build the empty state of the list. - final WidgetBuilder? emptyBuilder; - - /// A builder that is called to build the loading state of the list. - final WidgetBuilder? loadingBuilder; - - /// A builder that is called to build the error state of the list. - final Widget Function(BuildContext, StreamChatError)? errorBuilder; - - /// Called when the user taps this list tile. - final void Function(Thread)? onThreadTap; - - /// Called when the user long-presses on this list tile. - final void Function(Thread)? onThreadLongPress; - - /// The index to take into account when triggering [controller.loadMore]. - final int loadMoreTriggerIndex; - - /// {@template flutter.widgets.scroll_view.scrollDirection} - /// The axis along which the scroll view scrolls. - /// - /// Defaults to [Axis.vertical]. - /// {@endtemplate} - final Axis scrollDirection; - - /// The amount of space by which to inset the children. - final EdgeInsetsGeometry? padding; - - /// Whether to wrap each child in an [AutomaticKeepAlive]. - /// - /// Typically, children in lazy list are wrapped in [AutomaticKeepAlive] - /// widgets so that children can use [KeepAliveNotification]s to preserve - /// their state when they would otherwise be garbage collected off-screen. - /// - /// This feature (and [addRepaintBoundaries]) must be disabled if the children - /// are going to manually maintain their [KeepAlive] state. It may also be - /// more efficient to disable this feature if it is known ahead of time that - /// none of the children will ever try to keep themselves alive. - /// - /// Defaults to true. - final bool addAutomaticKeepAlives; - - /// Whether to wrap each child in a [RepaintBoundary]. - /// - /// Typically, children in a scrolling container are wrapped in repaint - /// boundaries so that they do not need to be repainted as the list scrolls. - /// If the children are easy to repaint (e.g., solid color blocks or a short - /// snippet of text), it might be more efficient to not add a repaint boundary - /// and simply repaint the children during scrolling. - /// - /// Defaults to true. - final bool addRepaintBoundaries; - - /// Whether to wrap each child in an [IndexedSemantics]. - /// - /// Typically, children in a scrolling container must be annotated with a - /// semantic index in order to generate the correct accessibility - /// announcements. This should only be set to false if the indexes have - /// already been provided by an [IndexedSemantics] widget. - /// - /// Defaults to true. - /// - /// See also: - /// - /// * [IndexedSemantics], for an explanation of how to manually - /// provide semantic indexes. - final bool addSemanticIndexes; - - /// {@template flutter.widgets.scroll_view.reverse} - /// Whether the scroll view scrolls in the reading direction. - /// - /// For example, if [scrollDirection] is [Axis.vertical], then the scroll view - /// scrolls from top to bottom when [reverse] is false and from bottom to top - /// when [reverse] is true. - /// - /// Defaults to false. - /// {@endtemplate} - final bool reverse; - - /// {@template flutter.widgets.scroll_view.controller} - /// An object that can be used to control the position to which this scroll - /// view is scrolled. - /// - /// Must be null if [primary] is true. - /// - /// A [ScrollController] serves several purposes. It can be used to control - /// the initial scroll position (see [ScrollController.initialScrollOffset]). - /// It can be used to control whether the scroll view should automatically - /// save and restore its scroll position in the [PageStorage] (see - /// [ScrollController.keepScrollOffset]). It can be used to read the current - /// scroll position (see [ScrollController.offset]), or change it (see - /// [ScrollController.animateTo]). - /// {@endtemplate} - final ScrollController? scrollController; - - /// {@template flutter.widgets.scroll_view.primary} - /// Whether this is the primary scroll view associated with the parent - /// [PrimaryScrollController]. - /// - /// When this is true, the scroll view is scrollable even if it does not have - /// sufficient content to actually scroll. Otherwise, by default the user can - /// only scroll the view if it has sufficient content. See [physics]. - /// - /// Also when true, the scroll view is used for default [ScrollAction]s. If a - /// ScrollAction is not handled by an otherwise focused part of the - /// application, the ScrollAction will be evaluated using this scroll view, - /// for example, when executing [Shortcuts] key events like page up and down. - /// - /// On iOS, this also identifies the scroll view that will scroll to top in - /// response to a tap in the status bar. - /// {@endtemplate} - /// - /// Defaults to true when [scrollController] is null. - final bool? primary; - - /// {@template flutter.widgets.scroll_view.shrinkWrap} - /// Whether the extent of the scroll view in the [scrollDirection] should be - /// determined by the contents being viewed. - /// - /// If the scroll view does not shrink wrap, then the scroll view will expand - /// to the maximum allowed size in the [scrollDirection]. If the scroll view - /// has unbounded constraints in the [scrollDirection], then [shrinkWrap] must - /// be true. - /// - /// Shrink wrapping the content of the scroll view is significantly more - /// expensive than expanding to the maximum allowed size because the content - /// can expand and contract during scrolling, which means the size of the - /// scroll view needs to be recomputed whenever the scroll position changes. - /// - /// Defaults to false. - /// {@endtemplate} - final bool shrinkWrap; - - /// {@template flutter.widgets.scroll_view.physics} - /// How the scroll view should respond to user input. - /// - /// For example, determines how the scroll view continues to animate after the - /// user stops dragging the scroll view. - /// - /// Defaults to matching platform conventions. Furthermore, if [primary] is - /// false, then the user cannot scroll if there is insufficient content to - /// scroll, while if [primary] is true, they can always attempt to scroll. - /// - /// To force the scroll view to always be scrollable even if there is - /// insufficient content, as if [primary] was true but without necessarily - /// setting it to true, provide an [AlwaysScrollableScrollPhysics] physics - /// object, as in: - /// - /// ```dart - /// physics: const AlwaysScrollableScrollPhysics(), - /// ``` - /// - /// To force the scroll view to use the default platform conventions and not - /// be scrollable if there is insufficient content, regardless of the value of - /// [primary], provide an explicit [ScrollPhysics] object, as in: - /// - /// ```dart - /// physics: const ScrollPhysics(), - /// ``` - /// - /// The physics can be changed dynamically (by providing a new object in a - /// subsequent build), but new physics will only take effect if the _class_ of - /// the provided object changes. Merely constructing a new instance with a - /// different configuration is insufficient to cause the physics to be - /// reapplied. (This is because the final object used is generated - /// dynamically, which can be relatively expensive, and it would be - /// inefficient to speculatively create this object each frame to see if the - /// physics should be updated.) - /// {@endtemplate} - /// - /// If an explicit [ScrollBehavior] is provided to [scrollBehavior], the - /// [ScrollPhysics] provided by that behavior will take precedence after - /// [physics]. - final ScrollPhysics? physics; - - /// {@macro flutter.rendering.RenderViewportBase.cacheExtent} - final double? cacheExtent; - - /// {@macro flutter.widgets.scrollable.dragStartBehavior} - final DragStartBehavior dragStartBehavior; - - /// {@template flutter.widgets.scroll_view.keyboardDismissBehavior} - /// [ScrollViewKeyboardDismissBehavior] the defines how this [ScrollView] will - /// dismiss the keyboard automatically. - /// {@endtemplate} - final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior; - - /// {@macro flutter.widgets.scrollable.restorationId} - final String? restorationId; - - /// {@macro flutter.material.Material.clipBehavior} - /// - /// Defaults to [Clip.hardEdge]. - final Clip clipBehavior; - - @override - Widget build(BuildContext context) { - return PagedValueListView( - scrollDirection: scrollDirection, - padding: padding, - physics: physics, - reverse: reverse, - controller: controller, - scrollController: scrollController, - primary: primary, - shrinkWrap: shrinkWrap, - addAutomaticKeepAlives: addAutomaticKeepAlives, - addRepaintBoundaries: addRepaintBoundaries, - addSemanticIndexes: addSemanticIndexes, - keyboardDismissBehavior: keyboardDismissBehavior, - restorationId: restorationId, - dragStartBehavior: dragStartBehavior, - cacheExtent: cacheExtent, - clipBehavior: clipBehavior, - loadMoreTriggerIndex: loadMoreTriggerIndex, - separatorBuilder: separatorBuilder, - itemBuilder: (context, threads, index) { - final thread = threads[index]; - final currentUser = StreamChat.of(context).currentUser; - final onTap = onThreadTap; - final onLongPress = onThreadLongPress; - - final tile = StreamThreadListTile( - thread: thread, - currentUser: currentUser, - onTap: onTap == null ? null : () => onTap(thread), - onLongPress: onLongPress == null ? null : () => onLongPress(thread), - ); - - return itemBuilder?.call(context, threads, index, tile) ?? tile; - }, - emptyBuilder: (context) { - final chatThemeData = StreamChatTheme.of(context); - return emptyBuilder?.call(context) ?? - Center( - child: Padding( - padding: const EdgeInsets.all(8), - child: StreamScrollViewEmptyWidget( - emptyIcon: StreamSvgIcon( - size: 148, - icon: StreamSvgIcons.threadReply, - color: chatThemeData.colorTheme.disabled, - ), - emptyTitle: Text( - context.translations.emptyMessagesText, - style: chatThemeData.textTheme.headline, - ), - ), - ), - ); - }, - loadMoreErrorBuilder: (context, error) => - StreamScrollViewLoadMoreError.list( - onTap: controller.retry, - error: Text(context.translations.loadingMessagesError), - ), - loadMoreIndicatorBuilder: (context) => const Center( - child: Padding( - padding: EdgeInsets.all(16), - child: StreamScrollViewLoadMoreIndicator(), - ), - ), - loadingBuilder: (context) => - loadingBuilder?.call(context) ?? - const Center( - child: StreamScrollViewLoadingWidget(), - ), - errorBuilder: (context, error) => - errorBuilder?.call(context, error) ?? - Center( - child: StreamScrollViewErrorWidget( - errorTitle: Text(context.translations.loadingMessagesError), - onRetryPressed: controller.refresh, - ), - ), - ); - } -} - -/// A widget that is used to display a separator between -/// [StreamThreadListTile] items. -class StreamThreadListSeparator extends StatelessWidget { - /// Creates a new instance of [StreamThreadListSeparator]. - const StreamThreadListSeparator({super.key}); - - @override - Widget build(BuildContext context) { - final effect = StreamChatTheme.of(context).colorTheme.borderBottom; - return Container( - height: 1, - // ignore: deprecated_member_use - color: effect.color!.withOpacity(effect.alpha ?? 1.0), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/thread_scroll_view/stream_unread_threads_banner.dart b/packages/stream_chat_flutter/lib/src/scroll_view/thread_scroll_view/stream_unread_threads_banner.dart deleted file mode 100644 index 3f2854be7b..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/thread_scroll_view/stream_unread_threads_banner.dart +++ /dev/null @@ -1,85 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/icons/stream_svg_icon.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter/src/utils/extensions.dart'; - -/// {@template unreadThreadsBanner} -/// A widget that shows a banner with the number of unread threads. -/// -/// This widget can be used to show a banner with the number of unread threads -/// on the top of the [ThreadListView]. -/// {@endtemplate} -class StreamUnreadThreadsBanner extends StatelessWidget { - /// {@macro unreadThreadsBanner} - const StreamUnreadThreadsBanner({ - super.key, - required this.unreadThreads, - this.onTap, - this.minHeight = 52, - this.margin = const EdgeInsets.symmetric(horizontal: 8, vertical: 6), - this.padding = const EdgeInsets.symmetric(horizontal: 16), - }); - - /// The set of all the unread threads. - final Set unreadThreads; - - /// Optional callback to handle tap events. - final VoidCallback? onTap; - - /// The minimum height of the banner. - /// - /// Defaults to 52. - final double minHeight; - - /// The margin applied to the banner. - /// - /// Defaults to `EdgeInsets.symmetric(horizontal: 8, vertical: 6)`. - final EdgeInsetsGeometry? margin; - - /// The padding applied to the banner. - /// - /// Defaults to `EdgeInsets.symmetric(horizontal: 16)`. - final EdgeInsetsGeometry padding; - - @override - Widget build(BuildContext context) { - if (unreadThreads.isEmpty) { - return const SizedBox.shrink(); - } - - final theme = StreamChatTheme.of(context); - - return GestureDetector( - onTap: onTap, - child: Container( - margin: margin, - padding: padding, - constraints: BoxConstraints(minHeight: minHeight), - decoration: BoxDecoration( - color: theme.colorTheme.textHighEmphasis, - borderRadius: BorderRadius.circular(16), - ), - child: Row( - children: [ - Expanded( - child: Text( - context.translations.newThreadsLabel( - count: unreadThreads.length, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: theme.textTheme.headline.copyWith( - color: theme.colorTheme.barsBg, - ), - ), - ), - StreamSvgIcon( - icon: StreamSvgIcons.reload, - color: theme.colorTheme.barsBg, - ), - ], - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_grid_tile.dart b/packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_grid_tile.dart deleted file mode 100644 index 4d512f05ea..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_grid_tile.dart +++ /dev/null @@ -1,102 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// A widget that displays a user. -/// -/// This widget is intended to be used as a Tile in [StreamUserGridView] -/// -/// It shows the user's avatar and name. -/// -/// See also: -/// * [StreamUserGridView] -/// * [StreamUserAvatar] -class StreamUserGridTile extends StatelessWidget { - /// Creates a new instance of [StreamUserGridTile] widget. - const StreamUserGridTile({ - super.key, - required this.user, - this.child, - this.footer, - this.onTap, - this.onLongPress, - }); - - /// The user to display. - final User user; - - /// The widget to display in the body of the tile. - final Widget? child; - - /// The widget to display in the footer of the tile. - final Widget? footer; - - /// Called when the user taps this grid tile. - final GestureTapCallback? onTap; - - /// Called when the user long-presses on this grid tile. - final GestureLongPressCallback? onLongPress; - - /// Creates a copy of this tile but with the given fields replaced with - /// the new values. - StreamUserGridTile copyWith({ - Key? key, - User? user, - Widget? child, - Widget? footer, - GestureTapCallback? onTap, - GestureLongPressCallback? onLongPress, - }) => - StreamUserGridTile( - key: key ?? this.key, - user: user ?? this.user, - footer: footer ?? this.footer, - onTap: onTap ?? this.onTap, - onLongPress: onLongPress ?? this.onLongPress, - child: child ?? this.child, - ); - - @override - Widget build(BuildContext context) { - final child = this.child ?? - StreamUserAvatar( - user: user, - borderRadius: BorderRadius.circular(32), - constraints: const BoxConstraints.tightFor( - height: 64, - width: 64, - ), - onlineIndicatorConstraints: const BoxConstraints.tightFor( - height: 12, - width: 12, - ), - ); - - final footer = this.footer ?? - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: Text( - user.name, - textAlign: TextAlign.center, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 12, - ), - ), - ); - - return InkWell( - onTap: onTap, - onLongPress: onLongPress, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - child, - const SizedBox(height: 4), - footer, - ], - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_grid_view.dart b/packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_grid_view.dart deleted file mode 100644 index 5aac833955..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_grid_view.dart +++ /dev/null @@ -1,394 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_error_widget.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_error.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_indicator.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_loading_widget.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Default grid delegate for [StreamUserGridView]. -const defaultUserGridViewDelegate = - SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 4); - -/// Signature for the item builder that creates the children of the -/// [StreamUserGridView]. -typedef StreamUserGridViewIndexedWidgetBuilder - = StreamScrollViewIndexedWidgetBuilder; - -/// A [GridView] that shows a grid of [User]s, -/// it uses [StreamUserGridTile] as a default item. -/// -/// Example: -/// -/// ```dart -/// StreamUserGridView( -/// controller: controller, -/// onUserTap: (user) { -/// // Handle user tap event -/// }, -/// onUserLongPress: (user) { -/// // Handle user long press event -/// }, -/// ) -/// ``` -/// -/// See also: -/// * [StreamUserListTile] -/// * [StreamUserListController] -class StreamUserGridView extends StatelessWidget { - /// Creates a new instance of [StreamUserGridView]. - const StreamUserGridView({ - super.key, - required this.controller, - this.gridDelegate = defaultUserGridViewDelegate, - this.itemBuilder, - this.emptyBuilder, - this.loadMoreErrorBuilder, - this.loadMoreIndicatorBuilder, - this.loadingBuilder, - this.errorBuilder, - this.onUserTap, - this.onUserLongPress, - this.loadMoreTriggerIndex = 3, - this.scrollDirection = Axis.vertical, - this.reverse = false, - this.scrollController, - this.primary, - this.physics, - this.shrinkWrap = false, - this.padding, - this.addAutomaticKeepAlives = true, - this.addRepaintBoundaries = true, - this.addSemanticIndexes = true, - this.cacheExtent, - this.semanticChildCount, - this.dragStartBehavior = DragStartBehavior.start, - this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, - this.restorationId, - this.clipBehavior = Clip.hardEdge, - }); - - /// The [StreamUserListController] used to control the grid of users. - final StreamUserListController controller; - - /// A delegate that controls the layout of the children within - /// the [PagedValueGridView]. - final SliverGridDelegate gridDelegate; - - /// A builder that is called to build items in the [PagedValueGridView]. - final StreamUserGridViewIndexedWidgetBuilder? itemBuilder; - - /// A builder that is called to build the empty state of the grid. - final WidgetBuilder? emptyBuilder; - - /// A builder that is called to build the load more error state of the grid. - final PagedValueScrollViewLoadMoreErrorBuilder? loadMoreErrorBuilder; - - /// A builder that is called to build the load more indicator of the grid. - final WidgetBuilder? loadMoreIndicatorBuilder; - - /// A builder that is called to build the loading state of the grid. - final WidgetBuilder? loadingBuilder; - - /// A builder that is called to build the error state of the grid. - final Widget Function(BuildContext, StreamChatError)? errorBuilder; - - /// Called when the user taps this grid tile. - final void Function(User)? onUserTap; - - /// Called when the user long-presses on this grid tile. - final void Function(User)? onUserLongPress; - - /// The index to take into account when triggering [controller.loadMore]. - final int loadMoreTriggerIndex; - - /// {@template flutter.widgets.scroll_view.scrollDirection} - /// The axis along which the scroll view scrolls. - /// - /// Defaults to [Axis.vertical]. - /// {@endtemplate} - final Axis scrollDirection; - - /// {@template flutter.widgets.scroll_view.reverse} - /// Whether the scroll view scrolls in the reading direction. - /// - /// For example, if the reading direction is left-to-right and - /// [scrollDirection] is [Axis.horizontal], then the scroll view scrolls from - /// left to right when [reverse] is false and from right to left when - /// [reverse] is true. - /// - /// Similarly, if [scrollDirection] is [Axis.vertical], then the scroll view - /// scrolls from top to bottom when [reverse] is false and from bottom to top - /// when [reverse] is true. - /// - /// Defaults to false. - /// {@endtemplate} - final bool reverse; - - /// {@template flutter.widgets.scroll_view.controller} - /// An object that can be used to control the position to which this scroll - /// view is scrolled. - /// - /// Must be null if [primary] is true. - /// - /// A [ScrollController] serves several purposes. It can be used to control - /// the initial scroll position (see [ScrollController.initialScrollOffset]). - /// It can be used to control whether the scroll view should automatically - /// save and restore its scroll position in the [PageStorage] (see - /// [ScrollController.keepScrollOffset]). It can be used to read the current - /// scroll position (see [ScrollController.offset]), or change it (see - /// [ScrollController.animateTo]). - /// {@endtemplate} - final ScrollController? scrollController; - - /// {@template flutter.widgets.scroll_view.primary} - /// Whether this is the primary scroll view associated with the parent - /// [PrimaryScrollController]. - /// - /// When this is true, the scroll view is scrollable even if it does not have - /// sufficient content to actually scroll. Otherwise, by default the user can - /// only scroll the view if it has sufficient content. See [physics]. - /// - /// Also when true, the scroll view is used for default [ScrollAction]s. If a - /// ScrollAction is not handled by - /// an otherwise focused part of the application, - /// the ScrollAction will be evaluated using this scroll view, for example, - /// when executing [Shortcuts] key events like page up and down. - /// - /// On iOS, this also identifies the scroll view that will scroll to top in - /// response to a tap in the status bar. - /// {@endtemplate} - /// - /// Defaults to true when [scrollDirection] is [Axis.vertical] and - /// [controller] is null. - final bool? primary; - - /// {@template flutter.widgets.scroll_view.physics} - /// How the scroll view should respond to user input. - /// - /// For example, determines how the scroll view continues to animate after the - /// user stops dragging the scroll view. - /// - /// Defaults to matching platform conventions. Furthermore, if [primary] is - /// false, then the user cannot scroll if there is insufficient content to - /// scroll, while if [primary] is true, they can always attempt to scroll. - /// - /// To force the scroll view to always be scrollable even if there is - /// insufficient content, as if [primary] was true but without necessarily - /// setting it to true, provide an [AlwaysScrollableScrollPhysics] physics - /// object, as in: - /// - /// ```dart - /// physics: const AlwaysScrollableScrollPhysics(), - /// ``` - /// - /// To force the scroll view to use the default platform conventions and not - /// be scrollable if there is insufficient content, regardless of the value of - /// [primary], provide an explicit [ScrollPhysics] object, as in: - /// - /// ```dart - /// physics: const ScrollPhysics(), - /// ``` - /// - /// The physics can be changed dynamically (by providing a new object in a - /// subsequent build), but new physics will only take effect if the _class_ of - /// the provided object changes. Merely constructing a new instance with a - /// different configuration is insufficient to cause the physics to be - /// reapplied. (This is because the final object used is generated - /// dynamically, which can be relatively expensive, and it would be - /// inefficient to speculatively create this object each frame to see if the - /// physics should be updated.) - /// {@endtemplate} - /// - /// If an explicit [ScrollBehavior] is provided to [scrollBehavior], the - /// [ScrollPhysics] provided by that behavior will take precedence after - /// [physics]. - final ScrollPhysics? physics; - - /// {@template flutter.widgets.scroll_view.shrinkWrap} - /// Whether the extent of the scroll view in the [scrollDirection] should be - /// determined by the contents being viewed. - /// - /// If the scroll view does not shrink wrap, then the scroll view will expand - /// to the maximum allowed size in the [scrollDirection]. If the scroll view - /// has unbounded constraints in the [scrollDirection], then [shrinkWrap] must - /// be true. - /// - /// Shrink wrapping the content of the scroll view is significantly more - /// expensive than expanding to the maximum allowed size because the content - /// can expand and contract during scrolling, which means the size of the - /// scroll view needs to be recomputed whenever the scroll position changes. - /// - /// Defaults to false. - /// {@endtemplate} - final bool shrinkWrap; - - /// The amount of space by which to inset the children. - final EdgeInsetsGeometry? padding; - - /// Whether to wrap each child in an [AutomaticKeepAlive]. - /// - /// Typically, children in lazy list are wrapped in [AutomaticKeepAlive] - /// widgets so that children can use [KeepAliveNotification]s to preserve - /// their state when they would otherwise be garbage collected off-screen. - /// - /// This feature (and [addRepaintBoundaries]) must be disabled if the children - /// are going to manually maintain their [KeepAlive] state. It may also be - /// more efficient to disable this feature if it is known ahead of time that - /// none of the children will ever try to keep themselves alive. - /// - /// Defaults to true. - final bool addAutomaticKeepAlives; - - /// Whether to wrap each child in a [RepaintBoundary]. - /// - /// Typically, children in a scrolling container are wrapped in repaint - /// boundaries so that they do not need to be repainted as the list scrolls. - /// If the children are easy to repaint (e.g., solid color blocks or a short - /// snippet of text), it might be more efficient to not add a repaint boundary - /// and simply repaint the children during scrolling. - /// - /// Defaults to true. - final bool addRepaintBoundaries; - - /// Whether to wrap each child in an [IndexedSemantics]. - /// - /// Typically, children in a scrolling container must be annotated with a - /// semantic index in order to generate the correct accessibility - /// announcements. This should only be set to false if the indexes have - /// already been provided by an [IndexedSemantics] widget. - /// - /// Defaults to true. - /// - /// See also: - /// - /// * [IndexedSemantics], for an explanation of how to manually - /// provide semantic indexes. - final bool addSemanticIndexes; - - /// {@macro flutter.rendering.RenderViewportBase.cacheExtent} - final double? cacheExtent; - - /// The number of children that will contribute semantic information. - /// - /// Some subtypes of [ScrollView] can infer this value automatically. For - /// example [ListView] will use the number of widgets in the child list, - /// while the [ListView.separated] constructor will use half that amount. - /// - /// For [CustomScrollView] and other types which do not receive a builder - /// or list of widgets, the child count must be explicitly provided. If the - /// number is unknown or unbounded this should be left unset or set to null. - /// - /// See also: - /// - /// * [SemanticsConfiguration.scrollChildCount], - /// the corresponding semantics property. - final int? semanticChildCount; - - /// {@macro flutter.widgets.scrollable.dragStartBehavior} - final DragStartBehavior dragStartBehavior; - - /// {@template flutter.widgets.scroll_view.keyboardDismissBehavior} - /// [ScrollViewKeyboardDismissBehavior] the defines how this [ScrollView] will - /// dismiss the keyboard automatically. - /// {@endtemplate} - final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior; - - /// {@macro flutter.widgets.scrollable.restorationId} - final String? restorationId; - - /// {@macro flutter.material.Material.clipBehavior} - /// - /// Defaults to [Clip.hardEdge]. - final Clip clipBehavior; - - @override - Widget build(BuildContext context) { - return PagedValueGridView( - scrollDirection: scrollDirection, - reverse: reverse, - controller: controller, - primary: primary, - physics: physics, - shrinkWrap: shrinkWrap, - padding: padding, - scrollController: scrollController, - addAutomaticKeepAlives: addAutomaticKeepAlives, - addRepaintBoundaries: addRepaintBoundaries, - addSemanticIndexes: addSemanticIndexes, - cacheExtent: cacheExtent, - semanticChildCount: semanticChildCount, - dragStartBehavior: dragStartBehavior, - keyboardDismissBehavior: keyboardDismissBehavior, - restorationId: restorationId, - clipBehavior: clipBehavior, - gridDelegate: gridDelegate, - itemBuilder: (context, users, index) { - final user = users[index]; - final onTap = onUserTap; - final onLongPress = onUserLongPress; - - final streamUserGridTile = StreamUserGridTile( - user: user, - onTap: onTap == null ? null : () => onTap(user), - onLongPress: onLongPress == null ? null : () => onLongPress(user), - ); - - return itemBuilder?.call( - context, - users, - index, - streamUserGridTile, - ) ?? - streamUserGridTile; - }, - emptyBuilder: (context) { - final chatThemeData = StreamChatTheme.of(context); - return emptyBuilder?.call(context) ?? - Center( - child: Padding( - padding: const EdgeInsets.all(8), - child: StreamScrollViewEmptyWidget( - emptyIcon: StreamSvgIcon( - size: 148, - icon: StreamSvgIcons.user, - color: chatThemeData.colorTheme.disabled, - ), - emptyTitle: Text( - context.translations.noUsersLabel, - style: chatThemeData.textTheme.headline, - ), - ), - ), - ); - }, - loadMoreErrorBuilder: (context, error) => - StreamScrollViewLoadMoreError.grid( - onTap: controller.retry, - error: Text( - context.translations.loadingUsersError, - textAlign: TextAlign.center, - ), - ), - loadMoreIndicatorBuilder: (context) => const Center( - child: Padding( - padding: EdgeInsets.all(16), - child: StreamScrollViewLoadMoreIndicator(), - ), - ), - loadingBuilder: (context) => - loadingBuilder?.call(context) ?? - const Center( - child: StreamScrollViewLoadingWidget(), - ), - errorBuilder: (context, error) => - errorBuilder?.call(context, error) ?? - Center( - child: StreamScrollViewErrorWidget( - errorTitle: Text(context.translations.loadingUsersError), - onRetryPressed: controller.refresh, - ), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_list_tile.dart b/packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_list_tile.dart deleted file mode 100644 index 32129c0110..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_list_tile.dart +++ /dev/null @@ -1,194 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// A widget that displays a user. -/// -/// This widget is intended to be used as a Tile in [StreamUserListView] -/// -/// It shows the user's avatar, name and last message. -/// -/// See also: -/// * [StreamUserListView] -/// * [StreamUserAvatar] -class StreamUserListTile extends StatelessWidget { - /// Creates a new instance of [StreamUserListTile]. - const StreamUserListTile({ - super.key, - required this.user, - this.leading, - this.title, - this.subtitle, - this.selected = false, - this.selectedWidget, - this.onTap, - this.onLongPress, - this.tileColor, - this.visualDensity = VisualDensity.compact, - this.contentPadding = const EdgeInsets.symmetric(horizontal: 8), - }); - - /// The user to display. - final User user; - - /// A widget to display before the title. - final Widget? leading; - - /// The primary content of the list tile. - final Widget? title; - - /// Additional content displayed below the title. - final Widget? subtitle; - - /// A widget to display at the end of tile. - final Widget? selectedWidget; - - /// If this tile is also [enabled] then icons and text are rendered with the - /// same color. - /// - /// By default the selected color is the theme's primary color. The selected - /// color can be overridden with a [ListTileTheme]. - /// - /// {@tool dartpad} - /// Here is an example of using a [StatefulWidget] to keep track of the - /// selected index, and using that to set the `selected` property on the - /// corresponding [ListTile]. - /// - /// ** See code in examples/api/lib/material/list_tile/list_tile.selected.0.dart ** - /// {@end-tool} - final bool selected; - - /// Called when the user taps this list tile. - final GestureTapCallback? onTap; - - /// Called when the user long-presses on this list tile. - final GestureLongPressCallback? onLongPress; - - /// {@template flutter.material.ListTile.tileColor} - /// Defines the background color of `ListTile`. - /// - /// When the value is null, - /// the `tileColor` is set to [ListTileTheme.tileColor] - /// if it's not null and to [Colors.transparent] if it's null. - /// {@endtemplate} - final Color? tileColor; - - /// Defines how compact the list tile's layout will be. - /// - /// {@macro flutter.material.themedata.visualDensity} - /// - /// See also: - /// - /// * [ThemeData.visualDensity], which specifies the [visualDensity] for all - /// widgets within a [Theme]. - final VisualDensity visualDensity; - - /// The tile's internal padding. - /// - /// Insets a [ListTile]'s contents: its [leading], [title], [subtitle], - /// and [trailing] widgets. - /// - /// If null, `EdgeInsets.symmetric(horizontal: 16.0)` is used. - final EdgeInsetsGeometry contentPadding; - - /// Creates a copy of this tile but with the given fields replaced with - /// the new values. - StreamUserListTile copyWith({ - Key? key, - User? user, - Widget? leading, - Widget? title, - Widget? subtitle, - Widget? selectedWidget, - bool? selected, - GestureTapCallback? onTap, - GestureLongPressCallback? onLongPress, - Color? tileColor, - VisualDensity? visualDensity, - EdgeInsetsGeometry? contentPadding, - }) => - StreamUserListTile( - key: key ?? this.key, - user: user ?? this.user, - leading: leading ?? this.leading, - title: title ?? this.title, - subtitle: subtitle ?? this.subtitle, - selectedWidget: selectedWidget ?? this.selectedWidget, - selected: selected ?? this.selected, - onTap: onTap ?? this.onTap, - onLongPress: onLongPress ?? this.onLongPress, - tileColor: tileColor ?? this.tileColor, - visualDensity: visualDensity ?? this.visualDensity, - contentPadding: contentPadding ?? this.contentPadding, - ); - - @override - Widget build(BuildContext context) { - final chatThemeData = StreamChatTheme.of(context); - - final leading = this.leading ?? - StreamUserAvatar( - user: user, - constraints: const BoxConstraints.tightFor( - height: 40, - width: 40, - ), - ); - - final title = this.title ?? - Text( - user.name, - style: chatThemeData.textTheme.bodyBold, - ); - - final subtitle = this.subtitle ?? - UserLastActive( - user: user, - ); - - final selectedWidget = this.selectedWidget ?? - StreamSvgIcon( - icon: StreamSvgIcons.checkSend, - color: chatThemeData.colorTheme.accentPrimary, - ); - - return ListTile( - onTap: onTap, - onLongPress: onLongPress, - leading: leading, - trailing: selected ? selectedWidget : null, - title: title, - subtitle: subtitle, - tileColor: tileColor, - visualDensity: visualDensity, - contentPadding: contentPadding, - ); - } -} - -/// A widget that displays a user's last active time. -class UserLastActive extends StatelessWidget { - /// Creates a new instance of the [UserLastActive] widget. - const UserLastActive({ - super.key, - required this.user, - }); - - /// The user whose last active time is displayed. - final User user; - - @override - Widget build(BuildContext context) { - final chatTheme = StreamChatTheme.of(context); - final lastActive = user.lastActive ?? DateTime.now(); - return Text( - user.online - ? context.translations.userOnlineText - : '${context.translations.userLastOnlineText} ' - '${Jiffy.parseFromDateTime(lastActive).fromNow()}', - style: chatTheme.textTheme.footnote.copyWith( - // ignore: deprecated_member_use - color: chatTheme.colorTheme.textHighEmphasis.withOpacity(0.5), - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_list_view.dart b/packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_list_view.dart deleted file mode 100644 index df2530aad0..0000000000 --- a/packages/stream_chat_flutter/lib/src/scroll_view/user_scroll_view/stream_user_list_view.dart +++ /dev/null @@ -1,380 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_error_widget.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_error.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_load_more_indicator.dart'; -import 'package:stream_chat_flutter/src/scroll_view/stream_scroll_view_loading_widget.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Default separator builder for [StreamUserListView]. -Widget defaultUserListViewSeparatorBuilder( - BuildContext context, - List users, - int index, -) => - const StreamUserListSeparator(); - -/// Signature for the item builder that creates the children of the -/// [StreamUserListView]. -typedef StreamUserListViewIndexedWidgetBuilder - = StreamScrollViewIndexedWidgetBuilder; - -/// A [ListView] that shows a list of [User]s, -/// it uses [StreamUserListTile] as a default item. -/// -/// This is the new version of [UserListView] that uses -/// [StreamUserListController]. -/// -/// Example: -/// -/// ```dart -/// StreamUserListView( -/// controller: controller, -/// onUserTap: (user) { -/// // Handle user tap event -/// }, -/// onUserLongPress: (user) { -/// // Handle user long press event -/// }, -/// ) -/// ``` -/// -/// See also: -/// * [StreamUserListTile] -/// * [StreamUserListController] -class StreamUserListView extends StatelessWidget { - /// Creates a new instance of [StreamUserListView]. - const StreamUserListView({ - super.key, - required this.controller, - this.itemBuilder, - this.separatorBuilder = defaultUserListViewSeparatorBuilder, - this.emptyBuilder, - this.loadingBuilder, - this.errorBuilder, - this.onUserTap, - this.onUserLongPress, - this.loadMoreTriggerIndex = 3, - this.scrollDirection = Axis.vertical, - this.reverse = false, - this.scrollController, - this.primary, - this.physics, - this.shrinkWrap = false, - this.padding, - this.addAutomaticKeepAlives = true, - this.addRepaintBoundaries = true, - this.addSemanticIndexes = true, - this.cacheExtent, - this.dragStartBehavior = DragStartBehavior.start, - this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, - this.restorationId, - this.clipBehavior = Clip.hardEdge, - }); - - /// The [StreamUserListController] used to control the list of users. - final StreamUserListController controller; - - /// A builder that is called to build items in the [ListView]. - final StreamUserListViewIndexedWidgetBuilder? itemBuilder; - - /// A builder that is called to build the list separator. - final PagedValueScrollViewIndexedWidgetBuilder separatorBuilder; - - /// A builder that is called to build the empty state of the list. - final WidgetBuilder? emptyBuilder; - - /// A builder that is called to build the loading state of the list. - final WidgetBuilder? loadingBuilder; - - /// A builder that is called to build the error state of the list. - final Widget Function(BuildContext, StreamChatError)? errorBuilder; - - /// Called when the user taps this list tile. - final void Function(User)? onUserTap; - - /// Called when the user long-presses on this list tile. - final void Function(User)? onUserLongPress; - - /// The index to take into account when triggering [controller.loadMore]. - final int loadMoreTriggerIndex; - - /// {@template flutter.widgets.scroll_view.scrollDirection} - /// The axis along which the scroll view scrolls. - /// - /// Defaults to [Axis.vertical]. - /// {@endtemplate} - final Axis scrollDirection; - - /// The amount of space by which to inset the children. - final EdgeInsetsGeometry? padding; - - /// Whether to wrap each child in an [AutomaticKeepAlive]. - /// - /// Typically, children in lazy list are wrapped in [AutomaticKeepAlive] - /// widgets so that children can use [KeepAliveNotification]s to preserve - /// their state when they would otherwise be garbage collected off-screen. - /// - /// This feature (and [addRepaintBoundaries]) must be disabled if the children - /// are going to manually maintain their [KeepAlive] state. It may also be - /// more efficient to disable this feature if it is known ahead of time that - /// none of the children will ever try to keep themselves alive. - /// - /// Defaults to true. - final bool addAutomaticKeepAlives; - - /// Whether to wrap each child in a [RepaintBoundary]. - /// - /// Typically, children in a scrolling container are wrapped in repaint - /// boundaries so that they do not need to be repainted as the list scrolls. - /// If the children are easy to repaint (e.g., solid color blocks or a short - /// snippet of text), it might be more efficient to not add a repaint boundary - /// and simply repaint the children during scrolling. - /// - /// Defaults to true. - final bool addRepaintBoundaries; - - /// Whether to wrap each child in an [IndexedSemantics]. - /// - /// Typically, children in a scrolling container must be annotated with a - /// semantic index in order to generate the correct accessibility - /// announcements. This should only be set to false if the indexes have - /// already been provided by an [IndexedSemantics] widget. - /// - /// Defaults to true. - /// - /// See also: - /// - /// * [IndexedSemantics], for an explanation of how to manually - /// provide semantic indexes. - final bool addSemanticIndexes; - - /// {@template flutter.widgets.scroll_view.reverse} - /// Whether the scroll view scrolls in the reading direction. - /// - /// For example, if [scrollDirection] is [Axis.vertical], then the scroll view - /// scrolls from top to bottom when [reverse] is false and from bottom to top - /// when [reverse] is true. - /// - /// Defaults to false. - /// {@endtemplate} - final bool reverse; - - /// {@template flutter.widgets.scroll_view.controller} - /// An object that can be used to control the position to which this scroll - /// view is scrolled. - /// - /// Must be null if [primary] is true. - /// - /// A [ScrollController] serves several purposes. It can be used to control - /// the initial scroll position (see [ScrollController.initialScrollOffset]). - /// It can be used to control whether the scroll view should automatically - /// save and restore its scroll position in the [PageStorage] (see - /// [ScrollController.keepScrollOffset]). It can be used to read the current - /// scroll position (see [ScrollController.offset]), or change it (see - /// [ScrollController.animateTo]). - /// {@endtemplate} - final ScrollController? scrollController; - - /// {@template flutter.widgets.scroll_view.primary} - /// Whether this is the primary scroll view associated with the parent - /// [PrimaryScrollController]. - /// - /// When this is true, the scroll view is scrollable even if it does not have - /// sufficient content to actually scroll. Otherwise, by default the user can - /// only scroll the view if it has sufficient content. See [physics]. - /// - /// Also when true, the scroll view is used for default [ScrollAction]s. If a - /// ScrollAction is not handled by an otherwise focused part of the - /// application, the ScrollAction will be evaluated using this scroll view, - /// for example, when executing [Shortcuts] key events like page up and down. - /// - /// On iOS, this also identifies the scroll view that will scroll to top in - /// response to a tap in the status bar. - /// {@endtemplate} - /// - /// Defaults to true when [scrollController] is null. - final bool? primary; - - /// {@template flutter.widgets.scroll_view.shrinkWrap} - /// Whether the extent of the scroll view in the [scrollDirection] should be - /// determined by the contents being viewed. - /// - /// If the scroll view does not shrink wrap, then the scroll view will expand - /// to the maximum allowed size in the [scrollDirection]. If the scroll view - /// has unbounded constraints in the [scrollDirection], then [shrinkWrap] must - /// be true. - /// - /// Shrink wrapping the content of the scroll view is significantly more - /// expensive than expanding to the maximum allowed size because the content - /// can expand and contract during scrolling, which means the size of the - /// scroll view needs to be recomputed whenever the scroll position changes. - /// - /// Defaults to false. - /// {@endtemplate} - final bool shrinkWrap; - - /// {@template flutter.widgets.scroll_view.physics} - /// How the scroll view should respond to user input. - /// - /// For example, determines how the scroll view continues to animate after the - /// user stops dragging the scroll view. - /// - /// Defaults to matching platform conventions. Furthermore, if [primary] is - /// false, then the user cannot scroll if there is insufficient content to - /// scroll, while if [primary] is true, they can always attempt to scroll. - /// - /// To force the scroll view to always be scrollable even if there is - /// insufficient content, as if [primary] was true but without necessarily - /// setting it to true, provide an [AlwaysScrollableScrollPhysics] physics - /// object, as in: - /// - /// ```dart - /// physics: const AlwaysScrollableScrollPhysics(), - /// ``` - /// - /// To force the scroll view to use the default platform conventions and not - /// be scrollable if there is insufficient content, regardless of the value of - /// [primary], provide an explicit [ScrollPhysics] object, as in: - /// - /// ```dart - /// physics: const ScrollPhysics(), - /// ``` - /// - /// The physics can be changed dynamically (by providing a new object in a - /// subsequent build), but new physics will only take effect if the _class_ of - /// the provided object changes. Merely constructing a new instance with a - /// different configuration is insufficient to cause the physics to be - /// reapplied. (This is because the final object used is generated - /// dynamically, which can be relatively expensive, and it would be - /// inefficient to speculatively create this object each frame to see if the - /// physics should be updated.) - /// {@endtemplate} - /// - /// If an explicit [ScrollBehavior] is provided to [scrollBehavior], the - /// [ScrollPhysics] provided by that behavior will take precedence after - /// [physics]. - final ScrollPhysics? physics; - - /// {@macro flutter.rendering.RenderViewportBase.cacheExtent} - final double? cacheExtent; - - /// {@macro flutter.widgets.scrollable.dragStartBehavior} - final DragStartBehavior dragStartBehavior; - - /// {@template flutter.widgets.scroll_view.keyboardDismissBehavior} - /// [ScrollViewKeyboardDismissBehavior] the defines how this [ScrollView] will - /// dismiss the keyboard automatically. - /// {@endtemplate} - final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior; - - /// {@macro flutter.widgets.scrollable.restorationId} - final String? restorationId; - - /// {@macro flutter.material.Material.clipBehavior} - /// - /// Defaults to [Clip.hardEdge]. - final Clip clipBehavior; - - @override - Widget build(BuildContext context) => PagedValueListView( - scrollDirection: scrollDirection, - padding: padding, - physics: physics, - reverse: reverse, - controller: controller, - scrollController: scrollController, - primary: primary, - shrinkWrap: shrinkWrap, - addAutomaticKeepAlives: addAutomaticKeepAlives, - addRepaintBoundaries: addRepaintBoundaries, - addSemanticIndexes: addSemanticIndexes, - keyboardDismissBehavior: keyboardDismissBehavior, - restorationId: restorationId, - dragStartBehavior: dragStartBehavior, - cacheExtent: cacheExtent, - clipBehavior: clipBehavior, - loadMoreTriggerIndex: loadMoreTriggerIndex, - separatorBuilder: separatorBuilder, - itemBuilder: (context, users, index) { - final user = users[index]; - final onTap = onUserTap; - final onLongPress = onUserLongPress; - - final streamUserListTile = StreamUserListTile( - user: user, - onTap: onTap == null ? null : () => onTap(user), - onLongPress: onLongPress == null ? null : () => onLongPress(user), - ); - - return itemBuilder?.call( - context, - users, - index, - streamUserListTile, - ) ?? - streamUserListTile; - }, - emptyBuilder: (context) { - final chatThemeData = StreamChatTheme.of(context); - return emptyBuilder?.call(context) ?? - Center( - child: Padding( - padding: const EdgeInsets.all(8), - child: StreamScrollViewEmptyWidget( - emptyIcon: StreamSvgIcon( - size: 148, - icon: StreamSvgIcons.user, - color: chatThemeData.colorTheme.disabled, - ), - emptyTitle: Text( - context.translations.noUsersLabel, - style: chatThemeData.textTheme.headline, - ), - ), - ), - ); - }, - loadMoreErrorBuilder: (context, error) => - StreamScrollViewLoadMoreError.list( - onTap: controller.retry, - error: Text(context.translations.loadingUsersError), - ), - loadMoreIndicatorBuilder: (context) => const Center( - child: Padding( - padding: EdgeInsets.all(16), - child: StreamScrollViewLoadMoreIndicator(), - ), - ), - loadingBuilder: (context) => - loadingBuilder?.call(context) ?? - const Center( - child: StreamScrollViewLoadingWidget(), - ), - errorBuilder: (context, error) => - errorBuilder?.call(context, error) ?? - Center( - child: StreamScrollViewErrorWidget( - errorTitle: Text(context.translations.loadingUsersError), - onRetryPressed: controller.refresh, - ), - ), - ); -} - -/// A widget that is used to display a separator between -/// [StreamUserListTile] items. -class StreamUserListSeparator extends StatelessWidget { - /// Creates a new instance of [StreamUserListSeparator]. - const StreamUserListSeparator({super.key}); - - @override - Widget build(BuildContext context) { - final effect = StreamChatTheme.of(context).colorTheme.borderBottom; - return Container( - height: 1, - // ignore: deprecated_member_use - color: effect.color!.withOpacity(effect.alpha ?? 1.0), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/stream_chat.dart b/packages/stream_chat_flutter/lib/src/stream_chat.dart deleted file mode 100644 index d67f5a1d5f..0000000000 --- a/packages/stream_chat_flutter/lib/src/stream_chat.dart +++ /dev/null @@ -1,175 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:flutter_portal/flutter_portal.dart'; -import 'package:stream_chat_flutter/src/video/vlc/vlc_manager.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamChat} -/// Widget used to provide information about the chat to the widget tree -/// -/// class MyApp extends StatelessWidget { -/// final StreamChatClient client; -/// -/// MyApp(this.client); -/// -/// @override -/// Widget build(BuildContext context) { -/// return MaterialApp( -/// home: Container( -/// child: StreamChat( -/// client: client, -/// child: ChannelListPage(), -/// ), -/// ), -/// ); -/// } -/// } -/// -/// Use [StreamChat.of] to get the current [StreamChatState] instance. -/// {@endtemplate} -class StreamChat extends StatefulWidget { - /// {@macro streamChat} - const StreamChat({ - super.key, - required this.client, - required this.child, - this.streamChatThemeData, - this.streamChatConfigData, - this.onBackgroundEventReceived, - this.backgroundKeepAlive = const Duration(minutes: 1), - this.connectivityStream, - }); - - /// Client to do chat operations with - final StreamChatClient client; - - /// Child which inherits details - final Widget? child; - - /// Theme to pass on - final StreamChatThemeData? streamChatThemeData; - - /// Non-theme related UI configuration options. - final StreamChatConfigurationData? streamChatConfigData; - - /// The amount of time that will pass before disconnecting the client - /// in the background - final Duration backgroundKeepAlive; - - /// Handler called whenever the [client] receives a new [Event] while the app - /// is in background. Can be used to display various notifications depending - /// upon the [Event.type] - final EventHandler? onBackgroundEventReceived; - - /// Stream of connectivity result - /// Visible for testing - @visibleForTesting - final Stream>? connectivityStream; - - @override - StreamChatState createState() => StreamChatState(); - - /// Use this method to get the current [StreamChatState] instance - static StreamChatState of(BuildContext context) { - StreamChatState? streamChatState; - - streamChatState = context.findAncestorStateOfType(); - - if (streamChatState == null) { - throw Exception( - 'You must have a StreamChat widget at the top of your widget tree', - ); - } - - return streamChatState; - } -} - -/// The current state of the StreamChat widget -class StreamChatState extends State { - /// Gets client from widget - StreamChatClient get client => widget.client; - - /// Gets configuration options from widget - StreamChatConfigurationData get streamChatConfigData => - widget.streamChatConfigData ?? StreamChatConfigurationData(); - - @override - void initState() { - super.initState(); - // Ensures that VLC only initializes in real desktop environments - if (!isTestEnvironment && isDesktopVideoPlayerSupported) { - VlcManager.instance.initialize(); - } - } - - @override - Widget build(BuildContext context) { - final theme = _getTheme(context, widget.streamChatThemeData); - return Portal( - child: StreamChatConfiguration( - data: streamChatConfigData, - child: StreamChatTheme( - data: theme, - child: Builder( - builder: (context) { - final materialTheme = Theme.of(context); - final streamTheme = StreamChatTheme.of(context); - return Theme( - data: materialTheme.copyWith( - primaryIconTheme: streamTheme.primaryIconTheme, - colorScheme: materialTheme.colorScheme.copyWith( - secondary: streamTheme.colorTheme.accentPrimary, - ), - ), - child: StreamChatCore( - client: client, - onBackgroundEventReceived: widget.onBackgroundEventReceived, - backgroundKeepAlive: widget.backgroundKeepAlive, - connectivityStream: widget.connectivityStream, - child: Builder( - builder: (context) { - StreamChatClient.additionalHeaders = { - 'X-Stream-Client': - '${StreamChatClient.defaultUserAgent}-' - 'ui-${StreamChatClient.packageVersion}', - }; - return widget.child ?? const Offstage(); - }, - ), - ), - ); - }, - ), - ), - ), - ); - } - - StreamChatThemeData _getTheme( - BuildContext context, - StreamChatThemeData? themeData, - ) { - final appBrightness = Theme.of(context).brightness; - final defaultTheme = StreamChatThemeData(brightness: appBrightness); - return defaultTheme.merge(themeData); - } - - /// The current user - User? get currentUser => widget.client.state.currentUser; - - /// The current user as a stream - Stream get currentUserStream => widget.client.state.currentUserStream; - - @override - void didChangeDependencies() { - final currentLocale = - Localizations.localeOf(context).toString().toLowerCase(); - final availableLocales = Jiffy.getSupportedLocales(); - if (availableLocales.contains(currentLocale)) { - Jiffy.setLocale(currentLocale); - } - super.didChangeDependencies(); - } -} diff --git a/packages/stream_chat_flutter/lib/src/stream_chat_configuration.dart b/packages/stream_chat_flutter/lib/src/stream_chat_configuration.dart deleted file mode 100644 index 5363cb4e1f..0000000000 --- a/packages/stream_chat_flutter/lib/src/stream_chat_configuration.dart +++ /dev/null @@ -1,243 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/indicators/loading_indicator.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamChatConfiguration} -/// Inherited widget providing the [StreamChatConfigurationData] -/// to the widget tree -/// {@endtemplate} -class StreamChatConfiguration extends InheritedWidget { - /// {@macro streamChatConfiguration} - const StreamChatConfiguration({ - super.key, - required this.data, - required super.child, - }); - - /// {@macro streamChatConfigurationData} - final StreamChatConfigurationData data; - - @override - bool updateShouldNotify(StreamChatConfiguration oldWidget) => - data != oldWidget.data; - - /// Use this method to get the current [StreamChatThemeData] instance - static StreamChatConfigurationData of(BuildContext context) { - final streamChatConfiguration = - context.dependOnInheritedWidgetOfExactType(); - - assert( - streamChatConfiguration != null, - ''' -You must have a StreamChatConfigurationProvider widget at the top of your widget tree''', - ); - - return streamChatConfiguration!.data; - } -} - -/// {@template streamChatConfigurationData} -/// Provides global, user-configurable, non-theme related configuration -/// options to Flutter applications that use Stream Chat. -/// -/// In order to set these configuration options, you must pass an instance of -/// this class to the [StreamChat] widget, or wrap a subtree using -/// the [StreamChatConfiguration] inherited widget. -/// -/// If you need to access the configuration directly at a later point in your -/// application, you can use the [StreamChatConfiguration.of] method -/// to retrieve it. -/// -/// If no [StreamChatConfigurationData] is provided, the -/// [StreamChatConfiguration.defaults] factory constructor is used to provide a -/// default configuration. -/// -/// If you want to keep some of the default values, but not others, you can use -/// the [StreamChatConfigurationData.copyWith] method to override the values in -/// question. -/// -/// Example 1: -/// ```dart -/// class MyApp extends StatelessWidget { -/// const MyApp({ -/// required this.client, -/// }); -/// -/// final StreamChatClient client; -/// -/// @override -/// Widget build(BuildContext context) { -/// return MaterialApp( -/// home: Container( -/// child: StreamChat( -/// client: client, -/// // No configuration provided, so the defaults are used. -/// child: ChannelListPage(), -/// ), -/// ), -/// ); -/// } -/// } -/// ``` -/// -/// Example 2: -/// ```dart -/// class MyApp extends StatelessWidget { -/// const MyApp({ -/// required this.client, -/// }); -/// -/// final StreamChatClient client; -/// -/// @override -/// Widget build(BuildContext context) { -/// return MaterialApp( -/// home: Container( -/// child: StreamChat( -/// client: client, -/// config: StreamChatConfiguration.defaults().copyWith( -/// // Override a specific default value here -/// ), -/// child: ChannelListPage(), -/// ), -/// ), -/// ); -/// } -/// } -/// ``` -/// {@endtemplate} -class StreamChatConfigurationData { - /// {@macro streamChatConfigurationData} - factory StreamChatConfigurationData({ - Widget loadingIndicator = const StreamLoadingIndicator(), - Widget Function(BuildContext, User)? defaultUserImage, - Widget Function(BuildContext, User)? placeholderUserImage, - List? reactionIcons, - bool? enforceUniqueReactions, - }) { - return StreamChatConfigurationData._( - loadingIndicator: loadingIndicator, - defaultUserImage: defaultUserImage ?? _defaultUserImage, - placeholderUserImage: placeholderUserImage, - reactionIcons: reactionIcons ?? _defaultReactionIcons, - enforceUniqueReactions: enforceUniqueReactions ?? true, - ); - } - - StreamChatConfigurationData._({ - required this.loadingIndicator, - required this.defaultUserImage, - required this.placeholderUserImage, - required this.reactionIcons, - required this.enforceUniqueReactions, - }); - - /// Copies the configuration options from one [StreamChatConfigurationData] to - /// another. - StreamChatConfigurationData copyWith({ - Widget? loadingIndicator, - Widget Function(BuildContext, User)? defaultUserImage, - Widget Function(BuildContext, User)? placeholderUserImage, - List? reactionIcons, - bool? enforceUniqueReactions, - }) { - return StreamChatConfigurationData( - reactionIcons: reactionIcons ?? this.reactionIcons, - defaultUserImage: defaultUserImage ?? this.defaultUserImage, - placeholderUserImage: placeholderUserImage ?? this.placeholderUserImage, - loadingIndicator: loadingIndicator ?? this.loadingIndicator, - enforceUniqueReactions: - enforceUniqueReactions ?? this.enforceUniqueReactions, - ); - } - - /// The widget that will be shown to indicate loading. - final Widget loadingIndicator; - - /// The widget that will be built when the user image is unavailable. - final Widget Function(BuildContext, User) defaultUserImage; - - /// The widget that will be built when the user image is loading. - final Widget Function(BuildContext, User)? placeholderUserImage; - - /// Assets used for rendering reactions. - final List reactionIcons; - - /// Whether a new reaction should replace the existing one. - final bool enforceUniqueReactions; - - static final _defaultReactionIcons = [ - StreamReactionIcon( - type: 'love', - builder: (context, highlighted, size) { - final theme = StreamChatTheme.of(context); - return StreamSvgIcon( - icon: StreamSvgIcons.loveReaction, - color: highlighted - ? theme.colorTheme.accentPrimary - : theme.primaryIconTheme.color, - size: size, - ); - }, - ), - StreamReactionIcon( - type: 'like', - builder: (context, highlighted, size) { - final theme = StreamChatTheme.of(context); - return StreamSvgIcon( - icon: StreamSvgIcons.thumbsUpReaction, - color: highlighted - ? theme.colorTheme.accentPrimary - : theme.primaryIconTheme.color, - size: size, - ); - }, - ), - StreamReactionIcon( - type: 'sad', - builder: (context, highlighted, size) { - final theme = StreamChatTheme.of(context); - return StreamSvgIcon( - icon: StreamSvgIcons.thumbsDownReaction, - color: highlighted - ? theme.colorTheme.accentPrimary - : theme.primaryIconTheme.color, - size: size, - ); - }, - ), - StreamReactionIcon( - type: 'haha', - builder: (context, highlighted, size) { - final theme = StreamChatTheme.of(context); - return StreamSvgIcon( - icon: StreamSvgIcons.lolReaction, - color: highlighted - ? theme.colorTheme.accentPrimary - : theme.primaryIconTheme.color, - size: size, - ); - }, - ), - StreamReactionIcon( - type: 'wow', - builder: (context, highlighted, size) { - final theme = StreamChatTheme.of(context); - return StreamSvgIcon( - icon: StreamSvgIcons.wutReaction, - color: highlighted - ? theme.colorTheme.accentPrimary - : theme.primaryIconTheme.color, - size: size, - ); - }, - ), - ]; - - static Widget _defaultUserImage(BuildContext context, User user) => Center( - child: StreamGradientAvatar( - name: user.name, - userId: user.id, - ), - ); -} diff --git a/packages/stream_chat_flutter/lib/src/theme/audio_waveform_slider_theme.dart b/packages/stream_chat_flutter/lib/src/theme/audio_waveform_slider_theme.dart deleted file mode 100644 index 0b2b9b5424..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/audio_waveform_slider_theme.dart +++ /dev/null @@ -1,139 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; -import 'package:stream_chat_flutter/src/theme/audio_waveform_theme.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -/// {@template streamAudioWaveformSliderTheme} -/// Overrides the default style of [StreamAudioWaveformSlider] descendants. -/// -/// See also: -/// -/// * [StreamVoiceRecordingAttachmentThemeData], which is used to configure -/// this theme. -/// {@endtemplate} -class StreamAudioWaveformSliderTheme extends InheritedTheme { - /// Creates a [StreamAudioWaveformSliderTheme]. - /// - /// The [data] parameter must not be null. - const StreamAudioWaveformSliderTheme({ - super.key, - required this.data, - required super.child, - }); - - /// The configuration of this theme. - final StreamAudioWaveformSliderThemeData data; - - /// The closest instance of this class that encloses the given context. - /// - /// If there is no enclosing [StreamAudioWaveformSliderTheme] widget, - /// then [StreamAudioWaveformSliderTheme.audioWaveformSliderTheme] is used. - /// - /// Typical usage is as follows: - /// - /// ```dart - /// StreamAudioWaveformSliderTheme theme = - /// StreamAudioWaveformSliderTheme.of(context); - /// ``` - static StreamAudioWaveformSliderThemeData of(BuildContext context) { - final audioWaveformSliderTheme = context - .dependOnInheritedWidgetOfExactType(); - return audioWaveformSliderTheme?.data ?? - StreamChatTheme.of(context).audioWaveformSliderTheme; - } - - @override - Widget wrap(BuildContext context, Widget child) => - StreamAudioWaveformSliderTheme(data: data, child: child); - - @override - bool updateShouldNotify(StreamAudioWaveformSliderTheme oldWidget) => - data != oldWidget.data; -} - -/// {@template streamAudioWaveformSliderThemeData} -/// A style that overrides the default appearance of -/// [StreamAudioWaveformSlider] widgets when used with -/// [StreamAudioWaveformSliderTheme] or with the overall -/// [StreamChatTheme]'s [StreamChatThemeData.audioWaveformSliderTheme]. -/// {@endtemplate} -class StreamAudioWaveformSliderThemeData with Diagnosticable { - /// {@macro streamVoiceRecordingAttachmentThemeData} - const StreamAudioWaveformSliderThemeData({ - this.audioWaveformTheme, - this.thumbColor, - this.thumbBorderColor, - }); - - /// The theme of the audio waveform. - final StreamAudioWaveformThemeData? audioWaveformTheme; - - /// The color of the thumb. - final Color? thumbColor; - - /// The color of the thumb border. - final Color? thumbBorderColor; - - /// A copy of [StreamAudioWaveformSliderThemeData] with specified attributes - /// overridden. - StreamAudioWaveformSliderThemeData copyWith({ - StreamAudioWaveformThemeData? audioWaveformTheme, - Color? thumbColor, - Color? thumbBorderColor, - }) { - return StreamAudioWaveformSliderThemeData( - audioWaveformTheme: audioWaveformTheme ?? this.audioWaveformTheme, - thumbColor: thumbColor ?? this.thumbColor, - thumbBorderColor: thumbBorderColor ?? this.thumbBorderColor, - ); - } - - /// Merges this [StreamPollOptionsDialogThemeData] with the [other]. - StreamAudioWaveformSliderThemeData merge( - StreamAudioWaveformSliderThemeData? other, - ) { - if (other == null) return this; - return copyWith( - audioWaveformTheme: other.audioWaveformTheme, - thumbColor: other.thumbColor, - thumbBorderColor: other.thumbBorderColor, - ); - } - - /// Linearly interpolate between two [StreamPollOptionsDialogThemeData]. - static StreamAudioWaveformSliderThemeData lerp( - StreamAudioWaveformSliderThemeData a, - StreamAudioWaveformSliderThemeData b, - double t, - ) => - StreamAudioWaveformSliderThemeData( - audioWaveformTheme: StreamAudioWaveformThemeData.lerp( - a.audioWaveformTheme!, b.audioWaveformTheme!, t), - thumbColor: Color.lerp(a.thumbColor, b.thumbColor, t), - thumbBorderColor: Color.lerp(a.thumbBorderColor, b.thumbBorderColor, t), - ); - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StreamAudioWaveformSliderThemeData && - other.audioWaveformTheme == audioWaveformTheme && - other.thumbColor == thumbColor && - other.thumbBorderColor == thumbBorderColor; - - @override - int get hashCode => - audioWaveformTheme.hashCode ^ - thumbColor.hashCode ^ - thumbBorderColor.hashCode; - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty( - 'audioWaveformTheme', audioWaveformTheme)) - ..add(ColorProperty('thumbColor', thumbColor)) - ..add(ColorProperty('thumbBorderColor', thumbBorderColor)); - } -} diff --git a/packages/stream_chat_flutter/lib/src/theme/audio_waveform_theme.dart b/packages/stream_chat_flutter/lib/src/theme/audio_waveform_theme.dart deleted file mode 100644 index dde8fc78df..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/audio_waveform_theme.dart +++ /dev/null @@ -1,159 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -/// {@template streamAudioWaveformTheme} -/// Overrides the default style of [StreamAudioWaveform] descendants. -/// -/// See also: -/// -/// * [StreamVoiceRecordingAttachmentThemeData], which is used to configure -/// this theme. -/// {@endtemplate} -class StreamAudioWaveformTheme extends InheritedTheme { - /// Creates a [StreamAudioWaveformTheme]. - /// - /// The [data] parameter must not be null. - const StreamAudioWaveformTheme({ - super.key, - required this.data, - required super.child, - }); - - /// The configuration of this theme. - final StreamAudioWaveformThemeData data; - - /// The closest instance of this class that encloses the given context. - /// - /// If there is no enclosing [StreamAudioWaveformTheme] widget, - /// then [StreamAudioWaveformTheme.audioWaveformSliderTheme] is used. - /// - /// Typical usage is as follows: - /// - /// ```dart - /// StreamAudioWaveformTheme theme = StreamAudioWaveformTheme.of(context); - /// ``` - static StreamAudioWaveformThemeData of(BuildContext context) { - final audioWaveformTheme = - context.dependOnInheritedWidgetOfExactType(); - return audioWaveformTheme?.data ?? - StreamChatTheme.of(context).audioWaveformTheme; - } - - @override - Widget wrap(BuildContext context, Widget child) => - StreamAudioWaveformTheme(data: data, child: child); - - @override - bool updateShouldNotify(StreamAudioWaveformTheme oldWidget) => - data != oldWidget.data; -} - -/// {@template streamVoiceRecordingAttachmentThemeData} -/// A style that overrides the default appearance of -/// [StreamAudioWaveformSlider] widgets when used with -/// [StreamAudioWaveformTheme] or with the overall -/// [StreamChatTheme]'s [StreamChatThemeData.audioWaveformSliderTheme]. -/// {@endtemplate} -class StreamAudioWaveformThemeData with Diagnosticable { - /// {@macro streamAudioWaveformThemeData} - const StreamAudioWaveformThemeData({ - this.color, - this.progressColor, - this.minBarHeight, - this.spacingRatio, - this.heightScale, - }); - - /// The color of the wave bars. - final Color? color; - - /// The color of the progressed wave bars. - final Color? progressColor; - - /// The minimum height of the bars. - final double? minBarHeight; - - /// The ratio of the spacing between the bars. - final double? spacingRatio; - - /// The scale of the height of the bars. - final double? heightScale; - - /// A copy of [StreamAudioWaveformThemeData] with specified attributes - /// overridden. - StreamAudioWaveformThemeData copyWith({ - Color? color, - Color? progressColor, - double? minBarHeight, - double? spacingRatio, - double? heightScale, - }) { - return StreamAudioWaveformThemeData( - color: color ?? this.color, - progressColor: progressColor ?? this.progressColor, - minBarHeight: minBarHeight ?? this.minBarHeight, - spacingRatio: spacingRatio ?? this.spacingRatio, - heightScale: heightScale ?? this.heightScale, - ); - } - - /// Merges this [StreamPollOptionsDialogThemeData] with the [other]. - StreamAudioWaveformThemeData merge( - StreamAudioWaveformThemeData? other, - ) { - if (other == null) return this; - return copyWith( - color: other.color, - progressColor: other.progressColor, - minBarHeight: other.minBarHeight, - spacingRatio: other.spacingRatio, - heightScale: other.heightScale, - ); - } - - /// Linearly interpolate between two [StreamPollOptionsDialogThemeData]. - static StreamAudioWaveformThemeData lerp( - StreamAudioWaveformThemeData a, - StreamAudioWaveformThemeData b, - double t, - ) => - StreamAudioWaveformThemeData( - color: Color.lerp(a.color, b.color, t), - progressColor: Color.lerp(a.progressColor, b.progressColor, t), - minBarHeight: lerpDouble(a.minBarHeight, b.minBarHeight, t), - spacingRatio: lerpDouble(a.spacingRatio, b.spacingRatio, t), - heightScale: lerpDouble(a.heightScale, b.heightScale, t), - ); - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StreamAudioWaveformThemeData && - other.color == color && - other.progressColor == progressColor && - other.minBarHeight == minBarHeight && - other.spacingRatio == spacingRatio && - other.heightScale == heightScale; - - @override - int get hashCode => - color.hashCode ^ - progressColor.hashCode ^ - minBarHeight.hashCode ^ - spacingRatio.hashCode ^ - heightScale.hashCode; - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(ColorProperty('color', color)) - ..add(ColorProperty('progressColor', progressColor)) - ..add(DoubleProperty('minBarHeight', minBarHeight)) - ..add(DoubleProperty('spacingRatio', spacingRatio)) - ..add(DoubleProperty('heightScale', heightScale)); - } -} diff --git a/packages/stream_chat_flutter/lib/src/theme/avatar_theme.dart b/packages/stream_chat_flutter/lib/src/theme/avatar_theme.dart deleted file mode 100644 index dbc36da931..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/avatar_theme.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -/// {@template avatarThemeData} -/// A style that overrides the default appearance of various avatar widgets. -/// {@endtemplate} -// ignore: prefer-match-file-name -class StreamAvatarThemeData with Diagnosticable { - /// {@macro avatarThemeData} - const StreamAvatarThemeData({ - BoxConstraints? constraints, - BorderRadius? borderRadius, - }) : _constraints = constraints, - _borderRadius = borderRadius; - - final BoxConstraints? _constraints; - final BorderRadius? _borderRadius; - - /// Get constraints for avatar - BoxConstraints get constraints => - _constraints ?? - const BoxConstraints.tightFor( - height: 32, - width: 32, - ); - - /// Get border radius - BorderRadius get borderRadius => _borderRadius ?? BorderRadius.circular(20); - - /// Copy this [StreamAvatarThemeData] to another. - StreamAvatarThemeData copyWith({ - BoxConstraints? constraints, - BorderRadius? borderRadius, - }) { - return StreamAvatarThemeData( - constraints: constraints ?? _constraints, - borderRadius: borderRadius ?? _borderRadius, - ); - } - - /// Linearly interpolate between two [UserAvatar] themes. - /// - /// All the properties must be non-null. - StreamAvatarThemeData lerp( - StreamAvatarThemeData a, - StreamAvatarThemeData b, - double t, - ) { - return StreamAvatarThemeData( - borderRadius: BorderRadius.lerp(a.borderRadius, b.borderRadius, t), - constraints: BoxConstraints.lerp(a.constraints, b.constraints, t), - ); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StreamAvatarThemeData && - runtimeType == other.runtimeType && - _constraints == other._constraints && - _borderRadius == other._borderRadius; - - @override - int get hashCode => _constraints.hashCode ^ _borderRadius.hashCode; - - /// Merges one [StreamAvatarThemeData] with the another - StreamAvatarThemeData merge(StreamAvatarThemeData? other) { - if (other == null) return this; - return copyWith( - constraints: other._constraints, - borderRadius: other._borderRadius, - ); - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty('borderRadius', borderRadius)) - ..add(DiagnosticsProperty('constraints', constraints)); - } -} diff --git a/packages/stream_chat_flutter/lib/src/theme/channel_header_theme.dart b/packages/stream_chat_flutter/lib/src/theme/channel_header_theme.dart deleted file mode 100644 index 2df24f685e..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/channel_header_theme.dart +++ /dev/null @@ -1,155 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/theme/avatar_theme.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter/src/theme/themes.dart'; - -/// {@template channel_header_theme} -/// Overrides the default style of [ChannelHeader] descendants. -/// -/// See also: -/// -/// * [StreamChannelHeaderThemeData], which is used to configure this theme. -/// {@endtemplate} -class StreamChannelHeaderTheme extends InheritedTheme { - /// Creates a [StreamChannelHeaderTheme]. - /// - /// The [data] parameter must not be null. - const StreamChannelHeaderTheme({ - super.key, - required this.data, - required super.child, - }); - - /// The configuration of this theme. - final StreamChannelHeaderThemeData data; - - /// The closest instance of this class that encloses the given context. - /// - /// If there is no enclosing [StreamChannelHeaderTheme] widget, then - /// [StreamChatThemeData.channelTheme.channelHeaderTheme] is used. - /// - /// Typical usage is as follows: - /// - /// ```dart - /// final theme = ChannelHeaderTheme.of(context); - /// ``` - static StreamChannelHeaderThemeData of(BuildContext context) { - final channelHeaderTheme = - context.dependOnInheritedWidgetOfExactType(); - return channelHeaderTheme?.data ?? - StreamChatTheme.of(context).channelHeaderTheme; - } - - @override - Widget wrap(BuildContext context, Widget child) => - StreamChannelHeaderTheme(data: data, child: child); - - @override - bool updateShouldNotify(StreamChannelHeaderTheme oldWidget) => - data != oldWidget.data; -} - -/// {@template channel_header_theme_data} -/// A style that overrides the default appearance of [ChannelHeader]s when used -/// with [StreamChannelHeaderTheme] or with the overall [StreamChatTheme]'s -/// [StreamChatThemeData.channelHeaderTheme]. -/// -/// See also: -/// -/// * [StreamChannelHeaderTheme], the theme which is configured with this class. -/// * [StreamChatThemeData.channelHeaderTheme], which can be used to override -/// the default style for [ChannelHeader]s below the overall [StreamChatTheme]. -/// {@endtemplate} -class StreamChannelHeaderThemeData with Diagnosticable { - /// Creates a [StreamChannelHeaderThemeData] - const StreamChannelHeaderThemeData({ - this.titleStyle, - this.subtitleStyle, - this.avatarTheme, - this.color, - }); - - /// Theme for title - final TextStyle? titleStyle; - - /// Theme for subtitle - final TextStyle? subtitleStyle; - - /// Theme for avatar - final StreamAvatarThemeData? avatarTheme; - - /// Color for [StreamChannelHeaderThemeData] - final Color? color; - - /// Copy with theme - StreamChannelHeaderThemeData copyWith({ - TextStyle? titleStyle, - TextStyle? subtitleStyle, - StreamAvatarThemeData? avatarTheme, - Color? color, - }) { - return StreamChannelHeaderThemeData( - titleStyle: titleStyle ?? this.titleStyle, - subtitleStyle: subtitleStyle ?? this.subtitleStyle, - avatarTheme: avatarTheme ?? this.avatarTheme, - color: color ?? this.color, - ); - } - - /// Linearly interpolate between two [StreamChannelHeaderThemeData]. - /// - /// All the properties must be non-null. - StreamChannelHeaderThemeData lerp( - StreamChannelHeaderThemeData a, - StreamChannelHeaderThemeData b, - double t, - ) { - return StreamChannelHeaderThemeData( - titleStyle: TextStyle.lerp(a.titleStyle, b.titleStyle, t), - subtitleStyle: TextStyle.lerp(a.subtitleStyle, b.subtitleStyle, t), - avatarTheme: - const StreamAvatarThemeData().lerp(a.avatarTheme!, b.avatarTheme!, t), - color: Color.lerp(a.color, b.color, t), - ); - } - - /// Merge with other [StreamChannelHeaderThemeData] - StreamChannelHeaderThemeData merge(StreamChannelHeaderThemeData? other) { - if (other == null) return this; - return copyWith( - titleStyle: titleStyle?.merge(other.titleStyle) ?? other.titleStyle, - subtitleStyle: - subtitleStyle?.merge(other.subtitleStyle) ?? other.subtitleStyle, - avatarTheme: avatarTheme?.merge(other.avatarTheme) ?? other.avatarTheme, - color: other.color, - ); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StreamChannelHeaderThemeData && - runtimeType == other.runtimeType && - titleStyle == other.titleStyle && - subtitleStyle == other.subtitleStyle && - avatarTheme == other.avatarTheme && - color == other.color; - - @override - int get hashCode => - titleStyle.hashCode ^ - subtitleStyle.hashCode ^ - avatarTheme.hashCode ^ - color.hashCode; - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty('title', titleStyle)) - ..add(DiagnosticsProperty('subtitle', subtitleStyle)) - ..add(DiagnosticsProperty('avatarTheme', avatarTheme)) - ..add(ColorProperty('color', color)); - } -} diff --git a/packages/stream_chat_flutter/lib/src/theme/channel_list_header_theme.dart b/packages/stream_chat_flutter/lib/src/theme/channel_list_header_theme.dart deleted file mode 100644 index 7452065df7..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/channel_list_header_theme.dart +++ /dev/null @@ -1,135 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/theme/avatar_theme.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -/// {@template channelListHeaderTheme} -/// Overrides the default style of [ChannelListHeader] descendants. -/// -/// See also: -/// -/// * [StreamChannelListHeaderThemeData], which is used -/// to configure this theme. -/// {@endtemplate} -class StreamChannelListHeaderTheme extends InheritedTheme { - /// Creates a [StreamChannelListHeaderTheme]. - /// - /// The [data] parameter must not be null. - const StreamChannelListHeaderTheme({ - super.key, - required this.data, - required super.child, - }); - - /// The configuration of this theme. - final StreamChannelListHeaderThemeData data; - - /// The closest instance of this class that encloses the given context. - /// - /// If there is no enclosing [StreamChannelListHeaderTheme] widget, then - /// [StreamChatThemeData.channelListHeaderTheme] is used. - /// - /// Typical usage is as follows: - /// - /// ```dart - /// final theme = ChannelListHeaderTheme.of(context); - /// ``` - static StreamChannelListHeaderThemeData of(BuildContext context) { - final channelListHeaderTheme = context - .dependOnInheritedWidgetOfExactType(); - return channelListHeaderTheme?.data ?? - StreamChatTheme.of(context).channelListHeaderTheme; - } - - @override - Widget wrap(BuildContext context, Widget child) => - StreamChannelListHeaderTheme(data: data, child: child); - - @override - bool updateShouldNotify(StreamChannelListHeaderTheme oldWidget) => - data != oldWidget.data; -} - -/// {@template channel_list_header_theme_data} -/// Theme dedicated to the [ChannelListHeader] -/// {@endtemplate} -class StreamChannelListHeaderThemeData with Diagnosticable { - /// Returns a new [StreamChannelListHeaderThemeData] - const StreamChannelListHeaderThemeData({ - this.titleStyle, - this.avatarTheme, - this.color, - }); - - /// Style of the title text - final TextStyle? titleStyle; - - /// Theme dedicated to the userAvatar - final StreamAvatarThemeData? avatarTheme; - - /// Background color of the appbar - final Color? color; - - /// Returns a new [StreamChannelListHeaderThemeData] replacing some of its - /// properties - StreamChannelListHeaderThemeData copyWith({ - TextStyle? titleStyle, - StreamAvatarThemeData? avatarTheme, - Color? color, - }) { - return StreamChannelListHeaderThemeData( - titleStyle: titleStyle ?? this.titleStyle, - avatarTheme: avatarTheme ?? this.avatarTheme, - color: color ?? this.color, - ); - } - - /// Linearly interpolate from one [StreamChannelListHeaderThemeData] - /// to another. - StreamChannelListHeaderThemeData lerp( - StreamChannelListHeaderThemeData a, - StreamChannelListHeaderThemeData b, - double t, - ) { - return StreamChannelListHeaderThemeData( - avatarTheme: - const StreamAvatarThemeData().lerp(a.avatarTheme!, b.avatarTheme!, t), - color: Color.lerp(a.color, b.color, t), - titleStyle: TextStyle.lerp(a.titleStyle, b.titleStyle, t), - ); - } - - /// Merges [this] [StreamChannelListHeaderThemeData] with the [other] - StreamChannelListHeaderThemeData merge( - StreamChannelListHeaderThemeData? other, - ) { - if (other == null) return this; - return copyWith( - titleStyle: titleStyle?.merge(other.titleStyle) ?? other.titleStyle, - avatarTheme: avatarTheme?.merge(other.avatarTheme) ?? other.avatarTheme, - color: other.color, - ); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StreamChannelListHeaderThemeData && - runtimeType == other.runtimeType && - titleStyle == other.titleStyle && - avatarTheme == other.avatarTheme && - color == other.color; - - @override - int get hashCode => - titleStyle.hashCode ^ avatarTheme.hashCode ^ color.hashCode; - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty('titleStyle', titleStyle)) - ..add(DiagnosticsProperty('avatarTheme', avatarTheme)) - ..add(ColorProperty('color', color)); - } -} diff --git a/packages/stream_chat_flutter/lib/src/theme/channel_preview_theme.dart b/packages/stream_chat_flutter/lib/src/theme/channel_preview_theme.dart deleted file mode 100644 index ff7e5deac9..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/channel_preview_theme.dart +++ /dev/null @@ -1,176 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/theme/avatar_theme.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -/// {@template channelPreviewTheme} -/// Overrides the default style of [ChannelPreview] descendants. -/// -/// See also: -/// -/// * [StreamChannelPreviewThemeData], which is used to configure this theme. -/// {@endtemplate} -class StreamChannelPreviewTheme extends InheritedTheme { - /// Creates a [StreamChannelPreviewTheme]. - /// - /// The [data] parameter must not be null. - const StreamChannelPreviewTheme({ - super.key, - required this.data, - required super.child, - }); - - /// The configuration of this theme. - final StreamChannelPreviewThemeData data; - - /// The closest instance of this class that encloses the given context. - /// - /// If there is no enclosing [StreamChannelPreviewTheme] widget, then - /// [StreamChatThemeData.channelPreviewTheme] is used. - /// - /// Typical usage is as follows: - /// - /// ```dart - /// final theme = ChannelPreviewTheme.of(context); - /// ``` - static StreamChannelPreviewThemeData of(BuildContext context) { - final channelPreviewTheme = - context.dependOnInheritedWidgetOfExactType(); - return channelPreviewTheme?.data ?? - StreamChatTheme.of(context).channelPreviewTheme; - } - - @override - Widget wrap(BuildContext context, Widget child) => - StreamChannelPreviewTheme(data: data, child: child); - - @override - bool updateShouldNotify(StreamChannelPreviewTheme oldWidget) => - data != oldWidget.data; -} - -/// {@template channelPreviewThemeData} -/// A style that overrides the default appearance of [ChannelPreview]s when used -/// with [StreamChannelPreviewTheme] or with the overall [StreamChatTheme]'s -/// [StreamChatThemeData.channelPreviewTheme]. -/// -/// See also: -/// -/// * [StreamChannelPreviewTheme], the theme -/// which is configured with this class. -/// * [StreamChatThemeData.channelPreviewTheme], which can be used to override -/// the default style for [ChannelHeader]s below the overall [StreamChatTheme]. -/// {@endtemplate} -class StreamChannelPreviewThemeData with Diagnosticable { - /// Creates a [StreamChannelPreviewThemeData]. - const StreamChannelPreviewThemeData({ - this.titleStyle, - this.subtitleStyle, - this.lastMessageAtStyle, - this.avatarTheme, - this.unreadCounterColor, - this.indicatorIconSize, - }); - - /// Theme for title - final TextStyle? titleStyle; - - /// Theme for subtitle - final TextStyle? subtitleStyle; - - /// Theme of last message at - final TextStyle? lastMessageAtStyle; - - /// Avatar theme - final StreamAvatarThemeData? avatarTheme; - - /// Unread counter color - final Color? unreadCounterColor; - - /// Indicator icon size - final double? indicatorIconSize; - - /// Copy with theme - StreamChannelPreviewThemeData copyWith({ - TextStyle? titleStyle, - TextStyle? subtitleStyle, - TextStyle? lastMessageAtStyle, - StreamAvatarThemeData? avatarTheme, - Color? unreadCounterColor, - double? indicatorIconSize, - }) { - return StreamChannelPreviewThemeData( - titleStyle: titleStyle ?? this.titleStyle, - subtitleStyle: subtitleStyle ?? this.subtitleStyle, - lastMessageAtStyle: lastMessageAtStyle ?? this.lastMessageAtStyle, - avatarTheme: avatarTheme ?? this.avatarTheme, - unreadCounterColor: unreadCounterColor ?? this.unreadCounterColor, - indicatorIconSize: indicatorIconSize ?? this.indicatorIconSize, - ); - } - - /// Linearly interpolate one [StreamChannelPreviewThemeData] to another. - StreamChannelPreviewThemeData lerp( - StreamChannelPreviewThemeData a, - StreamChannelPreviewThemeData b, - double t, - ) { - return StreamChannelPreviewThemeData( - avatarTheme: - const StreamAvatarThemeData().lerp(a.avatarTheme!, b.avatarTheme!, t), - indicatorIconSize: a.indicatorIconSize, - lastMessageAtStyle: - TextStyle.lerp(a.lastMessageAtStyle, b.lastMessageAtStyle, t), - subtitleStyle: TextStyle.lerp(a.subtitleStyle, b.subtitleStyle, t), - titleStyle: TextStyle.lerp(a.titleStyle, b.titleStyle, t), - unreadCounterColor: - Color.lerp(a.unreadCounterColor, b.unreadCounterColor, t), - ); - } - - /// Merge with theme - StreamChannelPreviewThemeData merge(StreamChannelPreviewThemeData? other) { - if (other == null) return this; - return copyWith( - titleStyle: titleStyle?.merge(other.titleStyle) ?? other.titleStyle, - subtitleStyle: - subtitleStyle?.merge(other.subtitleStyle) ?? other.subtitleStyle, - lastMessageAtStyle: lastMessageAtStyle?.merge(other.lastMessageAtStyle) ?? - other.lastMessageAtStyle, - avatarTheme: avatarTheme?.merge(other.avatarTheme) ?? other.avatarTheme, - unreadCounterColor: other.unreadCounterColor, - ); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StreamChannelPreviewThemeData && - runtimeType == other.runtimeType && - titleStyle == other.titleStyle && - subtitleStyle == other.subtitleStyle && - lastMessageAtStyle == other.lastMessageAtStyle && - avatarTheme == other.avatarTheme && - unreadCounterColor == other.unreadCounterColor && - indicatorIconSize == other.indicatorIconSize; - - @override - int get hashCode => - titleStyle.hashCode ^ - subtitleStyle.hashCode ^ - lastMessageAtStyle.hashCode ^ - avatarTheme.hashCode ^ - unreadCounterColor.hashCode ^ - indicatorIconSize.hashCode; - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty('titleStyle', titleStyle)) - ..add(DiagnosticsProperty('subtitleStyle', subtitleStyle)) - ..add(DiagnosticsProperty('lastMessageAtStyle', lastMessageAtStyle)) - ..add(DiagnosticsProperty('avatarTheme', avatarTheme)) - ..add(ColorProperty('unreadCounterColor', unreadCounterColor)); - } -} diff --git a/packages/stream_chat_flutter/lib/src/theme/color_theme.dart b/packages/stream_chat_flutter/lib/src/theme/color_theme.dart deleted file mode 100644 index 167c68ee61..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/color_theme.dart +++ /dev/null @@ -1,310 +0,0 @@ -import 'package:flutter/material.dart'; - -/// {@template color_theme} -/// Theme that holds colors -/// {@endtemplate} -class StreamColorTheme { - /// Initialise with light theme - StreamColorTheme.light({ - this.textHighEmphasis = const Color(0xff000000), - this.textLowEmphasis = const Color(0xff7a7a7a), - this.disabled = const Color(0xffdbdbdb), - this.borders = const Color(0xffecebeb), - this.inputBg = const Color(0xffe9eaed), - this.appBg = const Color(0xfff7f7f8), - this.barsBg = const Color(0xffffffff), - this.linkBg = const Color(0xffe9f2ff), - this.accentPrimary = const Color(0xff005FFF), - this.accentError = const Color(0xffFF3842), - this.accentInfo = const Color(0xff20E070), - this.highlight = const Color(0xfffbf4dd), - this.overlay = const Color.fromRGBO(0, 0, 0, 0.2), - this.overlayDark = const Color.fromRGBO(0, 0, 0, 0.6), - this.bgGradient = const LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [Color(0xfff7f7f7), Color(0xfffcfcfc)], - stops: [0, 1], - ), - this.borderTop = const Effect( - sigmaX: 0, - sigmaY: -1, - color: Color(0xff000000), - blur: 0, - alpha: 0.08, - ), - this.borderBottom = const Effect( - sigmaX: 0, - sigmaY: 1, - color: Color(0xff000000), - blur: 0, - alpha: 0.08, - ), - this.shadowIconButton = const Effect( - sigmaX: 0, - sigmaY: 2, - color: Color(0xff000000), - alpha: 0.5, - blur: 4, - ), - this.modalShadow = const Effect( - sigmaX: 0, - sigmaY: 0, - color: Color(0xff000000), - alpha: 1, - blur: 8, - ), - }) : brightness = Brightness.light; - - /// Initialise with dark theme - StreamColorTheme.dark({ - this.textHighEmphasis = const Color(0xffffffff), - this.textLowEmphasis = const Color(0xff7a7a7a), - this.disabled = const Color(0xff2d2f2f), - this.borders = const Color(0xff1c1e22), - this.inputBg = const Color(0xff13151b), - this.appBg = const Color(0xff000000), - this.barsBg = const Color(0xff121416), - this.linkBg = const Color(0xff00193D), - this.accentPrimary = const Color(0xff337eff), - this.accentError = const Color(0xffFF3742), - this.accentInfo = const Color(0xff20E070), - this.borderTop = const Effect( - sigmaX: 0, - sigmaY: -1, - color: Color(0xff141924), - blur: 0, - ), - this.borderBottom = const Effect( - sigmaX: 0, - sigmaY: 1, - color: Color(0xff141924), - blur: 0, - alpha: 1, - ), - this.shadowIconButton = const Effect( - sigmaX: 0, - sigmaY: 2, - color: Color(0xff000000), - alpha: 0.5, - blur: 4, - ), - this.modalShadow = const Effect( - sigmaX: 0, - sigmaY: 0, - color: Color(0xff000000), - alpha: 1, - blur: 8, - ), - this.highlight = const Color(0xff302d22), - this.overlay = const Color.fromRGBO(0, 0, 0, 0.4), - this.overlayDark = const Color.fromRGBO(255, 255, 255, 0.6), - this.bgGradient = const LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Color(0xff101214), - Color(0xff070a0d), - ], - stops: [0, 1], - ), - }) : brightness = Brightness.dark; - - /// - final Color textHighEmphasis; - - /// - final Color textLowEmphasis; - - /// - final Color disabled; - - /// - final Color borders; - - /// - final Color inputBg; - - /// - final Color appBg; - - /// - final Color barsBg; - - /// - final Color linkBg; - - /// - final Color accentPrimary; - - /// - final Color accentError; - - /// - final Color accentInfo; - - /// - final Effect borderTop; - - /// - final Effect borderBottom; - - /// - final Effect shadowIconButton; - - /// - final Effect modalShadow; - - /// - final Color highlight; - - /// - final Color overlay; - - /// - final Color overlayDark; - - /// - final Gradient bgGradient; - - /// - final Brightness brightness; - - /// Copy with theme - StreamColorTheme copyWith({ - Brightness brightness = Brightness.light, - Color? textHighEmphasis, - Color? textLowEmphasis, - Color? disabled, - Color? borders, - Color? inputBg, - Color? appBg, - Color? barsBg, - Color? linkBg, - Color? accentPrimary, - Color? accentError, - Color? accentInfo, - Effect? borderTop, - Effect? borderBottom, - Effect? shadowIconButton, - Effect? modalShadow, - Color? highlight, - Color? overlay, - Color? overlayDark, - Gradient? bgGradient, - }) { - return brightness == Brightness.light - ? StreamColorTheme.light( - textHighEmphasis: textHighEmphasis ?? this.textHighEmphasis, - textLowEmphasis: textLowEmphasis ?? this.textLowEmphasis, - disabled: disabled ?? this.disabled, - borders: borders ?? this.borders, - inputBg: inputBg ?? this.inputBg, - appBg: appBg ?? this.appBg, - barsBg: barsBg ?? this.barsBg, - linkBg: linkBg ?? this.linkBg, - accentPrimary: accentPrimary ?? this.accentPrimary, - accentError: accentError ?? this.accentError, - accentInfo: accentInfo ?? this.accentInfo, - borderTop: borderTop ?? this.borderTop, - borderBottom: borderBottom ?? this.borderBottom, - shadowIconButton: shadowIconButton ?? this.shadowIconButton, - modalShadow: modalShadow ?? this.modalShadow, - highlight: highlight ?? this.highlight, - overlay: overlay ?? this.overlay, - overlayDark: overlayDark ?? this.overlayDark, - bgGradient: bgGradient ?? this.bgGradient, - ) - : StreamColorTheme.dark( - textHighEmphasis: textHighEmphasis ?? this.textHighEmphasis, - textLowEmphasis: textLowEmphasis ?? this.textLowEmphasis, - disabled: disabled ?? this.disabled, - borders: borders ?? this.borders, - inputBg: inputBg ?? this.inputBg, - appBg: appBg ?? this.appBg, - barsBg: barsBg ?? this.barsBg, - linkBg: linkBg ?? this.linkBg, - accentPrimary: accentPrimary ?? this.accentPrimary, - accentError: accentError ?? this.accentError, - accentInfo: accentInfo ?? this.accentInfo, - borderTop: borderTop ?? this.borderTop, - borderBottom: borderBottom ?? this.borderBottom, - shadowIconButton: shadowIconButton ?? this.shadowIconButton, - modalShadow: modalShadow ?? this.modalShadow, - highlight: highlight ?? this.highlight, - overlay: overlay ?? this.overlay, - overlayDark: overlayDark ?? this.overlayDark, - bgGradient: bgGradient ?? this.bgGradient, - ); - } - - /// Merge color theme - StreamColorTheme merge(StreamColorTheme? other) { - if (other == null) return this; - return copyWith( - textHighEmphasis: other.textHighEmphasis, - textLowEmphasis: other.textLowEmphasis, - disabled: other.disabled, - borders: other.borders, - inputBg: other.inputBg, - appBg: other.appBg, - barsBg: other.barsBg, - linkBg: other.linkBg, - accentPrimary: other.accentPrimary, - accentError: other.accentError, - accentInfo: other.accentInfo, - highlight: other.highlight, - overlay: other.overlay, - overlayDark: other.overlayDark, - bgGradient: other.bgGradient, - borderTop: other.borderTop, - borderBottom: other.borderBottom, - shadowIconButton: other.shadowIconButton, - modalShadow: other.modalShadow, - ); - } -} - -/// Effect store -class Effect { - /// Constructor for creating [Effect] - const Effect({ - this.sigmaX, - this.sigmaY, - this.color, - this.alpha, - this.blur, - }); - - /// - final double? sigmaX; - - /// - final double? sigmaY; - - /// - final Color? color; - - /// - final double? alpha; - - /// - final double? blur; - - /// Copy with new effect - Effect copyWith({ - double? sigmaX, - double? sigmaY, - Color? color, - double? alpha, - double? blur, - }) { - return Effect( - sigmaX: sigmaX ?? this.sigmaX, - sigmaY: sigmaY ?? this.sigmaY, - color: color ?? this.color, - alpha: color as double? ?? this.alpha, - blur: blur ?? this.blur, - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/theme/gallery_footer_theme.dart b/packages/stream_chat_flutter/lib/src/theme/gallery_footer_theme.dart deleted file mode 100644 index 84e1688a60..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/gallery_footer_theme.dart +++ /dev/null @@ -1,238 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -/// {@template galleryFooterTheme} -/// Overrides the default style of [GalleryFooter] descendants. -/// -/// See also: -/// -/// * [StreamGalleryFooterThemeData], which is used to configure this theme. -/// {@endtemplate} -class StreamGalleryFooterTheme extends InheritedTheme { - /// Creates an [StreamGalleryFooterTheme]. - /// - /// The [data] parameter must not be null. - const StreamGalleryFooterTheme({ - super.key, - required this.data, - required super.child, - }); - - /// The configuration of this theme. - final StreamGalleryFooterThemeData data; - - /// The closest instance of this class that encloses the given context. - /// - /// If there is no enclosing [StreamGalleryFooterTheme] widget, then - /// [StreamChatThemeData.galleryFooterTheme] is used. - /// - /// Typical usage is as follows: - /// - /// ```dart - /// ImageFooterTheme theme = ImageFooterTheme.of(context); - /// ``` - static StreamGalleryFooterThemeData of(BuildContext context) { - final imageFooterTheme = - context.dependOnInheritedWidgetOfExactType(); - return imageFooterTheme?.data ?? - StreamChatTheme.of(context).galleryFooterTheme; - } - - @override - Widget wrap(BuildContext context, Widget child) => - StreamGalleryFooterTheme(data: data, child: child); - - @override - bool updateShouldNotify(StreamGalleryFooterTheme oldWidget) => - data != oldWidget.data; -} - -/// {@template galleryFooterThemeData} -/// A style that overrides the default appearance of [GalleryFooter]s when used -/// with [StreamGalleryFooterTheme] or with the overall [StreamChatTheme]'s -/// [StreamChatThemeData.galleryFooterTheme]. -/// -/// See also: -/// -/// * [StreamGalleryFooterTheme], the theme which is configured with this class. -/// * [StreamChatThemeData.galleryFooterTheme], which can be used to override -/// the default style for [GalleryFooter]s below the overall [StreamChatTheme]. -/// {@endtemplate} -class StreamGalleryFooterThemeData with Diagnosticable { - /// Creates an [StreamGalleryFooterThemeData]. - const StreamGalleryFooterThemeData({ - this.backgroundColor, - this.shareIconColor, - this.titleTextStyle, - this.gridIconButtonColor, - this.bottomSheetBarrierColor, - this.bottomSheetBackgroundColor, - this.bottomSheetPhotosTextStyle, - this.bottomSheetCloseIconColor, - }); - - /// The background color for the [GalleryFooter] widget. - /// - /// Defaults to [ColorTheme.barsBg]. - final Color? backgroundColor; - - /// The color for the "share" icon. - /// - /// Defaults to [ColorTheme.textHighEmphasis]. - final Color? shareIconColor; - - /// The [TextStyle] to use for the [GalleryFooter] title text. - /// - /// Defaults to [TextTheme.headlineBold]. - final TextStyle? titleTextStyle; - - /// The color to use for the "grid" icon. - /// - /// Defaults to [ColorTheme.textHighEmphasis]. - final Color? gridIconButtonColor; - - /// The color to use behind the bottom sheet. - /// - /// Defaults to [ColorTheme.overlay]. - final Color? bottomSheetBarrierColor; - - /// The background color to use for the bottom sheet. - /// - /// Defaults to [ColorTheme.barsBg]. - final Color? bottomSheetBackgroundColor; - - /// The [TextStyle] to use for the "photos" text in the bottom sheet. - /// - /// Defaults to [TextTheme.headlineBold]. - final TextStyle? bottomSheetPhotosTextStyle; - - /// The color to use for the "close" icon. - /// - /// Defaults to [ColorTheme.textHighEmphasis]. - final Color? bottomSheetCloseIconColor; - - /// Copies this [StreamGalleryFooterThemeData] to another. - StreamGalleryFooterThemeData copyWith({ - Color? backgroundColor, - Color? shareIconColor, - TextStyle? titleTextStyle, - Color? gridIconButtonColor, - Color? bottomSheetBarrierColor, - Color? bottomSheetBackgroundColor, - TextStyle? bottomSheetPhotosTextStyle, - Color? bottomSheetCloseIconColor, - }) { - return StreamGalleryFooterThemeData( - backgroundColor: backgroundColor ?? this.backgroundColor, - shareIconColor: shareIconColor ?? this.shareIconColor, - titleTextStyle: titleTextStyle ?? this.titleTextStyle, - gridIconButtonColor: gridIconButtonColor ?? this.gridIconButtonColor, - bottomSheetBarrierColor: - bottomSheetBarrierColor ?? this.bottomSheetBarrierColor, - bottomSheetBackgroundColor: - bottomSheetBackgroundColor ?? this.bottomSheetBackgroundColor, - bottomSheetPhotosTextStyle: - bottomSheetPhotosTextStyle ?? this.bottomSheetPhotosTextStyle, - bottomSheetCloseIconColor: - bottomSheetCloseIconColor ?? this.bottomSheetCloseIconColor, - ); - } - - /// Linearly interpolate between two [GalleryFooter] themes. - /// - /// All the properties must be non-null. - StreamGalleryFooterThemeData lerp( - StreamGalleryFooterThemeData a, - StreamGalleryFooterThemeData b, - double t, - ) { - return StreamGalleryFooterThemeData( - backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t), - shareIconColor: Color.lerp(a.shareIconColor, b.shareIconColor, t), - titleTextStyle: TextStyle.lerp(a.titleTextStyle, b.titleTextStyle, t), - gridIconButtonColor: - Color.lerp(a.gridIconButtonColor, b.gridIconButtonColor, t), - bottomSheetBarrierColor: - Color.lerp(a.bottomSheetBarrierColor, b.bottomSheetBarrierColor, t), - bottomSheetBackgroundColor: Color.lerp( - a.bottomSheetBackgroundColor, - b.bottomSheetBackgroundColor, - t, - ), - bottomSheetPhotosTextStyle: TextStyle.lerp( - a.bottomSheetPhotosTextStyle, - b.bottomSheetPhotosTextStyle, - t, - ), - bottomSheetCloseIconColor: Color.lerp( - a.bottomSheetCloseIconColor, - b.bottomSheetCloseIconColor, - t, - ), - ); - } - - /// Merges one [StreamGalleryFooterThemeData] with another. - StreamGalleryFooterThemeData merge(StreamGalleryFooterThemeData? other) { - if (other == null) return this; - return copyWith( - backgroundColor: other.backgroundColor, - bottomSheetBarrierColor: other.bottomSheetBarrierColor, - bottomSheetBackgroundColor: other.bottomSheetBackgroundColor, - bottomSheetCloseIconColor: other.bottomSheetCloseIconColor, - bottomSheetPhotosTextStyle: other.bottomSheetPhotosTextStyle, - gridIconButtonColor: other.gridIconButtonColor, - titleTextStyle: other.titleTextStyle, - shareIconColor: other.shareIconColor, - ); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StreamGalleryFooterThemeData && - runtimeType == other.runtimeType && - backgroundColor == other.backgroundColor && - shareIconColor == other.shareIconColor && - titleTextStyle == other.titleTextStyle && - gridIconButtonColor == other.gridIconButtonColor && - bottomSheetBarrierColor == other.bottomSheetBarrierColor && - bottomSheetBackgroundColor == other.bottomSheetBackgroundColor && - bottomSheetPhotosTextStyle == other.bottomSheetPhotosTextStyle && - bottomSheetCloseIconColor == other.bottomSheetCloseIconColor; - - @override - int get hashCode => - backgroundColor.hashCode ^ - shareIconColor.hashCode ^ - titleTextStyle.hashCode ^ - gridIconButtonColor.hashCode ^ - bottomSheetBarrierColor.hashCode ^ - bottomSheetBackgroundColor.hashCode ^ - bottomSheetPhotosTextStyle.hashCode ^ - bottomSheetCloseIconColor.hashCode; - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(ColorProperty('backgroundColor', backgroundColor)) - ..add(ColorProperty('shareIconColor', shareIconColor)) - ..add(DiagnosticsProperty('titleTextStyle', titleTextStyle)) - ..add(ColorProperty('gridIconButtonColor', gridIconButtonColor)) - ..add(ColorProperty('bottomSheetBarrierColor', bottomSheetBarrierColor)) - ..add(ColorProperty( - 'bottomSheetBackgroundColor', - bottomSheetBackgroundColor, - )) - ..add(DiagnosticsProperty( - 'bottomSheetPhotosTextStyle', - bottomSheetPhotosTextStyle, - )) - ..add(ColorProperty( - 'bottomSheetCloseIconColor', - bottomSheetCloseIconColor, - )); - } -} diff --git a/packages/stream_chat_flutter/lib/src/theme/gallery_header_theme.dart b/packages/stream_chat_flutter/lib/src/theme/gallery_header_theme.dart deleted file mode 100644 index 90977d4c9f..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/gallery_header_theme.dart +++ /dev/null @@ -1,185 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -/// {@template galleryHeaderTheme} -/// Overrides the default style of [GalleryHeader] descendants. -/// -/// See also: -/// -/// * [StreamGalleryHeaderThemeData], which is used to configure this theme. -/// {@endtemplate} -class StreamGalleryHeaderTheme extends InheritedTheme { - /// Creates a [StreamGalleryHeaderTheme]. - /// - /// The [data] parameter must not be null. - const StreamGalleryHeaderTheme({ - super.key, - required this.data, - required super.child, - }); - - /// The configuration of this theme. - final StreamGalleryHeaderThemeData data; - - /// The closest instance of this class that encloses the given context. - /// - /// If there is no enclosing [StreamGalleryHeaderTheme] widget, then - /// [StreamChatThemeData.galleryHeaderTheme] is used. - /// - /// Typical usage is as follows: - /// - /// ```dart - /// ImageHeaderTheme theme = ImageHeaderTheme.of(context); - /// ``` - static StreamGalleryHeaderThemeData of(BuildContext context) { - final galleryHeaderTheme = - context.dependOnInheritedWidgetOfExactType(); - return galleryHeaderTheme?.data ?? - StreamChatTheme.of(context).galleryHeaderTheme; - } - - @override - Widget wrap(BuildContext context, Widget child) => - StreamGalleryHeaderTheme(data: data, child: child); - - @override - bool updateShouldNotify(StreamGalleryHeaderTheme oldWidget) => - data != oldWidget.data; -} - -/// {@template galleryHeaderThemeData} -/// A style that overrides the default appearance of [GalleryHeader]s when used -/// with [StreamGalleryHeaderTheme] or with the overall [StreamChatTheme]'s -/// [StreamChatThemeData.galleryHeaderTheme]. -/// -/// See also: -/// -/// * [StreamGalleryHeaderTheme], the theme which is configured with this class. -/// * [StreamChatThemeData.galleryHeaderTheme], which can be used to override -/// the default style for [GalleryHeader]s below the overall [StreamChatTheme]. -/// {@endtemplate} -class StreamGalleryHeaderThemeData with Diagnosticable { - /// Creates an [StreamGalleryHeaderThemeData]. - const StreamGalleryHeaderThemeData({ - this.closeButtonColor, - this.backgroundColor, - this.iconMenuPointColor, - this.titleTextStyle, - this.subtitleTextStyle, - this.bottomSheetBarrierColor, - }); - - /// The color of the "close" button. - /// - /// Defaults to [ColorTheme.textHighEmphasis]. - final Color? closeButtonColor; - - /// The background color of the [GalleryHeader] widget. - /// - /// Defaults to [ChannelHeaderTheme.color]. - final Color? backgroundColor; - - /// Defaults to [ColorTheme.textHighEmphasis]. - final Color? iconMenuPointColor; - - /// The [TextStyle] to use for the [GalleryHeader] title text. - /// - /// Defaults to [TextTheme.headlineBold]. - final TextStyle? titleTextStyle; - - /// The [TextStyle] to use for the [GalleryHeader] subtitle text. - /// - /// Defaults to [ChannelPreviewTheme.subtitleStyle]. - final TextStyle? subtitleTextStyle; - - /// - final Color? bottomSheetBarrierColor; - - /// Copies this [StreamGalleryHeaderThemeData] to another. - StreamGalleryHeaderThemeData copyWith({ - Color? closeButtonColor, - Color? backgroundColor, - Color? iconMenuPointColor, - TextStyle? titleTextStyle, - TextStyle? subtitleTextStyle, - Color? bottomSheetBarrierColor, - }) { - return StreamGalleryHeaderThemeData( - closeButtonColor: closeButtonColor ?? this.closeButtonColor, - backgroundColor: backgroundColor ?? this.backgroundColor, - iconMenuPointColor: iconMenuPointColor ?? this.iconMenuPointColor, - titleTextStyle: titleTextStyle ?? this.titleTextStyle, - subtitleTextStyle: subtitleTextStyle ?? this.subtitleTextStyle, - bottomSheetBarrierColor: - bottomSheetBarrierColor ?? this.bottomSheetBarrierColor, - ); - } - - /// Linearly interpolate between two [GalleryHeader] themes. - /// - /// All the properties must be non-null. - StreamGalleryHeaderThemeData lerp( - StreamGalleryHeaderThemeData a, - StreamGalleryHeaderThemeData b, - double t, - ) { - return StreamGalleryHeaderThemeData( - closeButtonColor: Color.lerp(a.closeButtonColor, b.closeButtonColor, t), - backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t), - iconMenuPointColor: - Color.lerp(a.iconMenuPointColor, b.iconMenuPointColor, t), - titleTextStyle: TextStyle.lerp(a.titleTextStyle, b.titleTextStyle, t), - subtitleTextStyle: - TextStyle.lerp(a.subtitleTextStyle, b.subtitleTextStyle, t), - bottomSheetBarrierColor: - Color.lerp(a.bottomSheetBarrierColor, b.bottomSheetBarrierColor, t), - ); - } - - /// Merges one [StreamGalleryHeaderThemeData] with the another - StreamGalleryHeaderThemeData merge(StreamGalleryHeaderThemeData? other) { - if (other == null) return this; - return copyWith( - closeButtonColor: other.closeButtonColor, - backgroundColor: other.backgroundColor, - iconMenuPointColor: other.iconMenuPointColor, - titleTextStyle: other.titleTextStyle, - subtitleTextStyle: other.subtitleTextStyle, - bottomSheetBarrierColor: other.bottomSheetBarrierColor, - ); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StreamGalleryHeaderThemeData && - runtimeType == other.runtimeType && - closeButtonColor == other.closeButtonColor && - backgroundColor == other.backgroundColor && - iconMenuPointColor == other.iconMenuPointColor && - titleTextStyle == other.titleTextStyle && - subtitleTextStyle == other.subtitleTextStyle && - bottomSheetBarrierColor == other.bottomSheetBarrierColor; - - @override - int get hashCode => - closeButtonColor.hashCode ^ - backgroundColor.hashCode ^ - iconMenuPointColor.hashCode ^ - titleTextStyle.hashCode ^ - subtitleTextStyle.hashCode ^ - bottomSheetBarrierColor.hashCode; - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(ColorProperty('closeButtonColor', closeButtonColor)) - ..add(ColorProperty('backgroundColor', backgroundColor)) - ..add(ColorProperty('iconMenuPointColor', iconMenuPointColor)) - ..add(DiagnosticsProperty('titleTextStyle', titleTextStyle)) - ..add(DiagnosticsProperty('subtitleTextStyle', subtitleTextStyle)) - ..add(ColorProperty('bottomSheetBarrierColor', bottomSheetBarrierColor)); - } -} diff --git a/packages/stream_chat_flutter/lib/src/theme/message_input_theme.dart b/packages/stream_chat_flutter/lib/src/theme/message_input_theme.dart deleted file mode 100644 index 6e43fef838..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/message_input_theme.dart +++ /dev/null @@ -1,289 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template messageInputTheme} -/// Overrides the default style of [MessageInput] descendants. -/// -/// See also: -/// -/// * [StreamMessageInputThemeData], which is used to configure this theme. -/// {@endtemplate} -class StreamMessageInputTheme extends InheritedTheme { - /// Creates a [StreamMessageInputTheme]. - /// - /// The [data] parameter must not be null. - const StreamMessageInputTheme({ - super.key, - required this.data, - required super.child, - }); - - /// The configuration of this theme. - final StreamMessageInputThemeData data; - - /// The closest instance of this class that encloses the given context. - /// - /// If there is no enclosing [StreamMessageInputTheme] widget, then - /// [StreamChatThemeData.messageInputTheme] is used. - /// - /// Typical usage is as follows: - /// - /// ```dart - /// final theme = MessageInputTheme.of(context); - /// ``` - static StreamMessageInputThemeData of(BuildContext context) { - final messageInputTheme = - context.dependOnInheritedWidgetOfExactType(); - return messageInputTheme?.data ?? - StreamChatTheme.of(context).messageInputTheme; - } - - @override - Widget wrap(BuildContext context, Widget child) => - StreamMessageInputTheme(data: data, child: child); - - @override - bool updateShouldNotify(StreamMessageInputTheme oldWidget) => - data != oldWidget.data; -} - -/// {@template messageInputThemeData} -/// A style that overrides the default appearance of [MessageInput] widgets -/// when used with [StreamMessageInputTheme] -/// or with the overall [StreamChatTheme]'s -/// [StreamChatThemeData.messageInputTheme]. -/// {@endtemplate} -class StreamMessageInputThemeData with Diagnosticable { - /// Creates a [StreamMessageInputThemeData]. - const StreamMessageInputThemeData({ - this.sendAnimationDuration, - this.actionButtonColor, - this.sendButtonColor, - this.actionButtonIdleColor, - this.sendButtonIdleColor, - this.inputBackgroundColor, - this.inputTextStyle, - this.inputDecoration, - this.activeBorderGradient, - this.idleBorderGradient, - this.borderRadius, - this.expandButtonColor, - this.linkHighlightColor, - this.enableSafeArea, - this.elevation, - this.shadow, - }); - - /// Duration of the [MessageInput] send button animation - final Duration? sendAnimationDuration; - - /// Background color of [MessageInput] send button - final Color? sendButtonColor; - - /// Color of a link - final Color? linkHighlightColor; - - /// Background color of [MessageInput] action buttons - final Color? actionButtonColor; - - /// Background color of [MessageInput] send button - final Color? sendButtonIdleColor; - - /// Background color of [MessageInput] action buttons - final Color? actionButtonIdleColor; - - /// Background color of [MessageInput] expand button - final Color? expandButtonColor; - - /// Background color of [MessageInput] - final Color? inputBackgroundColor; - - /// TextStyle of [MessageInput] - final TextStyle? inputTextStyle; - - /// InputDecoration of [MessageInput] - final InputDecoration? inputDecoration; - - /// Border gradient when the [MessageInput] is not focused - final Gradient? idleBorderGradient; - - /// Border gradient when the [MessageInput] is focused - final Gradient? activeBorderGradient; - - /// Border radius of [MessageInput] - final BorderRadius? borderRadius; - - /// Wrap [MessageInput] with a [SafeArea widget] - final bool? enableSafeArea; - - /// Elevation of the [MessageInput] - final double? elevation; - - /// Shadow for the [MessageInput] widget - final BoxShadow? shadow; - - /// Returns a new [StreamMessageInputThemeData] - /// replacing some of its properties - StreamMessageInputThemeData copyWith({ - Duration? sendAnimationDuration, - Color? inputBackgroundColor, - Color? actionButtonColor, - Color? sendButtonColor, - Color? actionButtonIdleColor, - Color? linkHighlightColor, - Color? sendButtonIdleColor, - Color? expandButtonColor, - TextStyle? inputTextStyle, - InputDecoration? inputDecoration, - Gradient? activeBorderGradient, - Gradient? idleBorderGradient, - BorderRadius? borderRadius, - bool? enableSafeArea, - double? elevation, - BoxShadow? shadow, - }) { - return StreamMessageInputThemeData( - sendAnimationDuration: - sendAnimationDuration ?? this.sendAnimationDuration, - inputBackgroundColor: inputBackgroundColor ?? this.inputBackgroundColor, - actionButtonColor: actionButtonColor ?? this.actionButtonColor, - sendButtonColor: sendButtonColor ?? this.sendButtonColor, - actionButtonIdleColor: - actionButtonIdleColor ?? this.actionButtonIdleColor, - linkHighlightColor: linkHighlightColor ?? this.linkHighlightColor, - expandButtonColor: expandButtonColor ?? this.expandButtonColor, - inputTextStyle: inputTextStyle ?? this.inputTextStyle, - sendButtonIdleColor: sendButtonIdleColor ?? this.sendButtonIdleColor, - inputDecoration: inputDecoration ?? this.inputDecoration, - activeBorderGradient: activeBorderGradient ?? this.activeBorderGradient, - idleBorderGradient: idleBorderGradient ?? this.idleBorderGradient, - borderRadius: borderRadius ?? this.borderRadius, - enableSafeArea: enableSafeArea ?? this.enableSafeArea, - elevation: elevation ?? this.elevation, - shadow: shadow ?? this.shadow, - ); - } - - /// Linearly interpolate from one [StreamMessageInputThemeData] to another. - StreamMessageInputThemeData lerp( - StreamMessageInputThemeData a, - StreamMessageInputThemeData b, - double t, - ) { - return StreamMessageInputThemeData( - actionButtonColor: - Color.lerp(a.actionButtonColor, b.actionButtonColor, t), - actionButtonIdleColor: - Color.lerp(a.actionButtonIdleColor, b.actionButtonIdleColor, t), - activeBorderGradient: - Gradient.lerp(a.activeBorderGradient, b.activeBorderGradient, t), - borderRadius: BorderRadius.lerp(a.borderRadius, b.borderRadius, t), - expandButtonColor: - Color.lerp(a.expandButtonColor, b.expandButtonColor, t), - idleBorderGradient: - Gradient.lerp(a.idleBorderGradient, b.idleBorderGradient, t), - inputBackgroundColor: - Color.lerp(a.inputBackgroundColor, b.inputBackgroundColor, t), - inputTextStyle: TextStyle.lerp(a.inputTextStyle, b.inputTextStyle, t), - sendButtonColor: Color.lerp(a.sendButtonColor, b.sendButtonColor, t), - sendButtonIdleColor: - Color.lerp(a.sendButtonIdleColor, b.sendButtonIdleColor, t), - sendAnimationDuration: a.sendAnimationDuration, - inputDecoration: a.inputDecoration, - enableSafeArea: a.enableSafeArea, - elevation: lerpDouble(a.elevation, b.elevation, t), - shadow: BoxShadow.lerp(a.shadow, b.shadow, t), - ); - } - - /// Merges [this] [StreamMessageInputThemeData] with the [other] - StreamMessageInputThemeData merge(StreamMessageInputThemeData? other) { - if (other == null) return this; - return copyWith( - sendAnimationDuration: other.sendAnimationDuration, - inputBackgroundColor: other.inputBackgroundColor, - actionButtonColor: other.actionButtonColor, - actionButtonIdleColor: other.actionButtonIdleColor, - sendButtonColor: other.sendButtonColor, - sendButtonIdleColor: other.sendButtonIdleColor, - inputTextStyle: - inputTextStyle?.merge(other.inputTextStyle) ?? other.inputTextStyle, - inputDecoration: inputDecoration?.merge(other.inputDecoration) ?? - other.inputDecoration, - activeBorderGradient: other.activeBorderGradient, - idleBorderGradient: other.idleBorderGradient, - borderRadius: other.borderRadius, - expandButtonColor: other.expandButtonColor, - linkHighlightColor: other.linkHighlightColor, - enableSafeArea: other.enableSafeArea, - elevation: other.elevation, - shadow: other.shadow, - ); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StreamMessageInputThemeData && - runtimeType == other.runtimeType && - sendAnimationDuration == other.sendAnimationDuration && - sendButtonColor == other.sendButtonColor && - actionButtonColor == other.actionButtonColor && - sendButtonIdleColor == other.sendButtonIdleColor && - actionButtonIdleColor == other.actionButtonIdleColor && - expandButtonColor == other.expandButtonColor && - inputBackgroundColor == other.inputBackgroundColor && - inputTextStyle == other.inputTextStyle && - inputDecoration == other.inputDecoration && - idleBorderGradient == other.idleBorderGradient && - activeBorderGradient == other.activeBorderGradient && - borderRadius == other.borderRadius && - linkHighlightColor == other.linkHighlightColor && - enableSafeArea == other.enableSafeArea && - elevation == other.elevation && - shadow == other.shadow; - - @override - int get hashCode => - sendAnimationDuration.hashCode ^ - sendButtonColor.hashCode ^ - actionButtonColor.hashCode ^ - sendButtonIdleColor.hashCode ^ - actionButtonIdleColor.hashCode ^ - expandButtonColor.hashCode ^ - inputBackgroundColor.hashCode ^ - inputTextStyle.hashCode ^ - inputDecoration.hashCode ^ - idleBorderGradient.hashCode ^ - activeBorderGradient.hashCode ^ - borderRadius.hashCode ^ - linkHighlightColor.hashCode ^ - elevation.hashCode ^ - shadow.hashCode ^ - enableSafeArea.hashCode; - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty('sendAnimationDuration', sendAnimationDuration)) - ..add(ColorProperty('inputBackgroundColor', inputBackgroundColor)) - ..add(ColorProperty('actionButtonColor', actionButtonColor)) - ..add(ColorProperty('actionButtonIdleColor', actionButtonIdleColor)) - ..add(ColorProperty('sendButtonColor', sendButtonColor)) - ..add(ColorProperty('sendButtonIdleColor', sendButtonIdleColor)) - ..add(DiagnosticsProperty('inputTextStyle', inputTextStyle)) - ..add(DiagnosticsProperty('inputDecoration', inputDecoration)) - ..add(DiagnosticsProperty('activeBorderGradient', activeBorderGradient)) - ..add(DiagnosticsProperty('idleBorderGradient', idleBorderGradient)) - ..add(DiagnosticsProperty('borderRadius', borderRadius)) - ..add(ColorProperty('expandButtonColor', expandButtonColor)) - ..add(ColorProperty('linkHighlightColor', linkHighlightColor)) - ..add(DiagnosticsProperty('elevation', elevation)) - ..add(DiagnosticsProperty('shadow', shadow)) - ..add(DiagnosticsProperty('enableSafeArea', enableSafeArea)); - } -} diff --git a/packages/stream_chat_flutter/lib/src/theme/message_list_view_theme.dart b/packages/stream_chat_flutter/lib/src/theme/message_list_view_theme.dart deleted file mode 100644 index d21b3725e9..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/message_list_view_theme.dart +++ /dev/null @@ -1,136 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -/// {@template messageListViewTheme} -/// Overrides the default style of [MessageListView] descendants. -/// -/// See also: -/// -/// * [StreamMessageListViewThemeData], which is used to configure this theme. -/// {@endtemplate} -class StreamMessageListViewTheme extends InheritedTheme { - /// Creates a [StreamMessageListViewTheme]. - /// - /// The [data] parameter must not be null. - const StreamMessageListViewTheme({ - super.key, - required this.data, - required super.child, - }); - - /// The configuration of this theme. - final StreamMessageListViewThemeData data; - - /// The closest instance of this class that encloses the given context. - /// - /// If there is no enclosing [StreamMessageListViewTheme] widget, then - /// [StreamChatThemeData.messageListViewTheme] is used. - /// - /// Typical usage is as follows: - /// - /// ```dart - /// MessageListViewTheme theme = MessageListViewTheme.of(context); - /// ``` - static StreamMessageListViewThemeData of(BuildContext context) { - final messageListViewTheme = context - .dependOnInheritedWidgetOfExactType(); - return messageListViewTheme?.data ?? - StreamChatTheme.of(context).messageListViewTheme; - } - - @override - Widget wrap(BuildContext context, Widget child) => - StreamMessageListViewTheme(data: data, child: child); - - @override - bool updateShouldNotify(StreamMessageListViewTheme oldWidget) => - data != oldWidget.data; -} - -/// {@template messageListViewThemeData} -/// A style that overrides the default appearance of [MessageListView]s when -/// used with [StreamMessageListViewTheme] or with -/// the overall [StreamChatTheme]'s -/// [StreamChatThemeData.messageListViewTheme]. -/// -/// See also: -/// -/// * [StreamMessageListViewTheme], the theme -/// which is configured with this class. -/// * [StreamChatThemeData.messageListViewTheme], which can be used to override -/// the default style for [MessageListView]s below the overall -/// [StreamChatTheme]. -/// {@endtemplate} -class StreamMessageListViewThemeData with Diagnosticable { - /// Creates a [StreamMessageListViewThemeData]. - const StreamMessageListViewThemeData({ - this.backgroundColor, - this.backgroundImage, - }); - - /// The color of the [MessageListView] background. - final Color? backgroundColor; - - /// The image of the [MessageListView] background. - final DecorationImage? backgroundImage; - - /// Copies this [StreamMessageListViewThemeData] to another. - StreamMessageListViewThemeData copyWith({ - Color? backgroundColor, - DecorationImage? backgroundImage, - }) { - return StreamMessageListViewThemeData( - backgroundColor: backgroundColor ?? this.backgroundColor, - backgroundImage: backgroundImage ?? this.backgroundImage, - ); - } - - /// Linearly interpolate between two [MessageListView] themes. - /// - /// All the properties must be non-null. - StreamMessageListViewThemeData lerp( - StreamMessageListViewThemeData a, - StreamMessageListViewThemeData b, - double t, - ) { - return StreamMessageListViewThemeData( - backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t), - backgroundImage: t < 0.5 ? a.backgroundImage : b.backgroundImage, - ); - } - - /// Merges one [StreamMessageListViewThemeData] with another. - StreamMessageListViewThemeData merge(StreamMessageListViewThemeData? other) { - if (other == null) return this; - return copyWith( - backgroundColor: other.backgroundColor, - backgroundImage: other.backgroundImage, - ); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StreamMessageListViewThemeData && - runtimeType == other.runtimeType && - backgroundColor == other.backgroundColor && - backgroundImage == other.backgroundImage; - - @override - int get hashCode => backgroundColor.hashCode + backgroundImage.hashCode; - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(ColorProperty('backgroundColor', backgroundColor)) - ..add( - DiagnosticsProperty( - 'backgroundImage', - backgroundImage, - defaultValue: null, - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/theme/message_theme.dart b/packages/stream_chat_flutter/lib/src/theme/message_theme.dart deleted file mode 100644 index 32b2fa9d36..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/message_theme.dart +++ /dev/null @@ -1,305 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/theme/avatar_theme.dart'; - -/// {@template message_theme_data} -/// Class for getting message theme -/// {@endtemplate} -// ignore: prefer-match-file-name -class StreamMessageThemeData with Diagnosticable { - /// Creates a [StreamMessageThemeData]. - const StreamMessageThemeData({ - this.repliesStyle, - this.messageTextStyle, - this.messageAuthorStyle, - this.messageLinksStyle, - this.messageBackgroundColor, - this.messageBorderColor, - this.reactionsBackgroundColor, - this.reactionsBorderColor, - this.reactionsMaskColor, - this.avatarTheme, - this.createdAtStyle, - this.urlAttachmentBackgroundColor, - this.urlAttachmentHostStyle, - this.urlAttachmentTitleStyle, - this.urlAttachmentTextStyle, - this.urlAttachmentTitleMaxLine, - this.urlAttachmentTextMaxLine, - }); - - /// Text style for message text - final TextStyle? messageTextStyle; - - /// Text style for message author - final TextStyle? messageAuthorStyle; - - /// Text style for message links - final TextStyle? messageLinksStyle; - - /// Text style for created at text - final TextStyle? createdAtStyle; - - /// Text style for replies - final TextStyle? repliesStyle; - - /// Color for messageBackgroundColor - final Color? messageBackgroundColor; - - /// Color for message border color - final Color? messageBorderColor; - - /// Color for reactions - final Color? reactionsBackgroundColor; - - /// Colors reaction border - final Color? reactionsBorderColor; - - /// Color for reaction mask - final Color? reactionsMaskColor; - - /// Theme of the avatar - final StreamAvatarThemeData? avatarTheme; - - /// Background color for messages with url attachments. - final Color? urlAttachmentBackgroundColor; - - /// Color for url attachment host. - final TextStyle? urlAttachmentHostStyle; - - /// Color for url attachment title. - final TextStyle? urlAttachmentTitleStyle; - - /// Color for url attachment text. - final TextStyle? urlAttachmentTextStyle; - - /// Max number of lines in Url link title. - final int? urlAttachmentTitleMaxLine; - - /// Max number of lines in Url link text. - final int? urlAttachmentTextMaxLine; - - /// Copy with a theme - StreamMessageThemeData copyWith({ - TextStyle? messageTextStyle, - TextStyle? messageAuthorStyle, - TextStyle? messageLinksStyle, - TextStyle? createdAtStyle, - TextStyle? repliesStyle, - Color? messageBackgroundColor, - Color? messageBorderColor, - StreamAvatarThemeData? avatarTheme, - Color? reactionsBackgroundColor, - Color? reactionsBorderColor, - Color? reactionsMaskColor, - Color? urlAttachmentBackgroundColor, - TextStyle? urlAttachmentHostStyle, - TextStyle? urlAttachmentTitleStyle, - TextStyle? urlAttachmentTextStyle, - int? urlAttachmentTitleMaxLine, - int? urlAttachmentTextMaxLine, - }) { - return StreamMessageThemeData( - messageTextStyle: messageTextStyle ?? this.messageTextStyle, - messageAuthorStyle: messageAuthorStyle ?? this.messageAuthorStyle, - messageLinksStyle: messageLinksStyle ?? this.messageLinksStyle, - createdAtStyle: createdAtStyle ?? this.createdAtStyle, - messageBackgroundColor: - messageBackgroundColor ?? this.messageBackgroundColor, - messageBorderColor: messageBorderColor ?? this.messageBorderColor, - avatarTheme: avatarTheme ?? this.avatarTheme, - repliesStyle: repliesStyle ?? this.repliesStyle, - reactionsBackgroundColor: - reactionsBackgroundColor ?? this.reactionsBackgroundColor, - reactionsBorderColor: reactionsBorderColor ?? this.reactionsBorderColor, - reactionsMaskColor: reactionsMaskColor ?? this.reactionsMaskColor, - urlAttachmentBackgroundColor: - urlAttachmentBackgroundColor ?? this.urlAttachmentBackgroundColor, - urlAttachmentHostStyle: - urlAttachmentHostStyle ?? this.urlAttachmentHostStyle, - urlAttachmentTitleStyle: - urlAttachmentTitleStyle ?? this.urlAttachmentTitleStyle, - urlAttachmentTextStyle: - urlAttachmentTextStyle ?? this.urlAttachmentTextStyle, - urlAttachmentTitleMaxLine: - urlAttachmentTitleMaxLine ?? this.urlAttachmentTitleMaxLine, - urlAttachmentTextMaxLine: - urlAttachmentTextMaxLine ?? this.urlAttachmentTextMaxLine, - ); - } - - /// Linearly interpolate from one [StreamMessageThemeData] to another. - StreamMessageThemeData lerp( - StreamMessageThemeData a, - StreamMessageThemeData b, - double t, - ) { - return StreamMessageThemeData( - avatarTheme: - const StreamAvatarThemeData().lerp(a.avatarTheme!, b.avatarTheme!, t), - createdAtStyle: TextStyle.lerp(a.createdAtStyle, b.createdAtStyle, t), - messageAuthorStyle: - TextStyle.lerp(a.messageAuthorStyle, b.messageAuthorStyle, t), - messageBackgroundColor: - Color.lerp(a.messageBackgroundColor, b.messageBackgroundColor, t), - messageBorderColor: - Color.lerp(a.messageBorderColor, b.messageBorderColor, t), - messageLinksStyle: - TextStyle.lerp(a.messageLinksStyle, b.messageLinksStyle, t), - messageTextStyle: - TextStyle.lerp(a.messageTextStyle, b.messageTextStyle, t), - reactionsBackgroundColor: Color.lerp( - a.reactionsBackgroundColor, - b.reactionsBackgroundColor, - t, - ), - reactionsBorderColor: - Color.lerp(a.messageBorderColor, b.reactionsBorderColor, t), - reactionsMaskColor: - Color.lerp(a.reactionsMaskColor, b.reactionsMaskColor, t), - repliesStyle: TextStyle.lerp(a.repliesStyle, b.repliesStyle, t), - urlAttachmentBackgroundColor: Color.lerp( - a.urlAttachmentBackgroundColor, - b.urlAttachmentBackgroundColor, - t, - ), - urlAttachmentHostStyle: - TextStyle.lerp(a.urlAttachmentHostStyle, b.urlAttachmentHostStyle, t), - urlAttachmentTextStyle: TextStyle.lerp( - a.urlAttachmentTextStyle, - b.urlAttachmentTextStyle, - t, - ), - urlAttachmentTitleStyle: TextStyle.lerp( - a.urlAttachmentTitleStyle, - b.urlAttachmentTitleStyle, - t, - ), - urlAttachmentTitleMaxLine: lerpDouble( - a.urlAttachmentTitleMaxLine, - b.urlAttachmentTitleMaxLine, - t, - )?.round(), - urlAttachmentTextMaxLine: lerpDouble( - a.urlAttachmentTextMaxLine, - b.urlAttachmentTextMaxLine, - t, - )?.round(), - ); - } - - /// Merge with a theme - StreamMessageThemeData merge(StreamMessageThemeData? other) { - if (other == null) return this; - return copyWith( - messageTextStyle: messageTextStyle?.merge(other.messageTextStyle) ?? - other.messageTextStyle, - messageAuthorStyle: messageAuthorStyle?.merge(other.messageAuthorStyle) ?? - other.messageAuthorStyle, - messageLinksStyle: messageLinksStyle?.merge(other.messageLinksStyle) ?? - other.messageLinksStyle, - createdAtStyle: - createdAtStyle?.merge(other.createdAtStyle) ?? other.createdAtStyle, - repliesStyle: - repliesStyle?.merge(other.repliesStyle) ?? other.repliesStyle, - messageBackgroundColor: other.messageBackgroundColor, - messageBorderColor: other.messageBorderColor, - avatarTheme: avatarTheme?.merge(other.avatarTheme) ?? other.avatarTheme, - reactionsBackgroundColor: other.reactionsBackgroundColor, - reactionsBorderColor: other.reactionsBorderColor, - reactionsMaskColor: other.reactionsMaskColor, - urlAttachmentBackgroundColor: other.urlAttachmentBackgroundColor, - urlAttachmentHostStyle: other.urlAttachmentHostStyle, - urlAttachmentTitleStyle: other.urlAttachmentTitleStyle, - urlAttachmentTextStyle: other.urlAttachmentTextStyle, - urlAttachmentTitleMaxLine: other.urlAttachmentTitleMaxLine, - urlAttachmentTextMaxLine: other.urlAttachmentTextMaxLine, - ); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StreamMessageThemeData && - runtimeType == other.runtimeType && - messageTextStyle == other.messageTextStyle && - messageAuthorStyle == other.messageAuthorStyle && - messageLinksStyle == other.messageLinksStyle && - createdAtStyle == other.createdAtStyle && - repliesStyle == other.repliesStyle && - messageBackgroundColor == other.messageBackgroundColor && - messageBorderColor == other.messageBorderColor && - reactionsBackgroundColor == other.reactionsBackgroundColor && - reactionsBorderColor == other.reactionsBorderColor && - reactionsMaskColor == other.reactionsMaskColor && - avatarTheme == other.avatarTheme && - urlAttachmentBackgroundColor == other.urlAttachmentBackgroundColor && - urlAttachmentHostStyle == other.urlAttachmentHostStyle && - urlAttachmentTitleStyle == other.urlAttachmentTitleStyle && - urlAttachmentTextStyle == other.urlAttachmentTextStyle && - urlAttachmentTitleMaxLine == other.urlAttachmentTitleMaxLine && - urlAttachmentTextMaxLine == other.urlAttachmentTextMaxLine; - - @override - int get hashCode => - messageTextStyle.hashCode ^ - messageAuthorStyle.hashCode ^ - messageLinksStyle.hashCode ^ - createdAtStyle.hashCode ^ - repliesStyle.hashCode ^ - messageBackgroundColor.hashCode ^ - messageBorderColor.hashCode ^ - reactionsBackgroundColor.hashCode ^ - reactionsBorderColor.hashCode ^ - reactionsMaskColor.hashCode ^ - avatarTheme.hashCode ^ - urlAttachmentBackgroundColor.hashCode ^ - urlAttachmentHostStyle.hashCode ^ - urlAttachmentTitleStyle.hashCode ^ - urlAttachmentTextStyle.hashCode ^ - urlAttachmentTitleMaxLine.hashCode ^ - urlAttachmentTextMaxLine.hashCode; - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty('messageTextStyle', messageTextStyle)) - ..add(DiagnosticsProperty('messageAuthorStyle', messageAuthorStyle)) - ..add(DiagnosticsProperty('messageLinksStyle', messageLinksStyle)) - ..add(DiagnosticsProperty('createdAtStyle', createdAtStyle)) - ..add(DiagnosticsProperty('repliesStyle', repliesStyle)) - ..add(ColorProperty('messageBackgroundColor', messageBackgroundColor)) - ..add(ColorProperty('messageBorderColor', messageBorderColor)) - ..add(DiagnosticsProperty('avatarTheme', avatarTheme)) - ..add(ColorProperty('reactionsBackgroundColor', reactionsBackgroundColor)) - ..add(ColorProperty('reactionsBorderColor', reactionsBorderColor)) - ..add(ColorProperty('reactionsMaskColor', reactionsMaskColor)) - ..add(ColorProperty( - 'urlAttachmentBackgroundColor', - urlAttachmentBackgroundColor, - )) - ..add(DiagnosticsProperty( - 'urlAttachmentHostStyle', - urlAttachmentHostStyle, - )) - ..add(DiagnosticsProperty( - 'urlAttachmentTitleStyle', - urlAttachmentTitleStyle, - )) - ..add(DiagnosticsProperty( - 'urlAttachmentTextStyle', - urlAttachmentTextStyle, - )) - ..add(DiagnosticsProperty( - 'urlAttachmentTitleMaxLine', - urlAttachmentTitleMaxLine, - )) - ..add(DiagnosticsProperty( - 'urlAttachmentTextMaxLine', - urlAttachmentTextMaxLine, - )); - } -} diff --git a/packages/stream_chat_flutter/lib/src/theme/poll_comments_dialog_theme.dart b/packages/stream_chat_flutter/lib/src/theme/poll_comments_dialog_theme.dart deleted file mode 100644 index 9eb5f530f7..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/poll_comments_dialog_theme.dart +++ /dev/null @@ -1,179 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -/// {@template streamPollCommentsDialogTheme} -/// Overrides the default style of [StreamPollCommentsDialog] descendants. -/// -/// See also: -/// -/// * [StreamPollCommentsDialogThemeData], which is used to configure this -/// theme. -/// {@endtemplate} -class StreamPollCommentsDialogTheme extends InheritedTheme { - /// Creates a [StreamPollCommentsDialogTheme]. - /// - /// The [data] parameter must not be null. - const StreamPollCommentsDialogTheme({ - super.key, - required this.data, - required super.child, - }); - - /// The configuration of this theme. - final StreamPollCommentsDialogThemeData data; - - /// The closest instance of this class that encloses the given context. - /// - /// If there is no enclosing [StreamPollCommentsDialogTheme] widget, then - /// [StreamChatThemeData.pollCommentsDialogTheme] is used. - static StreamPollCommentsDialogThemeData of(BuildContext context) { - final pollCommentsDialogTheme = context - .dependOnInheritedWidgetOfExactType(); - return pollCommentsDialogTheme?.data ?? - StreamChatTheme.of(context).pollCommentsDialogTheme; - } - - @override - Widget wrap(BuildContext context, Widget child) => - StreamPollCommentsDialogTheme(data: data, child: child); - - @override - bool updateShouldNotify(StreamPollCommentsDialogTheme oldWidget) => - data != oldWidget.data; -} - -/// {@template streamPollCommentsDialogThemeData} -/// A style that overrides the default appearance of [StreamPollCommentsDialog] -/// widgets when used with [StreamPollCommentsDialogTheme] or with the overall -/// [StreamChatTheme]'s [StreamChatThemeData.pollCommentsDialogTheme]. -/// {@endtemplate} -class StreamPollCommentsDialogThemeData with Diagnosticable { - /// {@macro streamPollCommentsDialogThemeData} - const StreamPollCommentsDialogThemeData({ - this.backgroundColor, - this.appBarElevation, - this.appBarBackgroundColor, - this.appBarTitleTextStyle, - this.pollCommentItemBackgroundColor, - this.pollCommentItemBorderRadius, - this.updateYourCommentButtonStyle, - }); - - /// The background color of the dialog. - final Color? backgroundColor; - - /// The elevation of the app bar. - final double? appBarElevation; - - /// The background color of the app bar. - final Color? appBarBackgroundColor; - - /// The text style of the app bar title. - final TextStyle? appBarTitleTextStyle; - - /// The background color of the poll comment item. - final Color? pollCommentItemBackgroundColor; - - /// The border radius of the poll comment item. - final BorderRadius? pollCommentItemBorderRadius; - - /// The button style for the update your comment button. - final ButtonStyle? updateYourCommentButtonStyle; - - /// Copies this [StreamPollCommentsDialogThemeData] with some new values. - StreamPollCommentsDialogThemeData copyWith({ - Color? backgroundColor, - double? appBarElevation, - Color? appBarBackgroundColor, - TextStyle? appBarTitleTextStyle, - Color? pollCommentItemBackgroundColor, - BorderRadius? pollCommentItemBorderRadius, - ButtonStyle? updateYourCommentButtonStyle, - }) => - StreamPollCommentsDialogThemeData( - backgroundColor: backgroundColor ?? this.backgroundColor, - appBarElevation: appBarElevation ?? this.appBarElevation, - appBarBackgroundColor: - appBarBackgroundColor ?? this.appBarBackgroundColor, - appBarTitleTextStyle: appBarTitleTextStyle ?? this.appBarTitleTextStyle, - pollCommentItemBackgroundColor: pollCommentItemBackgroundColor ?? - this.pollCommentItemBackgroundColor, - pollCommentItemBorderRadius: - pollCommentItemBorderRadius ?? this.pollCommentItemBorderRadius, - updateYourCommentButtonStyle: - updateYourCommentButtonStyle ?? this.updateYourCommentButtonStyle, - ); - - /// Merges this [StreamPollCommentsDialogThemeData] with the [other]. - StreamPollCommentsDialogThemeData merge( - StreamPollCommentsDialogThemeData? other, - ) { - if (other == null) return this; - return copyWith( - backgroundColor: other.backgroundColor, - appBarElevation: other.appBarElevation, - appBarBackgroundColor: other.appBarBackgroundColor, - appBarTitleTextStyle: other.appBarTitleTextStyle, - pollCommentItemBackgroundColor: other.pollCommentItemBackgroundColor, - pollCommentItemBorderRadius: other.pollCommentItemBorderRadius, - updateYourCommentButtonStyle: other.updateYourCommentButtonStyle, - ); - } - - /// Linearly interpolate between two [StreamPollCommentsDialogThemeData]. - StreamPollCommentsDialogThemeData lerp( - StreamPollCommentsDialogThemeData? a, - StreamPollCommentsDialogThemeData? b, - double t, - ) { - return StreamPollCommentsDialogThemeData( - backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t), - appBarElevation: lerpDouble(a?.appBarElevation, b?.appBarElevation, t), - appBarBackgroundColor: - Color.lerp(a?.appBarBackgroundColor, b?.appBarBackgroundColor, t), - appBarTitleTextStyle: - TextStyle.lerp(a?.appBarTitleTextStyle, b?.appBarTitleTextStyle, t), - pollCommentItemBackgroundColor: Color.lerp( - a?.pollCommentItemBackgroundColor, - b?.pollCommentItemBackgroundColor, - t, - ), - pollCommentItemBorderRadius: BorderRadius.lerp( - a?.pollCommentItemBorderRadius, - b?.pollCommentItemBorderRadius, - t, - ), - updateYourCommentButtonStyle: ButtonStyle.lerp( - a?.updateYourCommentButtonStyle, - b?.updateYourCommentButtonStyle, - t, - ), - ); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StreamPollCommentsDialogThemeData && - other.backgroundColor == backgroundColor && - other.appBarElevation == appBarElevation && - other.appBarBackgroundColor == appBarBackgroundColor && - other.appBarTitleTextStyle == appBarTitleTextStyle && - other.pollCommentItemBackgroundColor == - pollCommentItemBackgroundColor && - other.pollCommentItemBorderRadius == pollCommentItemBorderRadius && - other.updateYourCommentButtonStyle == updateYourCommentButtonStyle; - - @override - int get hashCode => - backgroundColor.hashCode ^ - appBarElevation.hashCode ^ - appBarBackgroundColor.hashCode ^ - appBarTitleTextStyle.hashCode ^ - pollCommentItemBackgroundColor.hashCode ^ - pollCommentItemBorderRadius.hashCode ^ - updateYourCommentButtonStyle.hashCode; -} diff --git a/packages/stream_chat_flutter/lib/src/theme/poll_creator_theme.dart b/packages/stream_chat_flutter/lib/src/theme/poll_creator_theme.dart deleted file mode 100644 index 7f128acbaf..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/poll_creator_theme.dart +++ /dev/null @@ -1,318 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -/// {@template streamPollCreatorTheme} -/// Overrides the default style of [StreamPollCreatorWidget] descendants. -/// -/// See also: -/// -/// * [StreamPollCreatorThemeData], which is used to configure this theme. -/// {@endtemplate} -class StreamPollCreatorTheme extends InheritedTheme { - /// Creates a [StreamPollCreatorTheme]. - /// - /// The [data] parameter must not be null. - const StreamPollCreatorTheme({ - super.key, - required this.data, - required super.child, - }); - - /// The configuration of this theme. - final StreamPollCreatorThemeData data; - - /// The closest instance of this class that encloses the given context. - /// - /// If there is no enclosing [StreamPollCreatorTheme] widget, then - /// [StreamChatThemeData.pollCreatorTheme] is used. - /// - /// Typical usage is as follows: - /// - /// ```dart - /// StreamPollCreatorTheme theme = StreamPollCreatorTheme.of(context); - /// ``` - static StreamPollCreatorThemeData of(BuildContext context) { - final pollCreatorTheme = - context.dependOnInheritedWidgetOfExactType(); - return pollCreatorTheme?.data ?? - StreamChatTheme.of(context).pollCreatorTheme; - } - - @override - Widget wrap(BuildContext context, Widget child) => - StreamPollCreatorTheme(data: data, child: child); - - @override - bool updateShouldNotify(StreamPollCreatorTheme oldWidget) => - data != oldWidget.data; -} - -/// {@template streamPollCreatorThemeData} -/// A style that overrides the default appearance of [StreamPollCreator] widget -/// when used with [StreamPollCreatorTheme] or with the overall -/// [StreamChatTheme]'s [StreamChatThemeData.pollCreatorTheme]. -/// {@endtemplate} -class StreamPollCreatorThemeData with Diagnosticable { - /// {@macro streamPollCreatorThemeData} - const StreamPollCreatorThemeData({ - this.backgroundColor, - this.appBarTitleStyle, - this.appBarElevation, - this.appBarBackgroundColor, - this.questionTextFieldFillColor, - this.questionHeaderStyle, - this.questionTextFieldStyle, - this.questionTextFieldErrorStyle, - this.questionTextFieldBorderRadius, - this.optionsHeaderStyle, - this.optionsTextFieldStyle, - this.optionsTextFieldFillColor, - this.optionsTextFieldErrorStyle, - this.optionsTextFieldBorderRadius, - this.switchListTileFillColor, - this.switchListTileTitleStyle, - this.switchListTileErrorStyle, - this.switchListTileBorderRadius, - }); - - /// The background color of the poll creator. - final Color? backgroundColor; - - /// The text style of the appBar title. - final TextStyle? appBarTitleStyle; - - /// The elevation of the appBar. - final double? appBarElevation; - - /// The background color of the appBar. - final Color? appBarBackgroundColor; - - /// The fill color of the question text field. - final Color? questionTextFieldFillColor; - - /// The style of the question header text. - final TextStyle? questionHeaderStyle; - - /// The text style of the question text field. - final TextStyle? questionTextFieldStyle; - - /// The text style of the question error text when the question is invalid. - final TextStyle? questionTextFieldErrorStyle; - - /// The border radius of the question text field. - final BorderRadius? questionTextFieldBorderRadius; - - /// The fill color of the options text field. - final Color? optionsTextFieldFillColor; - - /// The style of the options header text. - final TextStyle? optionsHeaderStyle; - - /// The text style of the options text field. - final TextStyle? optionsTextFieldStyle; - - /// The text style of the options error text when the options are invalid. - final TextStyle? optionsTextFieldErrorStyle; - - /// The border radius of the options text field. - final BorderRadius? optionsTextFieldBorderRadius; - - /// The fill color of the switch list tile. - final Color? switchListTileFillColor; - - /// The text style of the switch list tile title. - final TextStyle? switchListTileTitleStyle; - - /// The text style of the switch list tile error text when the switch list - /// tile is invalid. - final TextStyle? switchListTileErrorStyle; - - /// The border radius of the switch list tile. - final BorderRadius? switchListTileBorderRadius; - - /// Copies this [StreamPollCreatorThemeData] with some new values. - StreamPollCreatorThemeData copyWith({ - Color? backgroundColor, - TextStyle? appBarTitleStyle, - double? appBarElevation, - Color? appBarBackgroundColor, - Color? questionTextFieldFillColor, - TextStyle? questionHeaderStyle, - TextStyle? questionTextFieldStyle, - TextStyle? questionTextFieldErrorStyle, - BorderRadius? questionTextFieldBorderRadius, - Color? optionsTextFieldFillColor, - TextStyle? optionsHeaderStyle, - TextStyle? optionsTextFieldStyle, - TextStyle? optionsTextFieldErrorStyle, - BorderRadius? optionsTextFieldBorderRadius, - Color? switchListTileFillColor, - TextStyle? switchListTileTitleStyle, - TextStyle? switchListTileErrorStyle, - BorderRadius? switchListTileBorderRadius, - }) { - return StreamPollCreatorThemeData( - backgroundColor: backgroundColor ?? this.backgroundColor, - appBarTitleStyle: appBarTitleStyle ?? this.appBarTitleStyle, - appBarElevation: appBarElevation ?? this.appBarElevation, - appBarBackgroundColor: - appBarBackgroundColor ?? this.appBarBackgroundColor, - questionTextFieldFillColor: - questionTextFieldFillColor ?? this.questionTextFieldFillColor, - questionHeaderStyle: questionHeaderStyle ?? this.questionHeaderStyle, - questionTextFieldStyle: - questionTextFieldStyle ?? this.questionTextFieldStyle, - questionTextFieldErrorStyle: - questionTextFieldErrorStyle ?? this.questionTextFieldErrorStyle, - questionTextFieldBorderRadius: - questionTextFieldBorderRadius ?? this.questionTextFieldBorderRadius, - optionsTextFieldFillColor: - optionsTextFieldFillColor ?? this.optionsTextFieldFillColor, - optionsHeaderStyle: optionsHeaderStyle ?? this.optionsHeaderStyle, - optionsTextFieldStyle: - optionsTextFieldStyle ?? this.optionsTextFieldStyle, - optionsTextFieldErrorStyle: - optionsTextFieldErrorStyle ?? this.optionsTextFieldErrorStyle, - optionsTextFieldBorderRadius: - optionsTextFieldBorderRadius ?? this.optionsTextFieldBorderRadius, - switchListTileFillColor: - switchListTileFillColor ?? this.switchListTileFillColor, - switchListTileTitleStyle: - switchListTileTitleStyle ?? this.switchListTileTitleStyle, - switchListTileErrorStyle: - switchListTileErrorStyle ?? this.switchListTileErrorStyle, - switchListTileBorderRadius: - switchListTileBorderRadius ?? this.switchListTileBorderRadius, - ); - } - - /// Merges [this] [StreamPollCreatorThemeData] with the [other] - StreamPollCreatorThemeData merge(StreamPollCreatorThemeData? other) { - if (other == null) return this; - return copyWith( - backgroundColor: other.backgroundColor ?? backgroundColor, - appBarTitleStyle: other.appBarTitleStyle ?? appBarTitleStyle, - appBarElevation: other.appBarElevation ?? appBarElevation, - appBarBackgroundColor: - other.appBarBackgroundColor ?? appBarBackgroundColor, - questionTextFieldFillColor: - other.questionTextFieldFillColor ?? questionTextFieldFillColor, - questionHeaderStyle: other.questionHeaderStyle ?? questionHeaderStyle, - questionTextFieldStyle: - other.questionTextFieldStyle ?? questionTextFieldStyle, - questionTextFieldErrorStyle: - other.questionTextFieldErrorStyle ?? questionTextFieldErrorStyle, - questionTextFieldBorderRadius: - other.questionTextFieldBorderRadius ?? questionTextFieldBorderRadius, - optionsTextFieldFillColor: - other.optionsTextFieldFillColor ?? optionsTextFieldFillColor, - optionsHeaderStyle: other.optionsHeaderStyle ?? optionsHeaderStyle, - optionsTextFieldStyle: - other.optionsTextFieldStyle ?? optionsTextFieldStyle, - optionsTextFieldErrorStyle: - other.optionsTextFieldErrorStyle ?? optionsTextFieldErrorStyle, - optionsTextFieldBorderRadius: - other.optionsTextFieldBorderRadius ?? optionsTextFieldBorderRadius, - switchListTileFillColor: - other.switchListTileFillColor ?? switchListTileFillColor, - switchListTileTitleStyle: - other.switchListTileTitleStyle ?? switchListTileTitleStyle, - switchListTileErrorStyle: - other.switchListTileErrorStyle ?? switchListTileErrorStyle, - switchListTileBorderRadius: - other.switchListTileBorderRadius ?? switchListTileBorderRadius, - ); - } - - /// Linearly interpolate between two [StreamPollCreatorThemeData]. - StreamPollCreatorThemeData lerp( - StreamPollCreatorThemeData a, - StreamPollCreatorThemeData b, - double t, - ) { - return StreamPollCreatorThemeData( - backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t), - appBarTitleStyle: - TextStyle.lerp(a.appBarTitleStyle, b.appBarTitleStyle, t), - appBarElevation: lerpDouble(a.appBarElevation, b.appBarElevation, t), - appBarBackgroundColor: - Color.lerp(a.appBarBackgroundColor, b.appBarBackgroundColor, t), - questionTextFieldFillColor: Color.lerp( - a.questionTextFieldFillColor, b.questionTextFieldFillColor, t), - questionHeaderStyle: - TextStyle.lerp(a.questionHeaderStyle, b.questionHeaderStyle, t), - questionTextFieldStyle: - TextStyle.lerp(a.questionTextFieldStyle, b.questionTextFieldStyle, t), - questionTextFieldErrorStyle: TextStyle.lerp( - a.questionTextFieldErrorStyle, b.questionTextFieldErrorStyle, t), - questionTextFieldBorderRadius: BorderRadius.lerp( - a.questionTextFieldBorderRadius, b.questionTextFieldBorderRadius, t), - optionsTextFieldFillColor: Color.lerp( - a.optionsTextFieldFillColor, b.optionsTextFieldFillColor, t), - optionsHeaderStyle: - TextStyle.lerp(a.optionsHeaderStyle, b.optionsHeaderStyle, t), - optionsTextFieldStyle: - TextStyle.lerp(a.optionsTextFieldStyle, b.optionsTextFieldStyle, t), - optionsTextFieldErrorStyle: TextStyle.lerp( - a.optionsTextFieldErrorStyle, b.optionsTextFieldErrorStyle, t), - optionsTextFieldBorderRadius: BorderRadius.lerp( - a.optionsTextFieldBorderRadius, b.optionsTextFieldBorderRadius, t), - switchListTileFillColor: - Color.lerp(a.switchListTileFillColor, b.switchListTileFillColor, t), - switchListTileTitleStyle: TextStyle.lerp( - a.switchListTileTitleStyle, b.switchListTileTitleStyle, t), - switchListTileErrorStyle: TextStyle.lerp( - a.switchListTileErrorStyle, b.switchListTileErrorStyle, t), - switchListTileBorderRadius: BorderRadius.lerp( - a.switchListTileBorderRadius, b.switchListTileBorderRadius, t), - ); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StreamPollCreatorThemeData && - other.backgroundColor == backgroundColor && - other.appBarTitleStyle == appBarTitleStyle && - other.appBarElevation == appBarElevation && - other.appBarBackgroundColor == appBarBackgroundColor && - other.questionTextFieldFillColor == questionTextFieldFillColor && - other.questionHeaderStyle == questionHeaderStyle && - other.questionTextFieldStyle == questionTextFieldStyle && - other.questionTextFieldErrorStyle == questionTextFieldErrorStyle && - other.questionTextFieldBorderRadius == - questionTextFieldBorderRadius && - other.optionsTextFieldFillColor == optionsTextFieldFillColor && - other.optionsHeaderStyle == optionsHeaderStyle && - other.optionsTextFieldStyle == optionsTextFieldStyle && - other.optionsTextFieldErrorStyle == optionsTextFieldErrorStyle && - other.optionsTextFieldBorderRadius == optionsTextFieldBorderRadius && - other.switchListTileFillColor == switchListTileFillColor && - other.switchListTileTitleStyle == switchListTileTitleStyle && - other.switchListTileErrorStyle == switchListTileErrorStyle && - other.switchListTileBorderRadius == switchListTileBorderRadius; - - @override - int get hashCode => - backgroundColor.hashCode ^ - appBarTitleStyle.hashCode ^ - appBarElevation.hashCode ^ - appBarBackgroundColor.hashCode ^ - questionTextFieldFillColor.hashCode ^ - questionHeaderStyle.hashCode ^ - questionTextFieldStyle.hashCode ^ - questionTextFieldErrorStyle.hashCode ^ - questionTextFieldBorderRadius.hashCode ^ - optionsTextFieldFillColor.hashCode ^ - optionsHeaderStyle.hashCode ^ - optionsTextFieldStyle.hashCode ^ - optionsTextFieldErrorStyle.hashCode ^ - optionsTextFieldBorderRadius.hashCode ^ - switchListTileFillColor.hashCode ^ - switchListTileTitleStyle.hashCode ^ - switchListTileErrorStyle.hashCode ^ - switchListTileBorderRadius.hashCode; -} diff --git a/packages/stream_chat_flutter/lib/src/theme/poll_interactor_theme.dart b/packages/stream_chat_flutter/lib/src/theme/poll_interactor_theme.dart deleted file mode 100644 index 1880ad5ce7..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/poll_interactor_theme.dart +++ /dev/null @@ -1,377 +0,0 @@ -// ignore_for_file: parameter_assignments - -import 'dart:ui'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -/// {@template streamPollInteractorTheme} -/// Overrides the default style of [StreamPollInteractorWidget] descendants. -/// -/// See also: -/// -/// * [StreamPollInteractorThemeData], which is used to configure this theme. -/// {@endtemplate} -class StreamPollInteractorTheme extends InheritedTheme { - /// Creates a [StreamPollInteractorTheme]. - /// - /// The [data] parameter must not be null. - const StreamPollInteractorTheme({ - super.key, - required this.data, - required super.child, - }); - - /// The configuration of this theme. - final StreamPollInteractorThemeData data; - - /// The closest instance of this class that encloses the given context. - /// - /// If there is no enclosing [StreamPollInteractorTheme] widget, then - /// [StreamChatThemeData.pollInteractorTheme] is used. - /// - /// Typical usage is as follows: - /// - /// ```dart - /// StreamPollInteractorTheme theme = StreamPollInteractorTheme.of(context); - /// ``` - static StreamPollInteractorThemeData of(BuildContext context) { - final pollInteractorTheme = - context.dependOnInheritedWidgetOfExactType(); - return pollInteractorTheme?.data ?? - StreamChatTheme.of(context).pollInteractorTheme; - } - - @override - Widget wrap(BuildContext context, Widget child) => - StreamPollInteractorTheme(data: data, child: child); - - @override - bool updateShouldNotify(StreamPollInteractorTheme oldWidget) => - data != oldWidget.data; -} - -/// {@template streamPollInteractorThemeData} -/// A style that overrides the default appearance of [StreamPollInteractor] -/// widget when used with [StreamPollCreatorTheme] or with the overall -/// [StreamChatTheme]'s [StreamChatThemeData.pollInteractorTheme]. -/// {@endtemplate} -class StreamPollInteractorThemeData with Diagnosticable { - /// {@macro streamPollInteractorThemeData} - const StreamPollInteractorThemeData({ - this.pollTitleStyle, - this.pollSubtitleStyle, - this.pollOptionTextStyle, - this.pollOptionVoteCountTextStyle, - this.pollOptionCheckboxShape, - this.pollOptionCheckboxCheckColor, - this.pollOptionCheckboxActiveColor, - this.pollOptionCheckboxBorderSide, - this.pollOptionVotesProgressBarMinHeight, - this.pollOptionVotesProgressBarTrackColor, - this.pollOptionVotesProgressBarValueColor, - this.pollOptionVotesProgressBarWinnerColor, - this.pollOptionVotesProgressBarBorderRadius, - this.pollActionButtonStyle, - this.pollActionDialogTitleStyle, - this.pollActionDialogTextFieldStyle, - this.pollActionDialogTextFieldFillColor, - this.pollActionDialogTextFieldBorderRadius, - }); - - /// The text style of the poll title. - final TextStyle? pollTitleStyle; - - /// The text style of the poll subtitle. - final TextStyle? pollSubtitleStyle; - - /// The text style of the poll option. - final TextStyle? pollOptionTextStyle; - - /// The text style of the poll option vote count. - final TextStyle? pollOptionVoteCountTextStyle; - - /// The shape of the poll option checkbox. - final OutlinedBorder? pollOptionCheckboxShape; - - /// The color used for the poll option checkbox check. - final Color? pollOptionCheckboxCheckColor; - - /// The color used for the checkbox when it's active. - final Color? pollOptionCheckboxActiveColor; - - /// The border configuration of the poll option checkbox. - final BorderSide? pollOptionCheckboxBorderSide; - - /// The minimum height of the poll option votes progress bar. - final double? pollOptionVotesProgressBarMinHeight; - - /// The track color of the poll option votes progress bar. - final Color? pollOptionVotesProgressBarTrackColor; - - /// The color of the poll option votes progress bar value. - final Color? pollOptionVotesProgressBarValueColor; - - /// The color of the poll option votes progress bar value when it's the - /// winner. - final Color? pollOptionVotesProgressBarWinnerColor; - - /// The border radius of the poll option votes progress bar. - final BorderRadius? pollOptionVotesProgressBarBorderRadius; - - /// The button style of the poll action buttons. - final ButtonStyle? pollActionButtonStyle; - - /// The text style of the poll action dialog title. - final TextStyle? pollActionDialogTitleStyle; - - /// The text style of the poll action dialog text field. - final TextStyle? pollActionDialogTextFieldStyle; - - /// The fill color of the poll action dialog text field. - final Color? pollActionDialogTextFieldFillColor; - - /// The border radius of the poll action dialog text field. - final BorderRadius? pollActionDialogTextFieldBorderRadius; - - /// Copies this [StreamPollInteractorThemeData] with some new values. - StreamPollInteractorThemeData copyWith({ - TextStyle? pollTitleStyle, - TextStyle? pollSubtitleStyle, - TextStyle? pollOptionTextStyle, - TextStyle? pollOptionVoteCountTextStyle, - OutlinedBorder? pollOptionCheckboxShape, - Color? pollOptionCheckboxCheckColor, - Color? pollOptionCheckboxActiveColor, - BorderSide? pollOptionCheckboxBorderSide, - double? pollOptionVotesProgressBarMinHeight, - Color? pollOptionVotesProgressBarTrackColor, - Color? pollOptionVotesProgressBarValueColor, - Color? pollOptionVotesProgressBarWinnerColor, - BorderRadius? pollOptionVotesProgressBarBorderRadius, - ButtonStyle? pollActionButtonStyle, - TextStyle? pollActionDialogTitleStyle, - TextStyle? pollActionDialogTextFieldStyle, - Color? pollActionDialogTextFieldFillColor, - BorderRadius? pollActionDialogTextFieldBorderRadius, - }) { - return StreamPollInteractorThemeData( - pollTitleStyle: pollTitleStyle ?? this.pollTitleStyle, - pollSubtitleStyle: pollSubtitleStyle ?? this.pollSubtitleStyle, - pollOptionTextStyle: pollOptionTextStyle ?? this.pollOptionTextStyle, - pollOptionVoteCountTextStyle: - pollOptionVoteCountTextStyle ?? this.pollOptionVoteCountTextStyle, - pollOptionCheckboxShape: - pollOptionCheckboxShape ?? this.pollOptionCheckboxShape, - pollOptionCheckboxCheckColor: - pollOptionCheckboxCheckColor ?? this.pollOptionCheckboxCheckColor, - pollOptionCheckboxActiveColor: - pollOptionCheckboxActiveColor ?? this.pollOptionCheckboxActiveColor, - pollOptionCheckboxBorderSide: - pollOptionCheckboxBorderSide ?? this.pollOptionCheckboxBorderSide, - pollOptionVotesProgressBarMinHeight: - pollOptionVotesProgressBarMinHeight ?? - this.pollOptionVotesProgressBarMinHeight, - pollOptionVotesProgressBarTrackColor: - pollOptionVotesProgressBarTrackColor ?? - this.pollOptionVotesProgressBarTrackColor, - pollOptionVotesProgressBarValueColor: - pollOptionVotesProgressBarValueColor ?? - this.pollOptionVotesProgressBarValueColor, - pollOptionVotesProgressBarWinnerColor: - pollOptionVotesProgressBarWinnerColor ?? - this.pollOptionVotesProgressBarWinnerColor, - pollOptionVotesProgressBarBorderRadius: - pollOptionVotesProgressBarBorderRadius ?? - this.pollOptionVotesProgressBarBorderRadius, - pollActionButtonStyle: - pollActionButtonStyle ?? this.pollActionButtonStyle, - pollActionDialogTitleStyle: - pollActionDialogTitleStyle ?? this.pollActionDialogTitleStyle, - pollActionDialogTextFieldStyle: - pollActionDialogTextFieldStyle ?? this.pollActionDialogTextFieldStyle, - pollActionDialogTextFieldFillColor: pollActionDialogTextFieldFillColor ?? - this.pollActionDialogTextFieldFillColor, - pollActionDialogTextFieldBorderRadius: - pollActionDialogTextFieldBorderRadius ?? - this.pollActionDialogTextFieldBorderRadius, - ); - } - - /// Merges [this] [StreamPollInteractorThemeData] with the [other] - StreamPollInteractorThemeData merge(StreamPollInteractorThemeData? other) { - if (other == null) return this; - return copyWith( - pollTitleStyle: other.pollTitleStyle ?? pollTitleStyle, - pollSubtitleStyle: other.pollSubtitleStyle ?? pollSubtitleStyle, - pollOptionTextStyle: other.pollOptionTextStyle ?? pollOptionTextStyle, - pollOptionVoteCountTextStyle: - other.pollOptionVoteCountTextStyle ?? pollOptionVoteCountTextStyle, - pollOptionCheckboxShape: - other.pollOptionCheckboxShape ?? pollOptionCheckboxShape, - pollOptionCheckboxCheckColor: - other.pollOptionCheckboxCheckColor ?? pollOptionCheckboxCheckColor, - pollOptionCheckboxActiveColor: - other.pollOptionCheckboxActiveColor ?? pollOptionCheckboxActiveColor, - pollOptionCheckboxBorderSide: - other.pollOptionCheckboxBorderSide ?? pollOptionCheckboxBorderSide, - pollOptionVotesProgressBarMinHeight: - other.pollOptionVotesProgressBarMinHeight ?? - pollOptionVotesProgressBarMinHeight, - pollOptionVotesProgressBarTrackColor: - other.pollOptionVotesProgressBarTrackColor ?? - pollOptionVotesProgressBarTrackColor, - pollOptionVotesProgressBarValueColor: - other.pollOptionVotesProgressBarValueColor ?? - pollOptionVotesProgressBarValueColor, - pollOptionVotesProgressBarWinnerColor: - other.pollOptionVotesProgressBarWinnerColor ?? - pollOptionVotesProgressBarWinnerColor, - pollOptionVotesProgressBarBorderRadius: - other.pollOptionVotesProgressBarBorderRadius ?? - pollOptionVotesProgressBarBorderRadius, - pollActionButtonStyle: - other.pollActionButtonStyle ?? pollActionButtonStyle, - pollActionDialogTitleStyle: - other.pollActionDialogTitleStyle ?? pollActionDialogTitleStyle, - pollActionDialogTextFieldStyle: other.pollActionDialogTextFieldStyle ?? - pollActionDialogTextFieldStyle, - pollActionDialogTextFieldFillColor: - other.pollActionDialogTextFieldFillColor ?? - pollActionDialogTextFieldFillColor, - pollActionDialogTextFieldBorderRadius: - other.pollActionDialogTextFieldBorderRadius ?? - pollActionDialogTextFieldBorderRadius, - ); - } - - /// Linearly interpolate between two [StreamPollInteractorThemeData]. - StreamPollInteractorThemeData lerp( - StreamPollInteractorThemeData a, - StreamPollInteractorThemeData b, - double t, - ) { - return StreamPollInteractorThemeData( - pollTitleStyle: TextStyle.lerp(a.pollTitleStyle, b.pollTitleStyle, t), - pollSubtitleStyle: - TextStyle.lerp(a.pollSubtitleStyle, b.pollSubtitleStyle, t), - pollOptionTextStyle: - TextStyle.lerp(a.pollOptionTextStyle, b.pollOptionTextStyle, t), - pollOptionVoteCountTextStyle: TextStyle.lerp( - a.pollOptionVoteCountTextStyle, b.pollOptionVoteCountTextStyle, t), - pollOptionCheckboxShape: OutlinedBorder.lerp( - a.pollOptionCheckboxShape, b.pollOptionCheckboxShape, t), - pollOptionCheckboxCheckColor: Color.lerp( - a.pollOptionCheckboxCheckColor, b.pollOptionCheckboxCheckColor, t), - pollOptionCheckboxActiveColor: Color.lerp( - a.pollOptionCheckboxActiveColor, b.pollOptionCheckboxActiveColor, t), - pollOptionCheckboxBorderSide: _lerpSides( - a.pollOptionCheckboxBorderSide, b.pollOptionCheckboxBorderSide, t), - pollOptionVotesProgressBarMinHeight: lerpDouble( - a.pollOptionVotesProgressBarMinHeight, - b.pollOptionVotesProgressBarMinHeight, - t), - pollOptionVotesProgressBarTrackColor: Color.lerp( - a.pollOptionVotesProgressBarTrackColor, - b.pollOptionVotesProgressBarTrackColor, - t), - pollOptionVotesProgressBarValueColor: Color.lerp( - a.pollOptionVotesProgressBarValueColor, - b.pollOptionVotesProgressBarValueColor, - t), - pollOptionVotesProgressBarWinnerColor: Color.lerp( - a.pollOptionVotesProgressBarWinnerColor, - b.pollOptionVotesProgressBarWinnerColor, - t), - pollOptionVotesProgressBarBorderRadius: BorderRadius.lerp( - a.pollOptionVotesProgressBarBorderRadius, - b.pollOptionVotesProgressBarBorderRadius, - t), - pollActionButtonStyle: - ButtonStyle.lerp(a.pollActionButtonStyle, b.pollActionButtonStyle, t), - pollActionDialogTitleStyle: TextStyle.lerp( - a.pollActionDialogTitleStyle, b.pollActionDialogTitleStyle, t), - pollActionDialogTextFieldStyle: TextStyle.lerp( - a.pollActionDialogTextFieldStyle, - b.pollActionDialogTextFieldStyle, - t), - pollActionDialogTextFieldFillColor: Color.lerp( - a.pollActionDialogTextFieldFillColor, - b.pollActionDialogTextFieldFillColor, - t), - pollActionDialogTextFieldBorderRadius: BorderRadius.lerp( - a.pollActionDialogTextFieldBorderRadius, - b.pollActionDialogTextFieldBorderRadius, - t), - ); - } - - // Special case because BorderSide.lerp() doesn't support null arguments - static BorderSide? _lerpSides(BorderSide? a, BorderSide? b, double t) { - if (a == null || b == null) return null; - if (identical(a, b)) return a; - - if (a is WidgetStateBorderSide) { - a = a.resolve({}); - } - if (b is WidgetStateBorderSide) { - b = b.resolve({}); - } - - return BorderSide.lerp(a!, b!, t); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StreamPollInteractorThemeData && - other.pollTitleStyle == pollTitleStyle && - other.pollSubtitleStyle == pollSubtitleStyle && - other.pollOptionTextStyle == pollOptionTextStyle && - other.pollOptionVoteCountTextStyle == pollOptionVoteCountTextStyle && - other.pollOptionCheckboxShape == pollOptionCheckboxShape && - other.pollOptionCheckboxCheckColor == pollOptionCheckboxCheckColor && - other.pollOptionCheckboxActiveColor == - pollOptionCheckboxActiveColor && - other.pollOptionCheckboxBorderSide == pollOptionCheckboxBorderSide && - other.pollOptionVotesProgressBarMinHeight == - pollOptionVotesProgressBarMinHeight && - other.pollOptionVotesProgressBarTrackColor == - pollOptionVotesProgressBarTrackColor && - other.pollOptionVotesProgressBarValueColor == - pollOptionVotesProgressBarValueColor && - other.pollOptionVotesProgressBarWinnerColor == - pollOptionVotesProgressBarWinnerColor && - other.pollOptionVotesProgressBarBorderRadius == - pollOptionVotesProgressBarBorderRadius && - other.pollActionButtonStyle == pollActionButtonStyle && - other.pollActionDialogTitleStyle == pollActionDialogTitleStyle && - other.pollActionDialogTextFieldStyle == - pollActionDialogTextFieldStyle && - other.pollActionDialogTextFieldFillColor == - pollActionDialogTextFieldFillColor && - other.pollActionDialogTextFieldBorderRadius == - pollActionDialogTextFieldBorderRadius; - - @override - int get hashCode => - pollTitleStyle.hashCode ^ - pollSubtitleStyle.hashCode ^ - pollOptionTextStyle.hashCode ^ - pollOptionVoteCountTextStyle.hashCode ^ - pollOptionCheckboxShape.hashCode ^ - pollOptionCheckboxCheckColor.hashCode ^ - pollOptionCheckboxActiveColor.hashCode ^ - pollOptionCheckboxBorderSide.hashCode ^ - pollOptionVotesProgressBarMinHeight.hashCode ^ - pollOptionVotesProgressBarTrackColor.hashCode ^ - pollOptionVotesProgressBarValueColor.hashCode ^ - pollOptionVotesProgressBarWinnerColor.hashCode ^ - pollOptionVotesProgressBarBorderRadius.hashCode ^ - pollActionButtonStyle.hashCode ^ - pollActionDialogTitleStyle.hashCode ^ - pollActionDialogTextFieldStyle.hashCode ^ - pollActionDialogTextFieldFillColor.hashCode ^ - pollActionDialogTextFieldBorderRadius.hashCode; -} diff --git a/packages/stream_chat_flutter/lib/src/theme/poll_option_votes_dialog_theme.dart b/packages/stream_chat_flutter/lib/src/theme/poll_option_votes_dialog_theme.dart deleted file mode 100644 index c58b05ebb6..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/poll_option_votes_dialog_theme.dart +++ /dev/null @@ -1,200 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -/// {@template streamPollOptionVotesDialogTheme} -/// Overrides the default style of [StreamPollOptionVotesDialog] descendants. -/// -/// See also: -/// -/// * [StreamPollOptionVotesDialogThemeData], which is used to configure this -/// theme. -/// {@endtemplate} -class StreamPollOptionVotesDialogTheme extends InheritedTheme { - /// Creates a [StreamPollOptionVotesDialogTheme]. - /// - /// The [data] parameter must not be null. - const StreamPollOptionVotesDialogTheme({ - super.key, - required this.data, - required super.child, - }); - - /// The configuration of this theme. - final StreamPollOptionVotesDialogThemeData data; - - /// The closest instance of this class that encloses the given context. - /// - /// If there is no enclosing [StreamPollOptionVotesDialogTheme] widget, then - /// [StreamChatThemeData.pollOptionVotesDialogTheme] is used. - static StreamPollOptionVotesDialogThemeData of(BuildContext context) { - final pollCommentsDialogTheme = context - .dependOnInheritedWidgetOfExactType(); - return pollCommentsDialogTheme?.data ?? - StreamChatTheme.of(context).pollOptionVotesDialogTheme; - } - - @override - Widget wrap(BuildContext context, Widget child) => - StreamPollOptionVotesDialogTheme(data: data, child: child); - - @override - bool updateShouldNotify(StreamPollOptionVotesDialogTheme oldWidget) => - data != oldWidget.data; -} - -/// {@template streamPollOptionVotesDialogThemeData} -/// A style that overrides the default appearance of -/// [StreamPollOptionVotesDialog] widgets when used with -/// [StreamPollCommentsDialogTheme] or with the overall [StreamChatTheme]'s -/// [StreamChatThemeData.pollOptionVotesDialogTheme]. -/// {@endtemplate} -class StreamPollOptionVotesDialogThemeData with Diagnosticable { - /// {@macro streamPollOptionVotesDialogThemeData} - const StreamPollOptionVotesDialogThemeData({ - this.backgroundColor, - this.appBarElevation, - this.appBarBackgroundColor, - this.appBarTitleTextStyle, - this.pollOptionVoteCountTextStyle, - this.pollOptionWinnerVoteCountTextStyle, - this.pollOptionVoteItemBackgroundColor, - this.pollOptionVoteItemBorderRadius, - }); - - /// The background color of the dialog. - final Color? backgroundColor; - - /// The elevation of the app bar. - final double? appBarElevation; - - /// The background color of the app bar. - final Color? appBarBackgroundColor; - - /// The text style of the app bar title. - final TextStyle? appBarTitleTextStyle; - - /// The text style of the poll option vote count. - final TextStyle? pollOptionVoteCountTextStyle; - - /// The text style of the winner poll option vote count. - final TextStyle? pollOptionWinnerVoteCountTextStyle; - - /// The background color of the poll option vote item. - final Color? pollOptionVoteItemBackgroundColor; - - /// The border radius of the poll option vote item. - final BorderRadius? pollOptionVoteItemBorderRadius; - - /// Copies this [StreamPollOptionVotesDialogThemeData] with some new values. - StreamPollOptionVotesDialogThemeData copyWith({ - Color? backgroundColor, - double? appBarElevation, - Color? appBarBackgroundColor, - TextStyle? appBarTitleTextStyle, - TextStyle? pollOptionVoteCountTextStyle, - TextStyle? pollOptionWinnerVoteCountTextStyle, - Color? pollOptionVoteItemBackgroundColor, - BorderRadius? pollOptionVoteItemBorderRadius, - }) => - StreamPollOptionVotesDialogThemeData( - backgroundColor: backgroundColor ?? this.backgroundColor, - appBarElevation: appBarElevation ?? this.appBarElevation, - appBarBackgroundColor: - appBarBackgroundColor ?? this.appBarBackgroundColor, - appBarTitleTextStyle: appBarTitleTextStyle ?? this.appBarTitleTextStyle, - pollOptionVoteCountTextStyle: - pollOptionVoteCountTextStyle ?? this.pollOptionVoteCountTextStyle, - pollOptionWinnerVoteCountTextStyle: - pollOptionWinnerVoteCountTextStyle ?? - this.pollOptionWinnerVoteCountTextStyle, - pollOptionVoteItemBackgroundColor: pollOptionVoteItemBackgroundColor ?? - this.pollOptionVoteItemBackgroundColor, - pollOptionVoteItemBorderRadius: pollOptionVoteItemBorderRadius ?? - this.pollOptionVoteItemBorderRadius, - ); - - /// Merges this [StreamPollOptionVotesDialogThemeData] with the [other]. - StreamPollOptionVotesDialogThemeData merge( - StreamPollOptionVotesDialogThemeData? other, - ) { - if (other == null) return this; - return copyWith( - backgroundColor: other.backgroundColor, - appBarElevation: other.appBarElevation, - appBarBackgroundColor: other.appBarBackgroundColor, - appBarTitleTextStyle: other.appBarTitleTextStyle, - pollOptionVoteCountTextStyle: other.pollOptionVoteCountTextStyle, - pollOptionWinnerVoteCountTextStyle: - other.pollOptionWinnerVoteCountTextStyle, - pollOptionVoteItemBackgroundColor: - other.pollOptionVoteItemBackgroundColor, - pollOptionVoteItemBorderRadius: other.pollOptionVoteItemBorderRadius, - ); - } - - /// Linearly interpolate between two poll option votes dialog themes. - StreamPollOptionVotesDialogThemeData lerp( - StreamPollOptionVotesDialogThemeData? a, - StreamPollOptionVotesDialogThemeData? b, - double t, - ) { - return StreamPollOptionVotesDialogThemeData( - backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t), - appBarElevation: lerpDouble(a?.appBarElevation, b?.appBarElevation, t), - appBarBackgroundColor: - Color.lerp(a?.appBarBackgroundColor, b?.appBarBackgroundColor, t), - appBarTitleTextStyle: - TextStyle.lerp(a?.appBarTitleTextStyle, b?.appBarTitleTextStyle, t), - pollOptionVoteCountTextStyle: TextStyle.lerp( - a?.pollOptionVoteCountTextStyle, - b?.pollOptionVoteCountTextStyle, - t, - ), - pollOptionWinnerVoteCountTextStyle: TextStyle.lerp( - a?.pollOptionWinnerVoteCountTextStyle, - b?.pollOptionWinnerVoteCountTextStyle, - t, - ), - pollOptionVoteItemBackgroundColor: Color.lerp( - a?.pollOptionVoteItemBackgroundColor, - b?.pollOptionVoteItemBackgroundColor, - t, - ), - pollOptionVoteItemBorderRadius: BorderRadiusGeometry.lerp( - a?.pollOptionVoteItemBorderRadius, - b?.pollOptionVoteItemBorderRadius, - t, - ) as BorderRadius?, - ); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StreamPollOptionVotesDialogThemeData && - other.backgroundColor == backgroundColor && - other.appBarElevation == appBarElevation && - other.appBarBackgroundColor == appBarBackgroundColor && - other.appBarTitleTextStyle == appBarTitleTextStyle && - other.pollOptionVoteCountTextStyle == pollOptionVoteCountTextStyle && - other.pollOptionWinnerVoteCountTextStyle == - pollOptionWinnerVoteCountTextStyle && - other.pollOptionVoteItemBackgroundColor == - pollOptionVoteItemBackgroundColor && - other.pollOptionVoteItemBorderRadius == - pollOptionVoteItemBorderRadius; - - @override - int get hashCode => - backgroundColor.hashCode ^ - appBarElevation.hashCode ^ - appBarBackgroundColor.hashCode ^ - appBarTitleTextStyle.hashCode ^ - pollOptionVoteCountTextStyle.hashCode ^ - pollOptionWinnerVoteCountTextStyle.hashCode ^ - pollOptionVoteItemBackgroundColor.hashCode ^ - pollOptionVoteItemBorderRadius.hashCode; -} diff --git a/packages/stream_chat_flutter/lib/src/theme/poll_options_dialog_theme.dart b/packages/stream_chat_flutter/lib/src/theme/poll_options_dialog_theme.dart deleted file mode 100644 index e3d40dd39a..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/poll_options_dialog_theme.dart +++ /dev/null @@ -1,170 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -/// {@template streamPollOptionsDialogTheme} -/// Overrides the default style of [StreamPollOptionsDialog] descendants. -/// -/// See also: -/// -/// * [StreamPollResultsDialogThemeData], which is used to configure this -/// theme. -/// {@endtemplate} -class StreamPollOptionsDialogTheme extends InheritedTheme { - /// Creates a [StreamPollOptionsDialogTheme]. - /// - /// The [data] parameter must not be null. - const StreamPollOptionsDialogTheme({ - super.key, - required this.data, - required super.child, - }); - - /// The configuration of this theme. - final StreamPollOptionsDialogThemeData data; - - /// The closest instance of this class that encloses the given context. - /// - /// If there is no enclosing [StreamPollOptionsDialogTheme] widget, then - /// [StreamChatThemeData.pollOptionsDialogTheme] is used. - static StreamPollOptionsDialogThemeData of(BuildContext context) { - final pollOptionsDialogTheme = context - .dependOnInheritedWidgetOfExactType(); - return pollOptionsDialogTheme?.data ?? - StreamChatTheme.of(context).pollOptionsDialogTheme; - } - - @override - Widget wrap(BuildContext context, Widget child) => - StreamPollOptionsDialogTheme(data: data, child: child); - - @override - bool updateShouldNotify(StreamPollOptionsDialogTheme oldWidget) => - data != oldWidget.data; -} - -/// {@template streamPollOptionsDialogThemeData} -/// A style that overrides the default appearance of [StreamPollOptionsDialog] -/// widgets when used with [StreamPollOptionsDialogTheme] or with the overall -/// [StreamChatTheme]'s [StreamChatThemeData.pollOptionsDialogTheme]. -/// {@endtemplate} -class StreamPollOptionsDialogThemeData with Diagnosticable { - /// {@macro streamPollOptionsDialogThemeData} - const StreamPollOptionsDialogThemeData({ - this.backgroundColor, - this.appBarElevation, - this.appBarBackgroundColor, - this.appBarTitleTextStyle, - this.pollTitleTextStyle, - this.pollTitleDecoration, - this.pollOptionsListViewDecoration, - }); - - /// The background color of the dialog. - final Color? backgroundColor; - - /// The elevation of the app bar. - final double? appBarElevation; - - /// The background color of the app bar. - final Color? appBarBackgroundColor; - - /// The text style of the app bar title. - final TextStyle? appBarTitleTextStyle; - - /// The text style of the poll title. - final TextStyle? pollTitleTextStyle; - - /// The decoration of the poll title. - final Decoration? pollTitleDecoration; - - /// The decoration of the poll options list view. - final Decoration? pollOptionsListViewDecoration; - - /// A copy of [StreamPollOptionsDialogThemeData] with specified attributes - /// overridden. - StreamPollOptionsDialogThemeData copyWith({ - Color? backgroundColor, - double? appBarElevation, - Color? appBarBackgroundColor, - TextStyle? appBarTitleTextStyle, - TextStyle? pollTitleTextStyle, - Decoration? pollTitleDecoration, - Decoration? pollOptionsListViewDecoration, - }) => - StreamPollOptionsDialogThemeData( - backgroundColor: backgroundColor ?? this.backgroundColor, - appBarElevation: appBarElevation ?? this.appBarElevation, - appBarBackgroundColor: - appBarBackgroundColor ?? this.appBarBackgroundColor, - appBarTitleTextStyle: appBarTitleTextStyle ?? this.appBarTitleTextStyle, - pollTitleTextStyle: pollTitleTextStyle ?? this.pollTitleTextStyle, - pollTitleDecoration: pollTitleDecoration ?? this.pollTitleDecoration, - pollOptionsListViewDecoration: - pollOptionsListViewDecoration ?? this.pollOptionsListViewDecoration, - ); - - /// Merges this [StreamPollOptionsDialogThemeData] with the [other]. - StreamPollOptionsDialogThemeData merge( - StreamPollOptionsDialogThemeData? other, - ) { - if (other == null) return this; - return copyWith( - backgroundColor: other.backgroundColor, - appBarElevation: other.appBarElevation, - appBarBackgroundColor: other.appBarBackgroundColor, - appBarTitleTextStyle: other.appBarTitleTextStyle, - pollTitleTextStyle: other.pollTitleTextStyle, - pollTitleDecoration: other.pollTitleDecoration, - pollOptionsListViewDecoration: other.pollOptionsListViewDecoration, - ); - } - - /// Linearly interpolate between two [StreamPollOptionsDialogThemeData]. - StreamPollOptionsDialogThemeData lerp( - StreamPollOptionsDialogThemeData a, - StreamPollOptionsDialogThemeData b, - double t, - ) => - StreamPollOptionsDialogThemeData( - backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t), - appBarElevation: lerpDouble(a.appBarElevation, b.appBarElevation, t), - appBarBackgroundColor: - Color.lerp(a.appBarBackgroundColor, b.appBarBackgroundColor, t), - appBarTitleTextStyle: - TextStyle.lerp(a.appBarTitleTextStyle, b.appBarTitleTextStyle, t), - pollTitleTextStyle: - TextStyle.lerp(a.pollTitleTextStyle, b.pollTitleTextStyle, t), - pollTitleDecoration: - Decoration.lerp(a.pollTitleDecoration, b.pollTitleDecoration, t), - pollOptionsListViewDecoration: Decoration.lerp( - a.pollOptionsListViewDecoration, - b.pollOptionsListViewDecoration, - t, - ), - ); - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StreamPollOptionsDialogThemeData && - other.backgroundColor == backgroundColor && - other.appBarElevation == appBarElevation && - other.appBarBackgroundColor == appBarBackgroundColor && - other.appBarTitleTextStyle == appBarTitleTextStyle && - other.pollTitleTextStyle == pollTitleTextStyle && - other.pollTitleDecoration == pollTitleDecoration && - other.pollOptionsListViewDecoration == pollOptionsListViewDecoration; - - @override - int get hashCode => - backgroundColor.hashCode ^ - appBarElevation.hashCode ^ - appBarBackgroundColor.hashCode ^ - appBarTitleTextStyle.hashCode ^ - pollTitleTextStyle.hashCode ^ - pollTitleDecoration.hashCode ^ - pollOptionsListViewDecoration.hashCode; -} diff --git a/packages/stream_chat_flutter/lib/src/theme/poll_results_dialog_theme.dart b/packages/stream_chat_flutter/lib/src/theme/poll_results_dialog_theme.dart deleted file mode 100644 index 859b1c149e..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/poll_results_dialog_theme.dart +++ /dev/null @@ -1,267 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -/// {@template streamPollResultsDialogTheme} -/// Overrides the default style of [StreamPollResultsDialog] descendants. -/// -/// See also: -/// -/// * [StreamPollResultsDialogThemeData], which is used to configure this -/// theme. -/// {@endtemplate} -class StreamPollResultsDialogTheme extends InheritedTheme { - /// Creates a [StreamPollResultsDialogTheme]. - /// - /// The [data] parameter must not be null. - const StreamPollResultsDialogTheme({ - super.key, - required this.data, - required super.child, - }); - - /// The configuration of this theme. - final StreamPollResultsDialogThemeData data; - - /// The closest instance of this class that encloses the given context. - /// - /// If there is no enclosing [StreamPollInteractorTheme] widget, then - /// [StreamChatThemeData.pollInteractorTheme] is used. - /// - /// Typical usage is as follows: - /// - /// ```dart - /// StreamPollCreatorTheme theme = StreamPollCreatorTheme.of(context); - /// ``` - static StreamPollResultsDialogThemeData of(BuildContext context) { - final pollResultsDialogTheme = context - .dependOnInheritedWidgetOfExactType(); - return pollResultsDialogTheme?.data ?? - StreamChatTheme.of(context).pollResultsDialogTheme; - } - - @override - Widget wrap(BuildContext context, Widget child) => - StreamPollResultsDialogTheme(data: data, child: child); - - @override - bool updateShouldNotify(StreamPollResultsDialogTheme oldWidget) => - data != oldWidget.data; -} - -/// {@template streamPollCreatorThemeData} -/// A style that overrides the default appearance of [StreamPollResultsDialog] -/// widgets when used with [StreamPollResultsDialogTheme] or with the overall -/// [StreamChatTheme]'s [StreamChatThemeData.pollResultsDialogTheme]. -/// {@endtemplate} -class StreamPollResultsDialogThemeData with Diagnosticable { - /// {@macro streamPollResultsDialogThemeData} - const StreamPollResultsDialogThemeData({ - this.backgroundColor, - this.appBarElevation, - this.appBarBackgroundColor, - this.appBarTitleTextStyle, - this.pollTitleTextStyle, - this.pollTitleDecoration, - this.pollOptionsDecoration, - this.pollOptionsWinnerDecoration, - this.pollOptionsTextStyle, - this.pollOptionsWinnerTextStyle, - this.pollOptionsVoteCountTextStyle, - this.pollOptionsWinnerVoteCountTextStyle, - this.pollOptionsShowAllVotesButtonStyle, - }); - - /// The background color of the dialog. - final Color? backgroundColor; - - /// The elevation of the app bar. - final double? appBarElevation; - - /// The background color of the app bar. - final Color? appBarBackgroundColor; - - /// The text style of the app bar title. - final TextStyle? appBarTitleTextStyle; - - /// The text style of the poll title. - final TextStyle? pollTitleTextStyle; - - /// The decoration of the poll title. - final Decoration? pollTitleDecoration; - - /// The decoration of the poll options. - final Decoration? pollOptionsDecoration; - - /// The decoration of the winner poll option. - final Decoration? pollOptionsWinnerDecoration; - - /// The text style of the poll options. - final TextStyle? pollOptionsTextStyle; - - /// The text style of the winner poll options. - final TextStyle? pollOptionsWinnerTextStyle; - - /// The text style of the poll options vote count. - final TextStyle? pollOptionsVoteCountTextStyle; - - /// The text style of the winner poll options vote count. - final TextStyle? pollOptionsWinnerVoteCountTextStyle; - - /// The style of the poll options show all votes button. - final ButtonStyle? pollOptionsShowAllVotesButtonStyle; - - /// A copy of [StreamPollResultsDialogThemeData] with the given fields - /// replaced with the new values. - StreamPollResultsDialogThemeData copyWith({ - Color? backgroundColor, - double? appBarElevation, - Color? appBarBackgroundColor, - TextStyle? appBarTitleTextStyle, - TextStyle? pollTitleTextStyle, - Decoration? pollTitleDecoration, - Decoration? pollOptionsDecoration, - Decoration? pollOptionsWinnerDecoration, - TextStyle? pollOptionsTextStyle, - TextStyle? pollOptionsWinnerTextStyle, - TextStyle? pollOptionsVoteCountTextStyle, - TextStyle? pollOptionsWinnerVoteCountTextStyle, - ButtonStyle? pollOptionsShowAllVotesButtonStyle, - }) { - return StreamPollResultsDialogThemeData( - backgroundColor: backgroundColor ?? this.backgroundColor, - appBarElevation: appBarElevation ?? this.appBarElevation, - appBarBackgroundColor: - appBarBackgroundColor ?? this.appBarBackgroundColor, - appBarTitleTextStyle: appBarTitleTextStyle ?? this.appBarTitleTextStyle, - pollTitleTextStyle: pollTitleTextStyle ?? this.pollTitleTextStyle, - pollTitleDecoration: pollTitleDecoration ?? this.pollTitleDecoration, - pollOptionsDecoration: - pollOptionsDecoration ?? this.pollOptionsDecoration, - pollOptionsWinnerDecoration: - pollOptionsWinnerDecoration ?? this.pollOptionsWinnerDecoration, - pollOptionsTextStyle: pollOptionsTextStyle ?? this.pollOptionsTextStyle, - pollOptionsWinnerTextStyle: - pollOptionsWinnerTextStyle ?? this.pollOptionsWinnerTextStyle, - pollOptionsVoteCountTextStyle: - pollOptionsVoteCountTextStyle ?? this.pollOptionsVoteCountTextStyle, - pollOptionsWinnerVoteCountTextStyle: - pollOptionsWinnerVoteCountTextStyle ?? - this.pollOptionsWinnerVoteCountTextStyle, - pollOptionsShowAllVotesButtonStyle: pollOptionsShowAllVotesButtonStyle ?? - this.pollOptionsShowAllVotesButtonStyle, - ); - } - - /// Merges [this] [StreamPollResultsDialogThemeData] with the [other] - StreamPollResultsDialogThemeData merge( - StreamPollResultsDialogThemeData? other, - ) { - if (other == null) return this; - return copyWith( - backgroundColor: other.backgroundColor, - appBarElevation: other.appBarElevation, - appBarBackgroundColor: other.appBarBackgroundColor, - appBarTitleTextStyle: other.appBarTitleTextStyle, - pollTitleTextStyle: other.pollTitleTextStyle, - pollTitleDecoration: other.pollTitleDecoration, - pollOptionsDecoration: other.pollOptionsDecoration, - pollOptionsWinnerDecoration: other.pollOptionsWinnerDecoration, - pollOptionsTextStyle: other.pollOptionsTextStyle, - pollOptionsWinnerTextStyle: other.pollOptionsWinnerTextStyle, - pollOptionsVoteCountTextStyle: other.pollOptionsVoteCountTextStyle, - pollOptionsWinnerVoteCountTextStyle: - other.pollOptionsWinnerVoteCountTextStyle, - pollOptionsShowAllVotesButtonStyle: - other.pollOptionsShowAllVotesButtonStyle, - ); - } - - /// Linearly interpolate between two [StreamPollResultsDialogThemeData]. - StreamPollResultsDialogThemeData lerp( - StreamPollResultsDialogThemeData a, - StreamPollResultsDialogThemeData b, - double t, - ) { - return StreamPollResultsDialogThemeData( - backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t), - appBarElevation: lerpDouble(a.appBarElevation, b.appBarElevation, t), - appBarBackgroundColor: - Color.lerp(a.appBarBackgroundColor, b.appBarBackgroundColor, t), - appBarTitleTextStyle: - TextStyle.lerp(a.appBarTitleTextStyle, b.appBarTitleTextStyle, t), - pollTitleTextStyle: - TextStyle.lerp(a.pollTitleTextStyle, b.pollTitleTextStyle, t), - pollTitleDecoration: - Decoration.lerp(a.pollTitleDecoration, b.pollTitleDecoration, t), - pollOptionsDecoration: - Decoration.lerp(a.pollOptionsDecoration, b.pollOptionsDecoration, t), - pollOptionsWinnerDecoration: Decoration.lerp( - a.pollOptionsWinnerDecoration, - b.pollOptionsWinnerDecoration, - t, - ), - pollOptionsTextStyle: - TextStyle.lerp(a.pollOptionsTextStyle, b.pollOptionsTextStyle, t), - pollOptionsWinnerTextStyle: TextStyle.lerp( - a.pollOptionsWinnerTextStyle, - b.pollOptionsWinnerTextStyle, - t, - ), - pollOptionsVoteCountTextStyle: TextStyle.lerp( - a.pollOptionsVoteCountTextStyle, - b.pollOptionsVoteCountTextStyle, - t, - ), - pollOptionsWinnerVoteCountTextStyle: TextStyle.lerp( - a.pollOptionsWinnerVoteCountTextStyle, - b.pollOptionsWinnerVoteCountTextStyle, - t, - ), - pollOptionsShowAllVotesButtonStyle: ButtonStyle.lerp( - a.pollOptionsShowAllVotesButtonStyle, - b.pollOptionsShowAllVotesButtonStyle, - t, - ), - ); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StreamPollResultsDialogThemeData && - other.backgroundColor == backgroundColor && - other.appBarElevation == appBarElevation && - other.appBarBackgroundColor == appBarBackgroundColor && - other.appBarTitleTextStyle == appBarTitleTextStyle && - other.pollTitleTextStyle == pollTitleTextStyle && - other.pollTitleDecoration == pollTitleDecoration && - other.pollOptionsDecoration == pollOptionsDecoration && - other.pollOptionsWinnerDecoration == pollOptionsWinnerDecoration && - other.pollOptionsTextStyle == pollOptionsTextStyle && - other.pollOptionsWinnerTextStyle == pollOptionsWinnerTextStyle && - other.pollOptionsVoteCountTextStyle == - pollOptionsVoteCountTextStyle && - other.pollOptionsWinnerVoteCountTextStyle == - pollOptionsWinnerVoteCountTextStyle && - other.pollOptionsShowAllVotesButtonStyle == - pollOptionsShowAllVotesButtonStyle; - - @override - int get hashCode => - backgroundColor.hashCode ^ - appBarElevation.hashCode ^ - appBarBackgroundColor.hashCode ^ - appBarTitleTextStyle.hashCode ^ - pollTitleTextStyle.hashCode ^ - pollTitleDecoration.hashCode ^ - pollOptionsDecoration.hashCode ^ - pollOptionsWinnerDecoration.hashCode ^ - pollOptionsTextStyle.hashCode ^ - pollOptionsWinnerTextStyle.hashCode ^ - pollOptionsVoteCountTextStyle.hashCode ^ - pollOptionsWinnerVoteCountTextStyle.hashCode ^ - pollOptionsShowAllVotesButtonStyle.hashCode; -} diff --git a/packages/stream_chat_flutter/lib/src/theme/stream_chat_theme.dart b/packages/stream_chat_flutter/lib/src/theme/stream_chat_theme.dart deleted file mode 100644 index 78e6323063..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/stream_chat_theme.dart +++ /dev/null @@ -1,751 +0,0 @@ -// ignore_for_file: deprecated_member_use_from_same_package - -import 'package:flutter/material.dart' hide TextTheme; -import 'package:stream_chat_flutter/src/misc/audio_waveform.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamChatTheme} -/// Inherited widget providing the [StreamChatThemeData] to the widget tree -/// {@endtemplate} -class StreamChatTheme extends InheritedWidget { - /// {@macro streamChatTheme} - const StreamChatTheme({ - super.key, - required this.data, - required super.child, - }); - - /// {@macro streamChatThemeData} - final StreamChatThemeData data; - - @override - bool updateShouldNotify(StreamChatTheme oldWidget) => data != oldWidget.data; - - /// Use this method to get the current [StreamChatThemeData] instance - static StreamChatThemeData of(BuildContext context) { - final streamChatTheme = - context.dependOnInheritedWidgetOfExactType(); - - assert( - streamChatTheme != null, - 'You must have a StreamChatTheme widget at the top of your widget tree', - ); - - return streamChatTheme!.data; - } -} - -/// {@template streamChatThemeData} -/// Theme data for Stream Chat -/// {@endtemplate} -class StreamChatThemeData { - /// Creates a theme from scratch - factory StreamChatThemeData({ - Brightness? brightness, - StreamTextTheme? textTheme, - StreamColorTheme? colorTheme, - StreamChannelListHeaderThemeData? channelListHeaderTheme, - StreamChannelPreviewThemeData? channelPreviewTheme, - StreamChannelHeaderThemeData? channelHeaderTheme, - StreamMessageThemeData? otherMessageTheme, - StreamMessageThemeData? ownMessageTheme, - StreamMessageInputThemeData? messageInputTheme, - Widget Function(BuildContext, User)? defaultUserImage, - PlaceholderUserImage? placeholderUserImage, - IconThemeData? primaryIconTheme, - @Deprecated('Use StreamChatConfigurationData.reactionIcons instead') - List? reactionIcons, - StreamGalleryHeaderThemeData? imageHeaderTheme, - StreamGalleryFooterThemeData? imageFooterTheme, - StreamMessageListViewThemeData? messageListViewTheme, - @Deprecated( - "Use 'StreamChatThemeData.voiceRecordingAttachmentTheme' instead") - StreamVoiceRecordingThemeData? voiceRecordingTheme, - StreamPollCreatorThemeData? pollCreatorTheme, - StreamPollInteractorThemeData? pollInteractorTheme, - StreamPollOptionsDialogThemeData? pollOptionsDialogTheme, - StreamPollResultsDialogThemeData? pollResultsDialogTheme, - StreamPollCommentsDialogThemeData? pollCommentsDialogTheme, - StreamPollOptionVotesDialogThemeData? pollOptionVotesDialogTheme, - StreamThreadListTileThemeData? threadListTileTheme, - StreamAudioWaveformThemeData? audioWaveformTheme, - StreamAudioWaveformSliderThemeData? audioWaveformSliderTheme, - StreamVoiceRecordingAttachmentThemeData? voiceRecordingAttachmentTheme, - }) { - brightness ??= colorTheme?.brightness ?? Brightness.light; - final isDark = brightness == Brightness.dark; - textTheme ??= isDark ? StreamTextTheme.dark() : StreamTextTheme.light(); - colorTheme ??= isDark ? StreamColorTheme.dark() : StreamColorTheme.light(); - - final defaultData = StreamChatThemeData.fromColorAndTextTheme( - colorTheme, - textTheme, - ); - - final customizedData = defaultData.copyWith( - channelListHeaderTheme: channelListHeaderTheme, - channelPreviewTheme: channelPreviewTheme, - channelHeaderTheme: channelHeaderTheme, - otherMessageTheme: otherMessageTheme, - ownMessageTheme: ownMessageTheme, - messageInputTheme: messageInputTheme, - defaultUserImage: defaultUserImage, - placeholderUserImage: placeholderUserImage, - primaryIconTheme: primaryIconTheme, - reactionIcons: reactionIcons, - galleryHeaderTheme: imageHeaderTheme, - galleryFooterTheme: imageFooterTheme, - messageListViewTheme: messageListViewTheme, - voiceRecordingTheme: voiceRecordingTheme, - pollCreatorTheme: pollCreatorTheme, - pollInteractorTheme: pollInteractorTheme, - pollOptionsDialogTheme: pollOptionsDialogTheme, - pollResultsDialogTheme: pollResultsDialogTheme, - pollCommentsDialogTheme: pollCommentsDialogTheme, - pollOptionVotesDialogTheme: pollOptionVotesDialogTheme, - threadListTileTheme: threadListTileTheme, - audioWaveformTheme: audioWaveformTheme, - audioWaveformSliderTheme: audioWaveformSliderTheme, - voiceRecordingAttachmentTheme: voiceRecordingAttachmentTheme, - ); - - return defaultData.merge(customizedData); - } - - /// Theme initialized with light - factory StreamChatThemeData.light() => - StreamChatThemeData(brightness: Brightness.light); - - /// Theme initialized with dark - factory StreamChatThemeData.dark() => - StreamChatThemeData(brightness: Brightness.dark); - - /// Raw theme initialization - const StreamChatThemeData.raw({ - required this.textTheme, - required this.colorTheme, - required this.channelListHeaderTheme, - required this.channelPreviewTheme, - required this.channelHeaderTheme, - required this.otherMessageTheme, - required this.ownMessageTheme, - required this.messageInputTheme, - required this.primaryIconTheme, - required this.galleryHeaderTheme, - required this.galleryFooterTheme, - required this.messageListViewTheme, - required this.voiceRecordingTheme, - required this.pollCreatorTheme, - required this.pollInteractorTheme, - required this.pollResultsDialogTheme, - required this.pollOptionsDialogTheme, - required this.pollCommentsDialogTheme, - required this.pollOptionVotesDialogTheme, - required this.threadListTileTheme, - required this.audioWaveformTheme, - required this.audioWaveformSliderTheme, - required this.voiceRecordingAttachmentTheme, - }); - - /// Creates a theme from a Material [Theme] - factory StreamChatThemeData.fromTheme(ThemeData theme) { - final defaultTheme = StreamChatThemeData(brightness: theme.brightness); - final customizedTheme = StreamChatThemeData.fromColorAndTextTheme( - defaultTheme.colorTheme.copyWith( - accentPrimary: theme.colorScheme.secondary, - ), - defaultTheme.textTheme, - ); - return defaultTheme.merge(customizedTheme); - } - - /// Creates a theme from a [StreamColorTheme] and a [StreamTextTheme] - factory StreamChatThemeData.fromColorAndTextTheme( - StreamColorTheme colorTheme, - StreamTextTheme textTheme, - ) { - final accentColor = colorTheme.accentPrimary; - final iconTheme = IconThemeData(color: colorTheme.textLowEmphasis); - final channelHeaderTheme = StreamChannelHeaderThemeData( - avatarTheme: StreamAvatarThemeData( - borderRadius: BorderRadius.circular(20), - constraints: const BoxConstraints.tightFor( - height: 40, - width: 40, - ), - ), - color: colorTheme.barsBg, - titleStyle: textTheme.headlineBold, - subtitleStyle: textTheme.footnote.copyWith( - color: const Color(0xff7A7A7A), - ), - ); - final channelPreviewTheme = StreamChannelPreviewThemeData( - unreadCounterColor: colorTheme.accentError, - avatarTheme: StreamAvatarThemeData( - borderRadius: BorderRadius.circular(20), - constraints: const BoxConstraints.tightFor( - height: 40, - width: 40, - ), - ), - titleStyle: textTheme.bodyBold, - subtitleStyle: textTheme.footnote.copyWith( - color: const Color(0xff7A7A7A), - ), - lastMessageAtStyle: textTheme.footnote.copyWith( - // ignore: deprecated_member_use - color: colorTheme.textHighEmphasis.withOpacity(0.5), - ), - indicatorIconSize: 16, - ); - - final audioWaveformTheme = StreamAudioWaveformThemeData( - color: colorTheme.textLowEmphasis, - progressColor: colorTheme.accentPrimary, - minBarHeight: 2, - spacingRatio: 0.3, - heightScale: 1, - ); - - final audioWaveformSliderTheme = StreamAudioWaveformSliderThemeData( - audioWaveformTheme: audioWaveformTheme, - thumbColor: Colors.white, - thumbBorderColor: colorTheme.borders, - ); - - return StreamChatThemeData.raw( - textTheme: textTheme, - colorTheme: colorTheme, - primaryIconTheme: iconTheme, - channelPreviewTheme: channelPreviewTheme, - channelListHeaderTheme: StreamChannelListHeaderThemeData( - avatarTheme: StreamAvatarThemeData( - borderRadius: BorderRadius.circular(20), - constraints: const BoxConstraints.tightFor( - height: 40, - width: 40, - ), - ), - color: colorTheme.barsBg, - titleStyle: textTheme.headlineBold, - ), - channelHeaderTheme: channelHeaderTheme, - ownMessageTheme: StreamMessageThemeData( - messageAuthorStyle: - textTheme.footnote.copyWith(color: colorTheme.textLowEmphasis), - messageTextStyle: textTheme.body, - createdAtStyle: - textTheme.footnote.copyWith(color: colorTheme.textLowEmphasis), - repliesStyle: textTheme.footnoteBold.copyWith(color: accentColor), - messageBackgroundColor: colorTheme.borders, - messageBorderColor: colorTheme.borders, - reactionsBackgroundColor: colorTheme.barsBg, - reactionsBorderColor: colorTheme.borders, - reactionsMaskColor: colorTheme.appBg, - avatarTheme: StreamAvatarThemeData( - borderRadius: BorderRadius.circular(20), - constraints: const BoxConstraints.tightFor( - height: 32, - width: 32, - ), - ), - messageLinksStyle: TextStyle(color: accentColor), - urlAttachmentBackgroundColor: colorTheme.linkBg, - urlAttachmentHostStyle: textTheme.bodyBold.copyWith(color: accentColor), - urlAttachmentTitleStyle: textTheme.footnoteBold, - urlAttachmentTextStyle: textTheme.footnote, - urlAttachmentTitleMaxLine: 1, - urlAttachmentTextMaxLine: 3, - ), - otherMessageTheme: StreamMessageThemeData( - reactionsBackgroundColor: colorTheme.borders, - reactionsBorderColor: colorTheme.borders, - reactionsMaskColor: colorTheme.appBg, - messageTextStyle: textTheme.body, - createdAtStyle: - textTheme.footnote.copyWith(color: colorTheme.textLowEmphasis), - messageAuthorStyle: - textTheme.footnote.copyWith(color: colorTheme.textLowEmphasis), - repliesStyle: textTheme.footnoteBold.copyWith(color: accentColor), - messageLinksStyle: TextStyle(color: accentColor), - messageBackgroundColor: colorTheme.barsBg, - messageBorderColor: colorTheme.borders, - avatarTheme: StreamAvatarThemeData( - borderRadius: BorderRadius.circular(20), - constraints: const BoxConstraints.tightFor( - height: 32, - width: 32, - ), - ), - urlAttachmentBackgroundColor: colorTheme.linkBg, - urlAttachmentHostStyle: textTheme.bodyBold.copyWith(color: accentColor), - urlAttachmentTitleStyle: textTheme.footnoteBold, - urlAttachmentTextStyle: textTheme.footnote, - urlAttachmentTitleMaxLine: 1, - urlAttachmentTextMaxLine: 3, - ), - messageInputTheme: StreamMessageInputThemeData( - borderRadius: BorderRadius.circular(20), - sendAnimationDuration: const Duration(milliseconds: 300), - actionButtonColor: colorTheme.accentPrimary, - actionButtonIdleColor: colorTheme.textLowEmphasis, - expandButtonColor: colorTheme.accentPrimary, - sendButtonColor: colorTheme.accentPrimary, - sendButtonIdleColor: colorTheme.disabled, - inputBackgroundColor: colorTheme.barsBg, - inputTextStyle: textTheme.body, - linkHighlightColor: colorTheme.accentPrimary, - idleBorderGradient: LinearGradient( - colors: [ - colorTheme.disabled, - colorTheme.disabled, - ], - ), - activeBorderGradient: LinearGradient( - colors: [ - colorTheme.disabled, - colorTheme.disabled, - ], - ), - ), - galleryHeaderTheme: StreamGalleryHeaderThemeData( - closeButtonColor: colorTheme.textHighEmphasis, - backgroundColor: channelHeaderTheme.color, - iconMenuPointColor: colorTheme.textHighEmphasis, - titleTextStyle: textTheme.headlineBold, - subtitleTextStyle: channelPreviewTheme.subtitleStyle, - bottomSheetBarrierColor: colorTheme.overlay, - ), - galleryFooterTheme: StreamGalleryFooterThemeData( - backgroundColor: colorTheme.barsBg, - shareIconColor: colorTheme.textHighEmphasis, - titleTextStyle: textTheme.headlineBold, - gridIconButtonColor: colorTheme.textHighEmphasis, - bottomSheetBarrierColor: colorTheme.overlay, - bottomSheetBackgroundColor: colorTheme.barsBg, - bottomSheetPhotosTextStyle: textTheme.headlineBold, - bottomSheetCloseIconColor: colorTheme.textHighEmphasis, - ), - messageListViewTheme: StreamMessageListViewThemeData( - backgroundColor: colorTheme.barsBg, - ), - pollCreatorTheme: StreamPollCreatorThemeData( - backgroundColor: colorTheme.appBg, - appBarBackgroundColor: colorTheme.barsBg, - appBarElevation: 1, - appBarTitleStyle: textTheme.headlineBold.copyWith( - color: colorTheme.textHighEmphasis, - ), - questionTextFieldFillColor: colorTheme.inputBg, - questionHeaderStyle: textTheme.headline.copyWith( - color: colorTheme.textHighEmphasis, - ), - questionTextFieldStyle: textTheme.headline.copyWith( - color: colorTheme.textHighEmphasis, - ), - questionTextFieldErrorStyle: textTheme.footnote.copyWith( - color: colorTheme.accentError, - ), - questionTextFieldBorderRadius: BorderRadius.circular(12), - optionsTextFieldFillColor: colorTheme.inputBg, - optionsHeaderStyle: textTheme.headline.copyWith( - color: colorTheme.textHighEmphasis, - ), - optionsTextFieldStyle: textTheme.headline.copyWith( - color: colorTheme.textHighEmphasis, - ), - optionsTextFieldErrorStyle: textTheme.footnote.copyWith( - color: colorTheme.accentError, - ), - optionsTextFieldBorderRadius: BorderRadius.circular(12), - switchListTileFillColor: colorTheme.inputBg, - switchListTileTitleStyle: textTheme.headline.copyWith( - color: colorTheme.textHighEmphasis, - ), - switchListTileErrorStyle: textTheme.footnote.copyWith( - color: colorTheme.accentError, - ), - switchListTileBorderRadius: BorderRadius.circular(12), - ), - pollInteractorTheme: StreamPollInteractorThemeData( - pollTitleStyle: textTheme.headlineBold.copyWith( - color: colorTheme.textHighEmphasis, - ), - pollSubtitleStyle: textTheme.footnote.copyWith( - color: colorTheme.textLowEmphasis, - ), - pollOptionTextStyle: textTheme.headline.copyWith( - color: colorTheme.textHighEmphasis, - ), - pollOptionVoteCountTextStyle: textTheme.footnote.copyWith( - color: colorTheme.textLowEmphasis, - ), - pollOptionCheckboxShape: const CircleBorder(), - pollOptionCheckboxCheckColor: Colors.white, - pollOptionCheckboxActiveColor: colorTheme.accentPrimary, - pollOptionCheckboxBorderSide: BorderSide( - width: 2, - color: colorTheme.disabled, - ), - pollOptionVotesProgressBarMinHeight: 4, - pollOptionVotesProgressBarTrackColor: colorTheme.disabled, - pollOptionVotesProgressBarValueColor: colorTheme.accentPrimary, - pollOptionVotesProgressBarWinnerColor: colorTheme.accentInfo, - pollOptionVotesProgressBarBorderRadius: BorderRadius.circular(4), - pollActionButtonStyle: TextButton.styleFrom( - textStyle: textTheme.headline, - foregroundColor: colorTheme.accentPrimary, - ), - pollActionDialogTitleStyle: textTheme.headlineBold.copyWith( - color: colorTheme.textHighEmphasis, - ), - pollActionDialogTextFieldStyle: textTheme.headline.copyWith( - color: colorTheme.textHighEmphasis, - ), - pollActionDialogTextFieldBorderRadius: BorderRadius.circular(12), - pollActionDialogTextFieldFillColor: colorTheme.inputBg, - ), - pollResultsDialogTheme: StreamPollResultsDialogThemeData( - backgroundColor: colorTheme.appBg, - appBarElevation: 1, - appBarBackgroundColor: colorTheme.barsBg, - appBarTitleTextStyle: textTheme.headlineBold.copyWith( - color: colorTheme.textHighEmphasis, - ), - pollTitleTextStyle: textTheme.headlineBold.copyWith( - color: colorTheme.textHighEmphasis, - ), - pollTitleDecoration: BoxDecoration( - color: colorTheme.inputBg, - borderRadius: BorderRadius.circular(12), - ), - pollOptionsDecoration: BoxDecoration( - color: colorTheme.inputBg, - borderRadius: BorderRadius.circular(12), - ), - pollOptionsWinnerDecoration: BoxDecoration( - color: colorTheme.inputBg, - borderRadius: BorderRadius.circular(12), - ), - pollOptionsTextStyle: textTheme.headlineBold.copyWith( - color: colorTheme.textHighEmphasis, - ), - pollOptionsWinnerTextStyle: textTheme.headlineBold.copyWith( - color: colorTheme.textHighEmphasis, - ), - pollOptionsVoteCountTextStyle: textTheme.headline.copyWith( - color: colorTheme.textHighEmphasis, - ), - pollOptionsWinnerVoteCountTextStyle: textTheme.headline.copyWith( - color: colorTheme.textHighEmphasis, - ), - pollOptionsShowAllVotesButtonStyle: TextButton.styleFrom( - textStyle: textTheme.headline, - foregroundColor: colorTheme.accentPrimary, - ), - ), - pollOptionsDialogTheme: StreamPollOptionsDialogThemeData( - backgroundColor: colorTheme.appBg, - appBarElevation: 1, - appBarBackgroundColor: colorTheme.barsBg, - appBarTitleTextStyle: textTheme.headlineBold.copyWith( - color: colorTheme.textHighEmphasis, - ), - pollTitleTextStyle: textTheme.headlineBold.copyWith( - color: colorTheme.textHighEmphasis, - ), - pollTitleDecoration: BoxDecoration( - color: colorTheme.inputBg, - borderRadius: BorderRadius.circular(12), - ), - pollOptionsListViewDecoration: BoxDecoration( - color: colorTheme.inputBg, - borderRadius: BorderRadius.circular(12), - ), - ), - pollCommentsDialogTheme: StreamPollCommentsDialogThemeData( - backgroundColor: colorTheme.appBg, - appBarElevation: 1, - appBarBackgroundColor: colorTheme.barsBg, - appBarTitleTextStyle: textTheme.headlineBold.copyWith( - color: colorTheme.textHighEmphasis, - ), - pollCommentItemBackgroundColor: colorTheme.inputBg, - pollCommentItemBorderRadius: BorderRadius.circular(12), - updateYourCommentButtonStyle: TextButton.styleFrom( - textStyle: textTheme.headlineBold, - foregroundColor: colorTheme.accentPrimary, - backgroundColor: colorTheme.inputBg, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - padding: const EdgeInsets.symmetric( - vertical: 18, - horizontal: 16, - ), - ), - ), - pollOptionVotesDialogTheme: StreamPollOptionVotesDialogThemeData( - backgroundColor: colorTheme.appBg, - appBarElevation: 1, - appBarBackgroundColor: colorTheme.barsBg, - appBarTitleTextStyle: textTheme.headlineBold.copyWith( - color: colorTheme.textHighEmphasis, - ), - pollOptionVoteCountTextStyle: textTheme.headline.copyWith( - color: colorTheme.textHighEmphasis, - ), - pollOptionWinnerVoteCountTextStyle: textTheme.headline.copyWith( - color: colorTheme.textHighEmphasis, - ), - pollOptionVoteItemBackgroundColor: colorTheme.inputBg, - pollOptionVoteItemBorderRadius: BorderRadius.circular(12), - ), - threadListTileTheme: StreamThreadListTileThemeData( - backgroundColor: colorTheme.barsBg, - padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 8), - threadUnreadMessageCountStyle: textTheme.footnoteBold.copyWith( - color: Colors.white, - ), - threadUnreadMessageCountBackgroundColor: - channelPreviewTheme.unreadCounterColor, - threadChannelNameStyle: textTheme.bodyBold.copyWith( - color: colorTheme.textHighEmphasis, - ), - threadReplyToMessageStyle: textTheme.footnote.copyWith( - color: colorTheme.textLowEmphasis, - ), - threadLatestReplyTimestampStyle: textTheme.footnote.copyWith( - color: colorTheme.textLowEmphasis, - ), - threadLatestReplyUsernameStyle: textTheme.bodyBold.copyWith( - color: colorTheme.textHighEmphasis, - ), - threadLatestReplyMessageStyle: textTheme.body.copyWith( - color: colorTheme.textLowEmphasis, - ), - ), - audioWaveformTheme: audioWaveformTheme, - audioWaveformSliderTheme: audioWaveformSliderTheme, - voiceRecordingAttachmentTheme: StreamVoiceRecordingAttachmentThemeData( - backgroundColor: colorTheme.barsBg, - playIcon: const StreamSvgIcon(icon: StreamSvgIcons.play), - pauseIcon: const StreamSvgIcon(icon: StreamSvgIcons.pause), - loadingIndicator: SizedBox.fromSize( - size: const Size.square(24 - /* Padding */ 2), - child: Center( - child: CircularProgressIndicator.adaptive( - valueColor: AlwaysStoppedAnimation(colorTheme.accentPrimary), - ), - ), - ), - audioControlButtonStyle: ElevatedButton.styleFrom( - elevation: 2, - iconColor: Colors.black, - padding: const EdgeInsets.symmetric(horizontal: 6), - backgroundColor: Colors.white, - shape: const CircleBorder(), - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - minimumSize: const Size(36, 36), - ), - titleTextStyle: textTheme.bodyBold.copyWith( - color: colorTheme.textHighEmphasis, - ), - durationTextStyle: textTheme.footnote.copyWith( - color: colorTheme.textLowEmphasis, - ), - speedControlButtonStyle: ElevatedButton.styleFrom( - elevation: 2, - textStyle: textTheme.footnote, - foregroundColor: Colors.black, - padding: const EdgeInsets.symmetric(horizontal: 8), - backgroundColor: Colors.white, - shape: const StadiumBorder(), - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - minimumSize: const Size(40, 28), - ), - audioWaveformSliderTheme: audioWaveformSliderTheme, - ), - voiceRecordingTheme: colorTheme.brightness == Brightness.dark - ? StreamVoiceRecordingThemeData.dark() - : StreamVoiceRecordingThemeData.light(), - ); - } - - /// The text themes used in the widgets - final StreamTextTheme textTheme; - - /// The color themes used in the widgets - final StreamColorTheme colorTheme; - - /// Theme of the [StreamChannelPreview] - final StreamChannelPreviewThemeData channelPreviewTheme; - - /// Theme of the [StreamChannelListHeader] - final StreamChannelListHeaderThemeData channelListHeaderTheme; - - /// Theme of the chat widgets dedicated to a channel header - final StreamChannelHeaderThemeData channelHeaderTheme; - - /// The default style for [StreamGalleryHeader]s below the overall - /// [StreamChatTheme]. - final StreamGalleryHeaderThemeData galleryHeaderTheme; - - /// The default style for [StreamGalleryFooter]s below the overall - /// [StreamChatTheme]. - final StreamGalleryFooterThemeData galleryFooterTheme; - - /// Theme of the current user messages - final StreamMessageThemeData ownMessageTheme; - - /// Theme of other users messages - final StreamMessageThemeData otherMessageTheme; - - /// Theme dedicated to the [StreamMessageInput] widget - final StreamMessageInputThemeData messageInputTheme; - - /// Primary icon theme - final IconThemeData primaryIconTheme; - - /// Theme configuration for the [StreamMessageListView] widget. - final StreamMessageListViewThemeData messageListViewTheme; - - /// Theme configuration for the [StreamVoiceRecordingListPLayer] widget. - @Deprecated("Use 'StreamChatThemeData.voiceRecordingAttachmentTheme' instead") - final StreamVoiceRecordingThemeData voiceRecordingTheme; - - /// Theme configuration for the [StreamPollCreatorWidget] widget. - final StreamPollCreatorThemeData pollCreatorTheme; - - /// Theme configuration for the [StreamPollInteractor] widget. - final StreamPollInteractorThemeData pollInteractorTheme; - - /// Theme configuration for the [StreamPollResultsDialog] widget. - final StreamPollResultsDialogThemeData pollResultsDialogTheme; - - /// Theme configuration for the [StreamPollOptionsDialog] widget. - final StreamPollOptionsDialogThemeData pollOptionsDialogTheme; - - /// Theme configuration for the [StreamPollCommentsDialog] widget. - final StreamPollCommentsDialogThemeData pollCommentsDialogTheme; - - /// Theme configuration for the [StreamPollOptionVotesDialog] widget. - final StreamPollOptionVotesDialogThemeData pollOptionVotesDialogTheme; - - /// Theme configuration for the [StreamThreadListTile] widget. - final StreamThreadListTileThemeData threadListTileTheme; - - /// Theme configuration for the [StreamAudioWaveform] widget. - final StreamAudioWaveformThemeData audioWaveformTheme; - - /// Theme configuration for the [StreamAudioWaveformSlider] widget. - final StreamAudioWaveformSliderThemeData audioWaveformSliderTheme; - - /// Theme configuration for the [StreamVoiceRecordingAttachment] widget. - final StreamVoiceRecordingAttachmentThemeData voiceRecordingAttachmentTheme; - - /// Creates a copy of [StreamChatThemeData] with specified attributes - /// overridden. - StreamChatThemeData copyWith({ - StreamTextTheme? textTheme, - StreamColorTheme? colorTheme, - StreamChannelPreviewThemeData? channelPreviewTheme, - StreamChannelHeaderThemeData? channelHeaderTheme, - StreamMessageThemeData? ownMessageTheme, - StreamMessageThemeData? otherMessageTheme, - StreamMessageInputThemeData? messageInputTheme, - Widget Function(BuildContext, User)? defaultUserImage, - PlaceholderUserImage? placeholderUserImage, - IconThemeData? primaryIconTheme, - StreamChannelListHeaderThemeData? channelListHeaderTheme, - @Deprecated('Use StreamChatConfigurationData.reactionIcons instead') - List? reactionIcons, - StreamGalleryHeaderThemeData? galleryHeaderTheme, - StreamGalleryFooterThemeData? galleryFooterTheme, - StreamMessageListViewThemeData? messageListViewTheme, - @Deprecated("Use 'voiceRecordingAttachmentTheme' instead") - StreamVoiceRecordingThemeData? voiceRecordingTheme, - StreamPollCreatorThemeData? pollCreatorTheme, - StreamPollInteractorThemeData? pollInteractorTheme, - StreamPollResultsDialogThemeData? pollResultsDialogTheme, - StreamPollOptionsDialogThemeData? pollOptionsDialogTheme, - StreamPollCommentsDialogThemeData? pollCommentsDialogTheme, - StreamPollOptionVotesDialogThemeData? pollOptionVotesDialogTheme, - StreamThreadListTileThemeData? threadListTileTheme, - StreamAudioWaveformThemeData? audioWaveformTheme, - StreamAudioWaveformSliderThemeData? audioWaveformSliderTheme, - StreamVoiceRecordingAttachmentThemeData? voiceRecordingAttachmentTheme, - }) => - StreamChatThemeData.raw( - channelListHeaderTheme: - this.channelListHeaderTheme.merge(channelListHeaderTheme), - textTheme: this.textTheme.merge(textTheme), - colorTheme: this.colorTheme.merge(colorTheme), - primaryIconTheme: this.primaryIconTheme.merge(primaryIconTheme), - channelPreviewTheme: - this.channelPreviewTheme.merge(channelPreviewTheme), - channelHeaderTheme: this.channelHeaderTheme.merge(channelHeaderTheme), - ownMessageTheme: this.ownMessageTheme.merge(ownMessageTheme), - otherMessageTheme: this.otherMessageTheme.merge(otherMessageTheme), - messageInputTheme: this.messageInputTheme.merge(messageInputTheme), - galleryHeaderTheme: galleryHeaderTheme ?? this.galleryHeaderTheme, - galleryFooterTheme: galleryFooterTheme ?? this.galleryFooterTheme, - messageListViewTheme: messageListViewTheme ?? this.messageListViewTheme, - voiceRecordingTheme: voiceRecordingTheme ?? this.voiceRecordingTheme, - pollCreatorTheme: pollCreatorTheme ?? this.pollCreatorTheme, - pollInteractorTheme: pollInteractorTheme ?? this.pollInteractorTheme, - pollResultsDialogTheme: - pollResultsDialogTheme ?? this.pollResultsDialogTheme, - pollOptionsDialogTheme: - pollOptionsDialogTheme ?? this.pollOptionsDialogTheme, - pollCommentsDialogTheme: - pollCommentsDialogTheme ?? this.pollCommentsDialogTheme, - pollOptionVotesDialogTheme: - pollOptionVotesDialogTheme ?? this.pollOptionVotesDialogTheme, - threadListTileTheme: threadListTileTheme ?? this.threadListTileTheme, - audioWaveformTheme: audioWaveformTheme ?? this.audioWaveformTheme, - audioWaveformSliderTheme: - audioWaveformSliderTheme ?? this.audioWaveformSliderTheme, - voiceRecordingAttachmentTheme: - voiceRecordingAttachmentTheme ?? this.voiceRecordingAttachmentTheme, - ); - - /// Merge themes - StreamChatThemeData merge(StreamChatThemeData? other) { - if (other == null) return this; - return copyWith( - channelListHeaderTheme: - channelListHeaderTheme.merge(other.channelListHeaderTheme), - textTheme: textTheme.merge(other.textTheme), - colorTheme: colorTheme.merge(other.colorTheme), - primaryIconTheme: other.primaryIconTheme, - channelPreviewTheme: channelPreviewTheme.merge(other.channelPreviewTheme), - channelHeaderTheme: channelHeaderTheme.merge(other.channelHeaderTheme), - ownMessageTheme: ownMessageTheme.merge(other.ownMessageTheme), - otherMessageTheme: otherMessageTheme.merge(other.otherMessageTheme), - messageInputTheme: messageInputTheme.merge(other.messageInputTheme), - galleryHeaderTheme: galleryHeaderTheme.merge(other.galleryHeaderTheme), - galleryFooterTheme: galleryFooterTheme.merge(other.galleryFooterTheme), - messageListViewTheme: - messageListViewTheme.merge(other.messageListViewTheme), - voiceRecordingTheme: voiceRecordingTheme.merge(other.voiceRecordingTheme), - pollCreatorTheme: pollCreatorTheme.merge(other.pollCreatorTheme), - pollInteractorTheme: pollInteractorTheme.merge(other.pollInteractorTheme), - pollResultsDialogTheme: - pollResultsDialogTheme.merge(other.pollResultsDialogTheme), - pollOptionsDialogTheme: - pollOptionsDialogTheme.merge(other.pollOptionsDialogTheme), - pollCommentsDialogTheme: - pollCommentsDialogTheme.merge(other.pollCommentsDialogTheme), - pollOptionVotesDialogTheme: - pollOptionVotesDialogTheme.merge(other.pollOptionVotesDialogTheme), - threadListTileTheme: threadListTileTheme.merge(other.threadListTileTheme), - audioWaveformTheme: audioWaveformTheme.merge(other.audioWaveformTheme), - audioWaveformSliderTheme: - audioWaveformSliderTheme.merge(other.audioWaveformSliderTheme), - voiceRecordingAttachmentTheme: voiceRecordingAttachmentTheme - .merge(other.voiceRecordingAttachmentTheme), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/theme/text_theme.dart b/packages/stream_chat_flutter/lib/src/theme/text_theme.dart deleted file mode 100644 index 9662b4bcd9..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/text_theme.dart +++ /dev/null @@ -1,167 +0,0 @@ -import 'package:flutter/material.dart'; - -/// {@template text_theme} -/// Class for holding text theme -/// {@endtemplate} -class StreamTextTheme { - /// Initialise light text theme - StreamTextTheme.light({ - this.title = const TextStyle( - fontSize: 22, - fontWeight: FontWeight.w500, - color: Colors.black, - ), - this.headline = const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - color: Colors.black, - ), - this.headlineBold = const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Colors.black, - ), - this.body = const TextStyle( - fontSize: 14, - fontWeight: FontWeight.w400, - color: Colors.black, - ), - this.bodyBold = const TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: Colors.black, - ), - this.footnote = const TextStyle( - fontSize: 12, - fontWeight: FontWeight.w400, - color: Colors.black, - ), - this.footnoteBold = const TextStyle( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Colors.black, - ), - this.captionBold = const TextStyle( - fontSize: 10, - fontWeight: FontWeight.w700, - color: Colors.black, - ), - }); - - /// Initialise with dark theme - StreamTextTheme.dark({ - this.title = const TextStyle( - fontSize: 22, - fontWeight: FontWeight.w500, - color: Colors.white, - ), - this.headline = const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - color: Colors.white, - ), - this.headlineBold = const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Colors.white, - ), - this.body = const TextStyle( - fontSize: 14, - fontWeight: FontWeight.w400, - color: Colors.white, - ), - this.bodyBold = const TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: Colors.white, - ), - this.footnote = const TextStyle( - fontSize: 12, - fontWeight: FontWeight.w400, - color: Colors.white, - ), - this.footnoteBold = const TextStyle( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Colors.white, - ), - this.captionBold = const TextStyle( - fontSize: 10, - fontWeight: FontWeight.w700, - color: Colors.white, - ), - }); - - /// Text theme for title - final TextStyle title; - - /// Body Text theme for headline - final TextStyle headlineBold; - - /// Text theme for headline - final TextStyle headline; - - /// Bold Text theme for body - final TextStyle bodyBold; - - /// Text theme body - final TextStyle body; - - /// Bold Text theme for footnote - final TextStyle footnoteBold; - - /// Text theme for footnote - final TextStyle footnote; - - /// Bold Text theme for caption - final TextStyle captionBold; - - /// Copy with theme - StreamTextTheme copyWith({ - Brightness brightness = Brightness.light, - TextStyle? body, - TextStyle? title, - TextStyle? headlineBold, - TextStyle? headline, - TextStyle? bodyBold, - TextStyle? footnoteBold, - TextStyle? footnote, - TextStyle? captionBold, - }) => - brightness == Brightness.light - ? StreamTextTheme.light( - body: body ?? this.body, - title: title ?? this.title, - headlineBold: headlineBold ?? this.headlineBold, - headline: headline ?? this.headline, - bodyBold: bodyBold ?? this.bodyBold, - footnoteBold: footnoteBold ?? this.footnoteBold, - footnote: footnote ?? this.footnote, - captionBold: captionBold ?? this.captionBold, - ) - : StreamTextTheme.dark( - body: body ?? this.body, - title: title ?? this.title, - headlineBold: headlineBold ?? this.headlineBold, - headline: headline ?? this.headline, - bodyBold: bodyBold ?? this.bodyBold, - footnoteBold: footnoteBold ?? this.footnoteBold, - footnote: footnote ?? this.footnote, - captionBold: captionBold ?? this.captionBold, - ); - - /// Merge text theme - StreamTextTheme merge(StreamTextTheme? other) { - if (other == null) return this; - return copyWith( - body: body.merge(other.body), - title: title.merge(other.title), - headlineBold: headlineBold.merge(other.headlineBold), - headline: headline.merge(other.headline), - bodyBold: bodyBold.merge(other.bodyBold), - footnoteBold: footnoteBold.merge(other.footnoteBold), - footnote: footnote.merge(other.footnote), - captionBold: captionBold.merge(other.captionBold), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/theme/themes.dart b/packages/stream_chat_flutter/lib/src/theme/themes.dart deleted file mode 100644 index 9e41282aa6..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/themes.dart +++ /dev/null @@ -1,22 +0,0 @@ -export 'audio_waveform_slider_theme.dart'; -export 'audio_waveform_theme.dart'; -export 'avatar_theme.dart'; -export 'channel_header_theme.dart'; -export 'channel_list_header_theme.dart'; -export 'channel_preview_theme.dart'; -export 'color_theme.dart'; -export 'gallery_footer_theme.dart'; -export 'gallery_header_theme.dart'; -export 'message_input_theme.dart'; -export 'message_list_view_theme.dart'; -export 'message_theme.dart'; -export 'poll_comments_dialog_theme.dart'; -export 'poll_creator_theme.dart'; -export 'poll_interactor_theme.dart'; -export 'poll_option_votes_dialog_theme.dart'; -export 'poll_options_dialog_theme.dart'; -export 'poll_results_dialog_theme.dart'; -export 'text_theme.dart'; -export 'thread_list_tile_theme.dart'; -export 'voice_attachment_theme.dart'; -export 'voice_recording_attachment_theme.dart'; diff --git a/packages/stream_chat_flutter/lib/src/theme/thread_list_tile_theme.dart b/packages/stream_chat_flutter/lib/src/theme/thread_list_tile_theme.dart deleted file mode 100644 index 0597ba0261..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/thread_list_tile_theme.dart +++ /dev/null @@ -1,225 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -/// {@template streamThreadListTileTheme} -/// Overrides the default style of [StreamThreadListTile] descendants. -/// -/// See also: -/// -/// * [StreamPollOptionVotesDialogThemeData], which is used to configure this -/// theme. -/// {@endtemplate} -class StreamThreadListTileTheme extends InheritedTheme { - /// Creates a [StreamThreadListTileTheme]. - /// - /// The [data] parameter must not be null. - const StreamThreadListTileTheme({ - super.key, - required this.data, - required super.child, - }); - - /// The configuration of this theme. - final StreamThreadListTileThemeData data; - - /// The closest instance of this class that encloses the given context. - /// - /// If there is no enclosing [StreamThreadListTileTheme] widget, then - /// [StreamChatThemeData.pollOptionVotesDialogTheme] is used. - static StreamThreadListTileThemeData of(BuildContext context) { - final threadListTileTheme = - context.dependOnInheritedWidgetOfExactType(); - return threadListTileTheme?.data ?? - StreamChatTheme.of(context).threadListTileTheme; - } - - @override - Widget wrap(BuildContext context, Widget child) => - StreamThreadListTileTheme(data: data, child: child); - - @override - bool updateShouldNotify(StreamThreadListTileTheme oldWidget) => - data != oldWidget.data; -} - -/// {@template streamThreadListTileThemeData} -/// A style that overrides the default appearance of -/// [StreamPollOptionVotesDialog] widgets when used with -/// [StreamPollCommentsDialogTheme] or with the overall [StreamChatTheme]'s -/// [StreamChatThemeData.pollOptionVotesDialogTheme]. -/// {@endtemplate} -class StreamThreadListTileThemeData with Diagnosticable { - /// {@macro streamThreadListTileThemeData} - const StreamThreadListTileThemeData({ - this.padding, - this.backgroundColor, - this.threadChannelNameStyle, - this.threadReplyToMessageStyle, - this.threadLatestReplyUsernameStyle, - this.threadLatestReplyMessageStyle, - this.threadLatestReplyTimestampStyle, - this.threadUnreadMessageCountStyle, - this.threadUnreadMessageCountBackgroundColor, - }); - - /// The padding around the [StreamThreadListTile] widget. - final EdgeInsetsGeometry? padding; - - /// The background color of the [StreamThreadListTile] widget. - final Color? backgroundColor; - - /// The style of the channel name in the [StreamThreadListTile] widget. - final TextStyle? threadChannelNameStyle; - - /// The style of the message the thread is replying to in the - /// [StreamThreadListTile] widget. - final TextStyle? threadReplyToMessageStyle; - - /// The style of the latest reply author username in the - /// [StreamThreadListTile] widget. - final TextStyle? threadLatestReplyUsernameStyle; - - /// The style of the latest reply message in the [StreamThreadListTile]. - /// widget. - final TextStyle? threadLatestReplyMessageStyle; - - /// The style of the latest reply timestamp in the [StreamThreadListTile]. - final TextStyle? threadLatestReplyTimestampStyle; - - /// The style of the unread message count in the [StreamThreadListTile]. - final TextStyle? threadUnreadMessageCountStyle; - - /// The background color of the unread message count in the - /// [StreamThreadListTile]. - final Color? threadUnreadMessageCountBackgroundColor; - - /// A copy of [StreamThreadListTileThemeData] with specified attributes - /// overridden. - StreamThreadListTileThemeData copyWith({ - EdgeInsetsGeometry? padding, - Color? backgroundColor, - TextStyle? threadChannelNameStyle, - TextStyle? threadReplyToMessageStyle, - TextStyle? threadLatestReplyUsernameStyle, - TextStyle? threadLatestReplyMessageStyle, - TextStyle? threadLatestReplyTimestampStyle, - TextStyle? threadUnreadMessageCountStyle, - Color? threadUnreadMessageCountBackgroundColor, - }) => - StreamThreadListTileThemeData( - padding: padding ?? this.padding, - backgroundColor: backgroundColor ?? this.backgroundColor, - threadChannelNameStyle: - threadChannelNameStyle ?? this.threadChannelNameStyle, - threadReplyToMessageStyle: - threadReplyToMessageStyle ?? this.threadReplyToMessageStyle, - threadLatestReplyUsernameStyle: threadLatestReplyUsernameStyle ?? - this.threadLatestReplyUsernameStyle, - threadLatestReplyMessageStyle: - threadLatestReplyMessageStyle ?? this.threadLatestReplyMessageStyle, - threadLatestReplyTimestampStyle: threadLatestReplyTimestampStyle ?? - this.threadLatestReplyTimestampStyle, - threadUnreadMessageCountStyle: - threadUnreadMessageCountStyle ?? this.threadUnreadMessageCountStyle, - threadUnreadMessageCountBackgroundColor: - threadUnreadMessageCountBackgroundColor ?? - this.threadUnreadMessageCountBackgroundColor, - ); - - /// Merges this [StreamThreadListTileThemeData] with the [other]. - StreamThreadListTileThemeData merge( - StreamThreadListTileThemeData? other, - ) { - if (other == null) return this; - return copyWith( - padding: other.padding, - backgroundColor: other.backgroundColor, - threadChannelNameStyle: other.threadChannelNameStyle, - threadReplyToMessageStyle: other.threadReplyToMessageStyle, - threadLatestReplyUsernameStyle: other.threadLatestReplyUsernameStyle, - threadLatestReplyMessageStyle: other.threadLatestReplyMessageStyle, - threadLatestReplyTimestampStyle: other.threadLatestReplyTimestampStyle, - threadUnreadMessageCountStyle: other.threadUnreadMessageCountStyle, - threadUnreadMessageCountBackgroundColor: - other.threadUnreadMessageCountBackgroundColor, - ); - } - - /// Linearly interpolate between two [StreamThreadListTileThemeData]. - StreamThreadListTileThemeData lerp( - StreamThreadListTileThemeData? a, - StreamThreadListTileThemeData? b, - double t, - ) => - StreamThreadListTileThemeData( - padding: EdgeInsetsGeometry.lerp(a?.padding, b?.padding, t), - backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t), - threadChannelNameStyle: TextStyle.lerp( - a?.threadChannelNameStyle, - b?.threadChannelNameStyle, - t, - ), - threadReplyToMessageStyle: TextStyle.lerp( - a?.threadReplyToMessageStyle, - b?.threadReplyToMessageStyle, - t, - ), - threadLatestReplyUsernameStyle: TextStyle.lerp( - a?.threadLatestReplyUsernameStyle, - b?.threadLatestReplyUsernameStyle, - t, - ), - threadLatestReplyMessageStyle: TextStyle.lerp( - a?.threadLatestReplyMessageStyle, - b?.threadLatestReplyMessageStyle, - t, - ), - threadLatestReplyTimestampStyle: TextStyle.lerp( - a?.threadLatestReplyTimestampStyle, - b?.threadLatestReplyTimestampStyle, - t, - ), - threadUnreadMessageCountStyle: TextStyle.lerp( - a?.threadUnreadMessageCountStyle, - b?.threadUnreadMessageCountStyle, - t, - ), - threadUnreadMessageCountBackgroundColor: Color.lerp( - a?.threadUnreadMessageCountBackgroundColor, - b?.threadUnreadMessageCountBackgroundColor, - t, - ), - ); - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StreamThreadListTileThemeData && - other.padding == padding && - other.backgroundColor == backgroundColor && - other.threadChannelNameStyle == threadChannelNameStyle && - other.threadReplyToMessageStyle == threadReplyToMessageStyle && - other.threadLatestReplyUsernameStyle == - threadLatestReplyUsernameStyle && - other.threadLatestReplyMessageStyle == - threadLatestReplyMessageStyle && - other.threadLatestReplyTimestampStyle == - threadLatestReplyTimestampStyle && - other.threadUnreadMessageCountStyle == - threadUnreadMessageCountStyle && - other.threadUnreadMessageCountBackgroundColor == - threadUnreadMessageCountBackgroundColor; - - @override - int get hashCode => - padding.hashCode ^ - backgroundColor.hashCode ^ - threadChannelNameStyle.hashCode ^ - threadReplyToMessageStyle.hashCode ^ - threadLatestReplyUsernameStyle.hashCode ^ - threadLatestReplyMessageStyle.hashCode ^ - threadLatestReplyTimestampStyle.hashCode ^ - threadUnreadMessageCountStyle.hashCode ^ - threadUnreadMessageCountBackgroundColor.hashCode; -} diff --git a/packages/stream_chat_flutter/lib/src/theme/voice_attachment_theme.dart b/packages/stream_chat_flutter/lib/src/theme/voice_attachment_theme.dart deleted file mode 100644 index db1ed7db65..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/voice_attachment_theme.dart +++ /dev/null @@ -1,466 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template StreamVoiceRecordingThemeData} -/// The theme data for the voice recording attachment builder. -/// {@endtemplate} -@Deprecated("Use 'StreamVoiceRecordingAttachmentThemeData' instead.") -class StreamVoiceRecordingThemeData with Diagnosticable { - /// {@macro StreamVoiceRecordingThemeData} - const StreamVoiceRecordingThemeData({ - required this.loadingTheme, - required this.sliderTheme, - required this.listPlayerTheme, - required this.playerTheme, - }); - - /// {@template ThemeDataLight} - /// Creates a theme data with light values. - /// {@endtemplate} - factory StreamVoiceRecordingThemeData.light() { - return StreamVoiceRecordingThemeData( - loadingTheme: StreamVoiceRecordingLoadingThemeData.light(), - sliderTheme: StreamVoiceRecordingSliderTheme.light(), - listPlayerTheme: StreamVoiceRecordingListPlayerThemeData.light(), - playerTheme: StreamVoiceRecordingPlayerThemeData.light(), - ); - } - - /// {@template ThemeDataDark} - /// Creates a theme data with dark values. - /// {@endtemplate} - factory StreamVoiceRecordingThemeData.dark() { - return StreamVoiceRecordingThemeData( - loadingTheme: StreamVoiceRecordingLoadingThemeData.dark(), - sliderTheme: StreamVoiceRecordingSliderTheme.dark(), - listPlayerTheme: StreamVoiceRecordingListPlayerThemeData.dark(), - playerTheme: StreamVoiceRecordingPlayerThemeData.dark(), - ); - } - - /// The theme for the loading widget. - final StreamVoiceRecordingLoadingThemeData loadingTheme; - - /// The theme for the slider widget. - final StreamVoiceRecordingSliderTheme sliderTheme; - - /// The theme for the list player widget. - final StreamVoiceRecordingListPlayerThemeData listPlayerTheme; - - /// The theme for the player widget. - final StreamVoiceRecordingPlayerThemeData playerTheme; - - /// {@template ThemeDataMerge} - /// Used to merge the values of another theme data object into this. - /// {@endtemplate} - StreamVoiceRecordingThemeData merge(StreamVoiceRecordingThemeData? other) { - if (other == null) return this; - return StreamVoiceRecordingThemeData( - loadingTheme: loadingTheme.merge(other.loadingTheme), - sliderTheme: sliderTheme.merge(other.sliderTheme), - listPlayerTheme: listPlayerTheme.merge(other.listPlayerTheme), - playerTheme: playerTheme.merge(other.playerTheme), - ); - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty('loadingTheme', loadingTheme)) - ..add(DiagnosticsProperty('sliderTheme', sliderTheme)) - ..add(DiagnosticsProperty('listPlayerTheme', listPlayerTheme)) - ..add(DiagnosticsProperty('playerTheme', playerTheme)); - } -} - -/// {@template StreamAudioPlayerLoadingTheme} -/// The theme data for the voice recording attachment builder -/// loading widget [StreamVoiceRecordingLoading]. -/// {@endtemplate} -@Deprecated("Use 'StreamVoiceRecordingAttachmentThemeData' instead.") -class StreamVoiceRecordingLoadingThemeData with Diagnosticable { - /// {@macro StreamAudioPlayerLoadingTheme} - const StreamVoiceRecordingLoadingThemeData({ - this.size, - this.strokeWidth, - this.color, - this.padding, - }); - - /// {@macro ThemeDataLight} - factory StreamVoiceRecordingLoadingThemeData.light() { - return const StreamVoiceRecordingLoadingThemeData( - size: Size(20, 20), - strokeWidth: 2, - color: Color(0xFF005FFF), - padding: EdgeInsets.all(8), - ); - } - - /// {@macro ThemeDataDark} - factory StreamVoiceRecordingLoadingThemeData.dark() { - return const StreamVoiceRecordingLoadingThemeData( - size: Size(20, 20), - strokeWidth: 2, - color: Color(0xFF005FFF), - padding: EdgeInsets.all(8), - ); - } - - /// The size of the loading indicator. - final Size? size; - - /// The stroke width of the loading indicator. - final double? strokeWidth; - - /// The color of the loading indicator. - final Color? color; - - /// The padding around the loading indicator. - final EdgeInsets? padding; - - /// {@macro ThemeDataMerge} - StreamVoiceRecordingLoadingThemeData merge( - StreamVoiceRecordingLoadingThemeData? other) { - if (other == null) return this; - return StreamVoiceRecordingLoadingThemeData( - size: other.size, - strokeWidth: other.strokeWidth, - color: other.color, - padding: other.padding, - ); - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty('size', size)) - ..add(DiagnosticsProperty('strokeWidth', strokeWidth)) - ..add(ColorProperty('color', color)) - ..add(DiagnosticsProperty('padding', padding)); - } -} - -/// {@template StreamAudioPlayerSliderTheme} -/// The theme data for the voice recording attachment builder audio player -/// slider [StreamVoiceRecordingSlider]. -/// {@endtemplate} -@Deprecated("Use 'StreamVoiceRecordingAttachmentThemeData' instead.") -class StreamVoiceRecordingSliderTheme with Diagnosticable { - /// {@macro StreamAudioPlayerSliderTheme} - const StreamVoiceRecordingSliderTheme({ - this.horizontalPadding = 10, - this.spacingRatio = 0.007, - this.waveHeightRatio = 1, - this.buttonBorderRadius = const BorderRadius.all(Radius.circular(8)), - this.buttonColor, - this.buttonBorderColor, - this.buttonBorderWidth = 1, - this.waveColorPlayed, - this.waveColorUnplayed, - this.buttonShadow = const BoxShadow( - color: Color(0x33000000), - blurRadius: 4, - offset: Offset(0, 2), - ), - }); - - /// {@macro ThemeDataLight} - factory StreamVoiceRecordingSliderTheme.light() { - return const StreamVoiceRecordingSliderTheme( - buttonColor: Color(0xFFFFFFFF), - buttonBorderColor: Color(0x3308070733), - waveColorPlayed: Color(0xFF005DFF), - waveColorUnplayed: Color(0xFF7E828B), - ); - } - - /// {@macro ThemeDataDark} - factory StreamVoiceRecordingSliderTheme.dark() { - return const StreamVoiceRecordingSliderTheme( - buttonColor: Color(0xFF005FFF), - buttonBorderColor: Color(0x3308070766), - waveColorPlayed: Color(0xFF337EFF), - waveColorUnplayed: Color(0xFF7E828B), - ); - } - - /// The color of the slider button. - final Color? buttonColor; - - /// The color of the border of the slider button. - final Color? buttonBorderColor; - - /// The width of the border of the slider button. - final double? buttonBorderWidth; - - /// The shadow of the slider button. - final BoxShadow? buttonShadow; - - /// The border radius of the slider button. - final BorderRadius buttonBorderRadius; - - /// The horizontal padding of the slider. - final double horizontalPadding; - - /// Spacing ratios. This is the percentage that the space takes from the whole - /// available space. Typically this value should be between 0.003 to 0.02. - /// Default = 0.01 - final double spacingRatio; - - /// The percentage maximum value of waves. This can be used to reduce the - /// height of bars. Default = 1; - final double waveHeightRatio; - - /// Color of the waves to the left side of the slider button. - final Color? waveColorPlayed; - - /// Color of the waves to the right side of the slider button. - final Color? waveColorUnplayed; - - /// {@macro ThemeDataMerge} - StreamVoiceRecordingSliderTheme merge( - StreamVoiceRecordingSliderTheme? other) { - if (other == null) return this; - return StreamVoiceRecordingSliderTheme( - buttonColor: other.buttonColor, - buttonBorderColor: other.buttonBorderColor, - buttonBorderRadius: other.buttonBorderRadius, - horizontalPadding: other.horizontalPadding, - spacingRatio: other.spacingRatio, - waveHeightRatio: other.waveHeightRatio, - waveColorPlayed: other.waveColorPlayed, - waveColorUnplayed: other.waveColorUnplayed, - ); - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(ColorProperty('buttonColor', buttonColor)) - ..add(ColorProperty('buttonBorderColor', buttonBorderColor)) - ..add(DiagnosticsProperty('buttonBorderRadius', buttonBorderRadius)) - ..add(DoubleProperty('horizontalPadding', horizontalPadding)) - ..add(DoubleProperty('spacingRatio', spacingRatio)) - ..add(DoubleProperty('waveHeightRatio', waveHeightRatio)) - ..add(ColorProperty('waveColorPlayed', waveColorPlayed)) - ..add(ColorProperty('waveColorUnplayed', waveColorUnplayed)); - } -} - -/// {@template StreamAudioListPlayerTheme} -/// The theme data for the voice recording attachment builder audio player -/// [StreamVoiceRecordingListPlayer]. -/// {@endtemplate} -@Deprecated("Use 'StreamVoiceRecordingAttachmentThemeData' instead.") -class StreamVoiceRecordingListPlayerThemeData with Diagnosticable { - /// {@macro StreamAudioListPlayerTheme} - const StreamVoiceRecordingListPlayerThemeData({ - this.backgroundColor, - this.borderColor, - this.borderRadius, - this.margin, - }); - - /// {@macro ThemeDataLight} - factory StreamVoiceRecordingListPlayerThemeData.light() { - return StreamVoiceRecordingListPlayerThemeData( - backgroundColor: const Color(0xFFFFFFFF), - borderColor: const Color(0xFFDBDDE1), - borderRadius: BorderRadius.circular(14), - margin: const EdgeInsets.all(4), - ); - } - - /// {@macro ThemeDataDark} - factory StreamVoiceRecordingListPlayerThemeData.dark() { - return StreamVoiceRecordingListPlayerThemeData( - backgroundColor: const Color(0xFF17191C), - borderColor: const Color(0xFF272A30), - borderRadius: BorderRadius.circular(14), - margin: const EdgeInsets.all(4), - ); - } - - /// The background color of the list. - final Color? backgroundColor; - - /// The border color of the list. - final Color? borderColor; - - /// The border radius of the list. - final BorderRadius? borderRadius; - - /// The margin of the list. - final EdgeInsets? margin; - - /// {@macro ThemeDataMerge} - StreamVoiceRecordingListPlayerThemeData merge( - StreamVoiceRecordingListPlayerThemeData? other) { - if (other == null) return this; - return StreamVoiceRecordingListPlayerThemeData( - backgroundColor: other.backgroundColor, - borderColor: other.borderColor, - borderRadius: other.borderRadius, - margin: other.margin, - ); - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(ColorProperty('backgroundColor', backgroundColor)) - ..add(ColorProperty('borderColor', borderColor)) - ..add(DiagnosticsProperty('borderRadius', borderRadius)) - ..add(DiagnosticsProperty('margin', margin)); - } -} - -/// {@template StreamVoiceRecordingPlayerTheme} -/// The theme data for the voice recording attachment builder audio player -/// {@endtemplate} -@Deprecated("Use 'StreamVoiceRecordingAttachmentThemeData' instead.") -class StreamVoiceRecordingPlayerThemeData with Diagnosticable { - /// {@macro StreamVoiceRecordingPlayerTheme} - const StreamVoiceRecordingPlayerThemeData({ - this.playIcon = Icons.play_arrow, - this.pauseIcon = Icons.pause, - this.iconColor, - this.buttonBackgroundColor, - this.buttonPadding = const EdgeInsets.symmetric(horizontal: 6), - this.buttonShape = const CircleBorder(), - this.buttonElevation = 2, - this.speedButtonSize = const Size(44, 36), - this.speedButtonElevation = 2, - this.speedButtonPadding = const EdgeInsets.symmetric(horizontal: 8), - this.speedButtonBackgroundColor = const Color(0xFFFFFFFF), - this.speedButtonShape = const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(50)), - ), - this.speedButtonTextStyle = const TextStyle( - fontSize: 12, - color: Color(0xFF080707), - ), - this.fileTypeIcon = const StreamSvgIcon( - icon: StreamSvgIcons.filetypeAudioAac, - ), - this.fileSizeTextStyle = const TextStyle(fontSize: 10), - this.timerTextStyle, - }); - - /// {@macro ThemeDataLight} - factory StreamVoiceRecordingPlayerThemeData.light() { - return const StreamVoiceRecordingPlayerThemeData( - iconColor: Color(0xFF080707), - buttonBackgroundColor: Color(0xFFFFFFFF), - ); - } - - /// {@macro ThemeDataDark} - factory StreamVoiceRecordingPlayerThemeData.dark() { - return const StreamVoiceRecordingPlayerThemeData( - iconColor: Color(0xFF080707), - buttonBackgroundColor: Color(0xFFFFFFFF), - ); - } - - /// The icon to display when the player is paused/stopped. - final IconData playIcon; - - /// The icon to display when the player is playing. - final IconData pauseIcon; - - /// The color of the icons. - final Color? iconColor; - - /// The background color of the buttons. - final Color? buttonBackgroundColor; - - /// The padding of the buttons. - final EdgeInsets? buttonPadding; - - /// The shape of the buttons. - final OutlinedBorder? buttonShape; - - /// The elevation of the buttons. - final double? buttonElevation; - - /// The size of the speed button. - final Size? speedButtonSize; - - /// The elevation of the speed button. - final double? speedButtonElevation; - - /// The padding of the speed button. - final EdgeInsets? speedButtonPadding; - - /// The background color of the speed button. - final Color? speedButtonBackgroundColor; - - /// The shape of the speed button. - final OutlinedBorder? speedButtonShape; - - /// The text style of the speed button. - final TextStyle? speedButtonTextStyle; - - /// The icon to display for the file type. - final Widget? fileTypeIcon; - - /// The text style of the file size. - final TextStyle? fileSizeTextStyle; - - /// The text style of the timer. - final TextStyle? timerTextStyle; - - /// {@macro ThemeDataMerge} - StreamVoiceRecordingPlayerThemeData merge( - StreamVoiceRecordingPlayerThemeData? other) { - if (other == null) return this; - return StreamVoiceRecordingPlayerThemeData( - playIcon: other.playIcon, - pauseIcon: other.pauseIcon, - iconColor: other.iconColor, - buttonBackgroundColor: other.buttonBackgroundColor, - buttonPadding: other.buttonPadding, - buttonShape: other.buttonShape, - buttonElevation: other.buttonElevation, - speedButtonSize: other.speedButtonSize, - speedButtonElevation: other.speedButtonElevation, - speedButtonPadding: other.speedButtonPadding, - speedButtonBackgroundColor: other.speedButtonBackgroundColor, - speedButtonShape: other.speedButtonShape, - speedButtonTextStyle: other.speedButtonTextStyle, - fileTypeIcon: other.fileTypeIcon, - fileSizeTextStyle: other.fileSizeTextStyle, - timerTextStyle: other.timerTextStyle, - ); - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty('playIcon', playIcon)) - ..add(DiagnosticsProperty('pauseIcon', pauseIcon)) - ..add(ColorProperty('iconColor', iconColor)) - ..add(ColorProperty('buttonBackgroundColor', buttonBackgroundColor)) - ..add(DiagnosticsProperty('buttonPadding', buttonPadding)) - ..add(DiagnosticsProperty('buttonShape', buttonShape)) - ..add(DoubleProperty('buttonElevation', buttonElevation)) - ..add(DiagnosticsProperty('speedButtonSize', speedButtonSize)) - ..add(DoubleProperty('speedButtonElevation', speedButtonElevation)) - ..add(DiagnosticsProperty('speedButtonPadding', speedButtonPadding)) - ..add(ColorProperty( - 'speedButtonBackgroundColor', speedButtonBackgroundColor)) - ..add(DiagnosticsProperty('speedButtonShape', speedButtonShape)) - ..add(DiagnosticsProperty('speedButtonTextStyle', speedButtonTextStyle)) - ..add(DiagnosticsProperty('fileTypeIcon', fileTypeIcon)) - ..add(DiagnosticsProperty('fileSizeTextStyle', fileSizeTextStyle)) - ..add(DiagnosticsProperty('timerTextStyle', timerTextStyle)); - } -} diff --git a/packages/stream_chat_flutter/lib/src/theme/voice_recording_attachment_theme.dart b/packages/stream_chat_flutter/lib/src/theme/voice_recording_attachment_theme.dart deleted file mode 100644 index 6103156c04..0000000000 --- a/packages/stream_chat_flutter/lib/src/theme/voice_recording_attachment_theme.dart +++ /dev/null @@ -1,198 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/theme/audio_waveform_slider_theme.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -/// {@template streamVoiceRecordingAttachmentTheme} -/// Overrides the default style of [StreamVoiceRecordingAttachment] descendants. -/// -/// See also: -/// -/// * [StreamVoiceRecordingAttachmentThemeData], which is used to configure -/// this theme. -/// {@endtemplate} -class StreamVoiceRecordingAttachmentTheme extends InheritedTheme { - /// Creates a [StreamVoiceRecordingAttachmentTheme]. - /// - /// The [data] parameter must not be null. - const StreamVoiceRecordingAttachmentTheme({ - super.key, - required this.data, - required super.child, - }); - - /// The configuration of this theme. - final StreamVoiceRecordingAttachmentThemeData data; - - /// The closest instance of this class that encloses the given context. - /// - /// If there is no enclosing [StreamVoiceRecordingAttachmentTheme] widget, - /// then [StreamVoiceRecordingAttachmentTheme.voiceRecordingTheme] is used. - /// - /// Typical usage is as follows: - /// - /// ```dart - /// StreamVoiceRecordingAttachmentTheme theme = - /// StreamVoiceRecordingAttachmentTheme.of(context); - /// ``` - static StreamVoiceRecordingAttachmentThemeData of(BuildContext context) { - final voiceRecordingTheme = context.dependOnInheritedWidgetOfExactType< - StreamVoiceRecordingAttachmentTheme>(); - return voiceRecordingTheme?.data ?? - StreamChatTheme.of(context).voiceRecordingAttachmentTheme; - } - - @override - Widget wrap(BuildContext context, Widget child) => - StreamVoiceRecordingAttachmentTheme(data: data, child: child); - - @override - bool updateShouldNotify(StreamVoiceRecordingAttachmentTheme oldWidget) => - data != oldWidget.data; -} - -/// {@template streamVoiceRecordingAttachmentThemeData} -/// A style that overrides the default appearance of -/// [StreamVoiceRecordingAttachment] widgets when used with -/// [StreamVoiceRecordingAttachmentTheme] or with the overall -/// [StreamChatTheme]'s [StreamChatThemeData.voiceRecordingAttachmentTheme]. -/// {@endtemplate} -class StreamVoiceRecordingAttachmentThemeData with Diagnosticable { - /// {@macro streamVoiceRecordingAttachmentThemeData} - const StreamVoiceRecordingAttachmentThemeData({ - this.backgroundColor, - this.playIcon, - this.pauseIcon, - this.loadingIndicator, - this.audioControlButtonStyle, - this.titleTextStyle, - this.durationTextStyle, - this.speedControlButtonStyle, - this.audioWaveformSliderTheme, - }); - - /// The background color of the attachment. - final Color? backgroundColor; - - /// The icon widget to show when the recording is playing. - final Widget? playIcon; - - /// The icon widget to show when the recording is paused. - final Widget? pauseIcon; - - /// The widget to show when the recording is loading. - final Widget? loadingIndicator; - - /// The style for the audio control button. - final ButtonStyle? audioControlButtonStyle; - - /// The text style for the title. - final TextStyle? titleTextStyle; - - /// The text style for the duration. - final TextStyle? durationTextStyle; - - /// The style for the speed control button. - final ButtonStyle? speedControlButtonStyle; - - /// The theme for the audio waveform slider. - final StreamAudioWaveformSliderThemeData? audioWaveformSliderTheme; - - /// A copy of [StreamVoiceRecordingAttachmentThemeData] with specified - /// attributes overridden. - StreamVoiceRecordingAttachmentThemeData copyWith({ - Color? backgroundColor, - Widget? playIcon, - Widget? pauseIcon, - Widget? loadingIndicator, - ButtonStyle? audioControlButtonStyle, - TextStyle? titleTextStyle, - TextStyle? durationTextStyle, - ButtonStyle? speedControlButtonStyle, - StreamAudioWaveformSliderThemeData? audioWaveformSliderTheme, - }) => - StreamVoiceRecordingAttachmentThemeData( - backgroundColor: backgroundColor ?? this.backgroundColor, - playIcon: playIcon ?? this.playIcon, - pauseIcon: pauseIcon ?? this.pauseIcon, - loadingIndicator: loadingIndicator ?? this.loadingIndicator, - audioControlButtonStyle: - audioControlButtonStyle ?? this.audioControlButtonStyle, - titleTextStyle: titleTextStyle ?? this.titleTextStyle, - durationTextStyle: durationTextStyle ?? this.durationTextStyle, - speedControlButtonStyle: - speedControlButtonStyle ?? this.speedControlButtonStyle, - audioWaveformSliderTheme: - audioWaveformSliderTheme ?? this.audioWaveformSliderTheme, - ); - - /// Merges this [StreamVoiceRecordingAttachmentThemeData] with the [other]. - StreamVoiceRecordingAttachmentThemeData merge( - StreamVoiceRecordingAttachmentThemeData? other, - ) { - if (other == null) return this; - return copyWith( - backgroundColor: other.backgroundColor, - playIcon: other.playIcon, - pauseIcon: other.pauseIcon, - loadingIndicator: other.loadingIndicator, - audioControlButtonStyle: other.audioControlButtonStyle, - titleTextStyle: other.titleTextStyle, - durationTextStyle: other.durationTextStyle, - speedControlButtonStyle: other.speedControlButtonStyle, - audioWaveformSliderTheme: audioWaveformSliderTheme?.merge( - other.audioWaveformSliderTheme, - ), - ); - } - - /// Linearly interpolate between two [StreamVoiceRecordingAttachmentThemeData] - /// objects. - static StreamVoiceRecordingAttachmentThemeData lerp( - StreamVoiceRecordingAttachmentThemeData a, - StreamVoiceRecordingAttachmentThemeData b, - double t, - ) { - return StreamVoiceRecordingAttachmentThemeData( - backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t), - playIcon: t < 0.5 ? a.playIcon : b.playIcon, - pauseIcon: t < 0.5 ? a.pauseIcon : b.pauseIcon, - loadingIndicator: t < 0.5 ? a.loadingIndicator : b.loadingIndicator, - audioControlButtonStyle: ButtonStyle.lerp( - a.audioControlButtonStyle, b.audioControlButtonStyle, t), - titleTextStyle: TextStyle.lerp(a.titleTextStyle, b.titleTextStyle, t), - durationTextStyle: - TextStyle.lerp(a.durationTextStyle, b.durationTextStyle, t), - speedControlButtonStyle: ButtonStyle.lerp( - a.speedControlButtonStyle, b.speedControlButtonStyle, t), - audioWaveformSliderTheme: StreamAudioWaveformSliderThemeData.lerp( - a.audioWaveformSliderTheme!, b.audioWaveformSliderTheme!, t), - ); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StreamVoiceRecordingAttachmentThemeData && - other.backgroundColor == backgroundColor && - other.playIcon == playIcon && - other.pauseIcon == pauseIcon && - other.loadingIndicator == loadingIndicator && - other.audioControlButtonStyle == audioControlButtonStyle && - other.titleTextStyle == titleTextStyle && - other.durationTextStyle == durationTextStyle && - other.speedControlButtonStyle == speedControlButtonStyle && - other.audioWaveformSliderTheme == audioWaveformSliderTheme; - - @override - int get hashCode => - backgroundColor.hashCode ^ - playIcon.hashCode ^ - pauseIcon.hashCode ^ - loadingIndicator.hashCode ^ - audioControlButtonStyle.hashCode ^ - titleTextStyle.hashCode ^ - durationTextStyle.hashCode ^ - speedControlButtonStyle.hashCode ^ - audioWaveformSliderTheme.hashCode; -} diff --git a/packages/stream_chat_flutter/lib/src/user/user_mention_tile.dart b/packages/stream_chat_flutter/lib/src/user/user_mention_tile.dart deleted file mode 100644 index f7f9ffa42d..0000000000 --- a/packages/stream_chat_flutter/lib/src/user/user_mention_tile.dart +++ /dev/null @@ -1,95 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template streamUserMentionTile} -/// Shows user tiles for mentions. -/// -/// Use [title], [subtitle], [leading], [trailing] for -/// substituting widgets in respective positions -/// {@endtemplate} -class StreamUserMentionTile extends StatelessWidget { - /// {@macro streamUserMentionTile} - const StreamUserMentionTile( - this.user, { - super.key, - this.title, - this.subtitle, - this.leading, - this.trailing, - }); - - /// User to display in the tile - final User user; - - /// Widget to display as title - final Widget? title; - - /// Widget to display below [title] - final Widget? subtitle; - - /// Widget at the start of the tile - final Widget? leading; - - /// Widget at the end of tile - final Widget? trailing; - - @override - Widget build(BuildContext context) { - final chatThemeData = StreamChatTheme.of(context); - return SizedBox( - height: 56, - child: Row( - children: [ - const SizedBox( - width: 16, - ), - leading ?? - StreamUserAvatar( - user: user, - constraints: BoxConstraints.tight(const Size(40, 40)), - ), - const SizedBox(width: 8), - Expanded( - child: Align( - alignment: Alignment.centerLeft, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - title ?? - Text( - user.name, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: chatThemeData.textTheme.bodyBold, - ), - const SizedBox(height: 2), - subtitle ?? - Text( - '@${user.id}', - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: chatThemeData.textTheme.footnoteBold.copyWith( - color: chatThemeData.colorTheme.textLowEmphasis, - ), - ), - ], - ), - ), - ), - trailing ?? - Padding( - padding: const EdgeInsets.only( - right: 18, - left: 8, - ), - child: StreamSvgIcon( - icon: StreamSvgIcons.mentions, - color: chatThemeData.colorTheme.accentPrimary, - ), - ), - ], - ), - ); - } -} diff --git a/packages/stream_chat_flutter/lib/src/utils/date_formatter.dart b/packages/stream_chat_flutter/lib/src/utils/date_formatter.dart deleted file mode 100644 index db1b5b4200..0000000000 --- a/packages/stream_chat_flutter/lib/src/utils/date_formatter.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:jiffy/jiffy.dart'; -import 'package:stream_chat_flutter/src/utils/extensions.dart'; - -/// Represents a function type that formats a date. -typedef DateFormatter = String Function( - BuildContext context, - DateTime date, -); - -/// Formats the given [date] as a String. -String formatDate(BuildContext context, DateTime date) { - if (date.isToday) return Jiffy.parseFromDateTime(date).jm; - if (date.isYesterday) return context.translations.yesterdayLabel; - if (date.isWithinAWeek) return Jiffy.parseFromDateTime(date).EEEE; - - return Jiffy.parseFromDateTime(date).yMd; -} - -extension on DateTime { - bool get isToday { - final jiffyDate = Jiffy.parseFromDateTime(this); - final jiffyNow = Jiffy.parseFromDateTime(DateTime.now()); - - return jiffyDate.isSame(jiffyNow, unit: Unit.day); - } - - bool get isYesterday { - final jiffyDate = Jiffy.parseFromDateTime(this); - final jiffyNow = Jiffy.parseFromDateTime(DateTime.now()); - - return jiffyDate.isSame(jiffyNow.subtract(days: 1), unit: Unit.day); - } - - bool get isWithinAWeek { - final jiffyDate = Jiffy.parseFromDateTime(this); - final jiffyNow = Jiffy.parseFromDateTime(DateTime.now()); - - return jiffyDate.isAfter(jiffyNow.subtract(days: 7), unit: Unit.day); - } -} diff --git a/packages/stream_chat_flutter/lib/src/utils/device_segmentation.dart b/packages/stream_chat_flutter/lib/src/utils/device_segmentation.dart deleted file mode 100644 index bd65820c5e..0000000000 --- a/packages/stream_chat_flutter/lib/src/utils/device_segmentation.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -/// Returns true if the app is running on web. -bool get isWeb => CurrentPlatform.isWeb; - -/// Returns true if the app is running in a mobile device. -bool get isMobileDevice => CurrentPlatform.isIos || CurrentPlatform.isAndroid; - -/// Returns true if the app is running in a desktop device. -bool get isDesktopDevice => - CurrentPlatform.isMacOS || - CurrentPlatform.isWindows || - CurrentPlatform.isLinux; - -/// Returns true if the app is running on windows or linux platform. -bool get isDesktopVideoPlayerSupported => - // Dart VLC is not supported on MacOS. - !CurrentPlatform.isMacOS && - (CurrentPlatform.isWindows || CurrentPlatform.isLinux); - -/// Returns true if the app is running in a mobile or web. -bool get isMobileDeviceOrWeb => isWeb || isMobileDevice; - -/// Returns true if the app is running in a desktop or web. -bool get isDesktopDeviceOrWeb => isWeb || isDesktopDevice; - -/// Returns true if the app is running in a flutter test environment. -bool get isTestEnvironment => CurrentPlatform.isFlutterTest; diff --git a/packages/stream_chat_flutter/lib/src/utils/extensions.dart b/packages/stream_chat_flutter/lib/src/utils/extensions.dart deleted file mode 100644 index 2e69728631..0000000000 --- a/packages/stream_chat_flutter/lib/src/utils/extensions.dart +++ /dev/null @@ -1,695 +0,0 @@ -import 'dart:io'; -import 'dart:math'; - -import 'package:collection/collection.dart'; -import 'package:diacritic/diacritic.dart'; -import 'package:file_picker/file_picker.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:image_picker/image_picker.dart'; -import 'package:image_size_getter/file_input.dart'; // For compatibility with flutter web. -import 'package:image_size_getter/image_size_getter.dart' hide Size; -import 'package:stream_chat_flutter/src/audio/audio_playlist_state.dart'; -import 'package:stream_chat_flutter/src/localization/translations.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -int _byteUnitConversionFactor = 1024; - -/// int extensions -extension IntExtension on int { - /// Parses int in bytes to human readable size. Like: 17 KB - /// instead of 17524 bytes; - String toHumanReadableSize() { - if (this <= 0) return '0 B'; - const suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; - final i = (log(this) / log(_byteUnitConversionFactor)).floor(); - final numberValue = - (this / pow(_byteUnitConversionFactor, i)).toStringAsFixed(2); - final suffix = suffixes[i]; - return '$numberValue $suffix'; - } -} - -/// Durations extensions. -extension DurationExtension on Duration { - /// Transforms Duration to a minutes and seconds time. Like: 04:13. - String toMinutesAndSeconds() { - final minutes = inMinutes.remainder(60).toString().padLeft(2, '0'); - final seconds = inSeconds.remainder(60).toString().padLeft(2, '0'); - - return '$minutes:$seconds'; - } -} - -/// String extension -extension StringExtension on String { - /// Returns the capitalized string - String capitalize() => - isNotEmpty ? '${this[0].toUpperCase()}${substring(1).toLowerCase()}' : ''; - - /// Returns the biggest line of a text. - String biggestLine() { - if (contains('\n')) { - return split('\n') - .reduce((curr, next) => curr.length > next.length ? curr : next); - } else { - return this; - } - } - - /// Returns whether the string contains only emoji's or not. - /// - /// Emojis guidelines - /// 1 to 3 emojis: big size with no text bubble. - /// 4+ emojis or emojis+text: standard size with text bubble. - bool get isOnlyEmoji { - final trimmedString = trim(); - if (trimmedString.isEmpty) return false; - if (trimmedString.characters.length > 3) return false; - final emojiRegex = RegExp( - r'^(\u00a9|\u00ae|\u200d|[\ufe00-\ufe0f]|[\u2600-\u27FF]|[\u2300-\u2bFF]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])+$', - multiLine: true, - caseSensitive: false, - ); - return emojiRegex.hasMatch(trimmedString); - } - - /// Removes accents and diacritics from the given String. - String get diacriticsInsensitive => removeDiacritics(this); - - /// Levenshtein distance between this and [t]. - int levenshteinDistance(String t) => levenshtein(this, t); - - /// Returns a resized imageUrl with the given [width], [height], [resize] - /// and [crop] if it is from Stream CDN or Dashboard. - /// - /// Read more at https://getstream.io/chat/docs/flutter-dart/file_uploads/?language=dart#image-resizing - String getResizedImageUrl({ - // TODO: Are these sizes optimal? Consider web/desktop - double width = 400, - double height = 400, - String /*clip|crop|scale|fill*/ resize = 'clip', - String /*center|top|bottom|left|right*/ crop = 'center', - }) { - final uri = Uri.parse(this); - final host = uri.host; - - final fromStreamCDN = host.endsWith('stream-io-cdn.com'); - final fromStreamDashboard = host.endsWith('stream-cloud-uploads.imgix.net'); - - if (!fromStreamCDN && !fromStreamDashboard) return this; - - final queryParameters = {...uri.queryParameters}; - - if (fromStreamCDN) { - if (queryParameters['h'].isNullOrMatches('*') && - queryParameters['w'].isNullOrMatches('*') && - queryParameters['crop'].isNullOrMatches('*') && - queryParameters['resize'].isNullOrMatches('*')) { - queryParameters['h'] = height.floor().toString(); - queryParameters['w'] = width.floor().toString(); - queryParameters['crop'] = crop; - queryParameters['resize'] = resize; - } - } else if (fromStreamDashboard) { - queryParameters['height'] = height.floor().toString(); - queryParameters['width'] = width.floor().toString(); - queryParameters['fit'] = crop; - } - - return uri.replace(queryParameters: queryParameters).toString(); - } -} - -/// List extension -extension IterableExtension on Iterable { - /// Insert any item inBetween the list items - List insertBetween(T item) => expand((e) sync* { - yield item; - yield e; - }).skip(1).toList(growable: false); -} - -/// Useful extension for [PlatformFile] -extension PlatformFileX on PlatformFile { - /// Converts the [PlatformFile] into [AttachmentFile] - AttachmentFile get toAttachmentFile { - return AttachmentFile( - // Path is not supported on web. - path: CurrentPlatform.isWeb ? null : path, - name: name, - bytes: bytes, - size: size, - ); - } - - /// Converts the [PlatformFile] to a [Attachment]. - Attachment toAttachment({required String type}) { - final file = toAttachmentFile; - final extraDataMap = {}; - - final mimeType = file.mediaType?.mimeType; - - if (mimeType != null) { - extraDataMap['mime_type'] = mimeType; - } - - extraDataMap['file_size'] = file.size!; - - final attachment = Attachment( - file: file, - type: type, - extraData: extraDataMap, - ); - - return attachment; - } -} - -/// Useful extension for [XFile] -extension XFileX on XFile { - /// Converts the [PlatformFile] into [AttachmentFile] - Future get toAttachmentFile async { - final bytes = await readAsBytes(); - return AttachmentFile( - // Path is not supported on web. - path: CurrentPlatform.isWeb ? null : path, - name: name, - size: bytes.length, - bytes: bytes, - ); - } - - /// Converts the [XFile] to a [Attachment]. - Future toAttachment({required String type}) async { - final file = await toAttachmentFile; - - final extraDataMap = {}; - - final mimeType = this.mimeType ?? file.mediaType?.mimeType; - - if (mimeType != null) { - extraDataMap['mime_type'] = mimeType; - } - - extraDataMap['file_size'] = file.size!; - - final attachment = Attachment( - file: file, - type: type, - extraData: extraDataMap, - ); - - return attachment; - } -} - -/// Extension on [InputDecoration] -extension InputDecorationX on InputDecoration { - /// Merges this [InputDecoration] with the [other] - InputDecoration merge(InputDecoration? other) { - if (other == null) return this; - return copyWith( - icon: other.icon, - labelText: other.labelText, - labelStyle: labelStyle?.merge(other.labelStyle) ?? other.labelStyle, - helperText: other.helperText, - helperStyle: helperStyle?.merge(other.helperStyle) ?? other.helperStyle, - helperMaxLines: other.helperMaxLines, - hintText: other.hintText, - hintStyle: hintStyle?.merge(other.hintStyle) ?? other.hintStyle, - hintTextDirection: other.hintTextDirection, - hintMaxLines: other.hintMaxLines, - errorText: other.errorText, - errorStyle: errorStyle?.merge(other.errorStyle) ?? other.errorStyle, - errorMaxLines: other.errorMaxLines, - floatingLabelBehavior: other.floatingLabelBehavior, - isCollapsed: other.isCollapsed, - isDense: other.isDense, - contentPadding: other.contentPadding, - prefixIcon: other.prefixIcon, - prefix: other.prefix, - prefixText: other.prefixText, - prefixIconConstraints: other.prefixIconConstraints, - prefixStyle: prefixStyle?.merge(other.prefixStyle) ?? other.prefixStyle, - suffixIcon: other.suffixIcon, - suffix: other.suffix, - suffixText: other.suffixText, - suffixStyle: suffixStyle?.merge(other.suffixStyle) ?? other.suffixStyle, - suffixIconConstraints: other.suffixIconConstraints, - counter: other.counter, - counterText: other.counterText, - counterStyle: - counterStyle?.merge(other.counterStyle) ?? other.counterStyle, - filled: other.filled, - fillColor: other.fillColor, - focusColor: other.focusColor, - hoverColor: other.hoverColor, - errorBorder: other.errorBorder, - focusedBorder: other.focusedBorder, - focusedErrorBorder: other.focusedErrorBorder, - disabledBorder: other.disabledBorder, - enabledBorder: other.enabledBorder, - border: other.border, - enabled: other.enabled, - semanticCounterText: other.semanticCounterText, - alignLabelWithHint: other.alignLabelWithHint, - ); - } -} - -/// Gets text scale factor through context -extension BuildContextX on BuildContext { - // ignore: public_member_api_docs - double get textScaleFactor => - // ignore: deprecated_member_use - MediaQuery.maybeOf(this)?.textScaleFactor ?? 1.0; - - /// Retrieves current translations according to locale - /// Defaults to [DefaultTranslations] - Translations get translations => - StreamChatLocalizations.of(this) ?? DefaultTranslations.instance; -} - -/// Extension on [BorderRadius] -extension FlipBorder on BorderRadius { - /// Flips borders (Y) - BorderRadius mirrorBorderIfReversed({bool reverse = true}) => reverse - ? BorderRadius.only( - topLeft: topRight, - topRight: topLeft, - bottomLeft: bottomRight, - bottomRight: bottomLeft, - ) - : this; -} - -/// Extension on [IconButton] -extension IconButtonX on IconButton { - /// Creates a copy of [IconButton] with specified attributes overridden. - IconButton copyWith({ - double? iconSize, - VisualDensity? visualDensity, - EdgeInsetsGeometry? padding, - AlignmentGeometry? alignment, - double? splashRadius, - Color? color, - Color? focusColor, - Color? hoverColor, - Color? highlightColor, - Color? splashColor, - Color? disabledColor, - void Function()? onPressed, - MouseCursor? mouseCursor, - FocusNode? focusNode, - bool? autofocus, - String? tooltip, - bool? enableFeedback, - BoxConstraints? constraints, - Widget? icon, - }) { - return IconButton( - iconSize: iconSize ?? this.iconSize, - visualDensity: visualDensity ?? this.visualDensity, - padding: padding ?? this.padding, - alignment: alignment ?? this.alignment, - splashRadius: splashRadius ?? this.splashRadius, - color: color ?? this.color, - focusColor: focusColor ?? this.focusColor, - hoverColor: hoverColor ?? this.hoverColor, - highlightColor: highlightColor ?? this.highlightColor, - splashColor: splashColor ?? this.splashColor, - disabledColor: disabledColor ?? this.disabledColor, - onPressed: onPressed ?? this.onPressed, - mouseCursor: mouseCursor ?? this.mouseCursor, - focusNode: focusNode ?? this.focusNode, - autofocus: autofocus ?? this.autofocus, - tooltip: tooltip ?? this.tooltip, - enableFeedback: enableFeedback ?? this.enableFeedback, - constraints: constraints ?? this.constraints, - icon: icon ?? this.icon, - ); - } -} - -/// Extensions on List -extension UserListX on List { - /// It does an search on a list of [User] and returns users with - /// `id` or `name` containing the [query]. - /// - /// Results are returned sorted by their edit distance from the - /// searched string, distance is calculated using the [levenshtein] algorithm. - List search(String query) { - String normalize(String input) => input.toLowerCase().diacriticsInsensitive; - - final normalizedQuery = normalize(query); - - final matchingUsers = {}; // User:lDistance - - for (final user in this) { - final normalizedId = normalize(user.id); - final normalizedUserName = normalize(user.name); - final lDistance = normalizedUserName.levenshteinDistance(normalizedQuery); - final containsId = normalizedId.contains(normalizedQuery); - final containsName = normalizedUserName.contains(normalizedQuery); - if (lDistance < 3 || containsId || containsName) { - matchingUsers[user] = lDistance; - } - } - - final entries = matchingUsers.entries.toList(growable: false) - ..sort((prev, curr) { - bool containsQuery(User user) => - normalize(user.id).contains(normalizedQuery) || - normalize(user.name).contains(normalizedQuery); - - final containsInPrev = containsQuery(prev.key); - final containsInCurr = containsQuery(curr.key); - - if (containsInPrev && !containsInCurr) { - return -1; - } else if (!containsInPrev && containsInCurr) { - return 1; - } - return prev.value.compareTo(curr.value); - }); - - return entries.map((e) => e.key).toList(growable: false); - } -} - -/// Extensions on Message -extension MessageX on Message { - /// It replaces the user mentions with the actual user names. - Message replaceMentions({bool linkify = true}) { - var messageTextToRender = text; - for (final user in mentionedUsers.toSet()) { - final userId = user.id; - final userName = user.name; - if (linkify) { - messageTextToRender = messageTextToRender?.replaceAll( - RegExp('@($userId|$userName)'), - '[@$userName]($userId)', - ); - } else { - messageTextToRender = messageTextToRender?.replaceAll( - RegExp('@($userId|$userName)'), - '@$userName', - ); - } - } - return copyWith(text: messageTextToRender); - } - - /// Returns an approximation of message size - double roughMessageSize(double? fontSize) { - var messageTextLength = min(text?.biggestLine().length ?? 0, 65); - - if (quotedMessage != null) { - var quotedMessageLength = - (min(quotedMessage!.text?.biggestLine().length ?? 0, 65)) + 8; - - if (quotedMessage!.attachments.isNotEmpty) { - quotedMessageLength += 8; - } - - if (quotedMessageLength > messageTextLength * 1.2) { - messageTextLength = quotedMessageLength; - } - } - - // Quoted message have a smaller font, so it is necessary to reduce the - // size of the multiplier to count for the smaller font. - var multiplier = 0.55; - if (quotedMessage != null) { - multiplier = 0.45; - } - - return messageTextLength * (fontSize ?? 1) * multiplier; - } - - /// It returns the message with the translated text if available locally - Message translate(String language) => - copyWith(text: i18n?['${language}_text'] ?? text); - - /// It returns the message replacing the mentioned user names with - /// the respective user ids - Message replaceMentionsWithId() { - if (mentionedUsers.isEmpty) return this; - - var messageTextToSend = text; - if (messageTextToSend == null) return this; - - for (final user in mentionedUsers.toSet()) { - final userName = user.name; - messageTextToSend = messageTextToSend!.replaceAll( - '@$userName', - '@${user.id}', - ); - } - - return copyWith(text: messageTextToSend); - } -} - -/// Extensions on [Uri] -extension UriX on Uri { - /// Return the URI adding the http scheme if it is missing - Uri get withScheme { - if (hasScheme) return this; - return Uri.parse('http://${toString()}'); - } -} - -/// Extensions on generic type [T] -extension TypeX on T? { - /// Returns true if the value is null or matches the given [value] - /// otherwise returns false. - bool isNullOrMatches(T value) => this == null || this == value; -} - -/// Useful extensions on [FileType] -extension FileTypeX on FileType { - /// Converts the [FileType] to a [String]. - String toAttachmentType() { - switch (this) { - case FileType.image: - return AttachmentType.image; - case FileType.video: - return AttachmentType.video; - case FileType.audio: - return AttachmentType.audio; - case FileType.any: - case FileType.media: - case FileType.custom: - return AttachmentType.file; - } - } -} - -/// Useful extensions on [AttachmentPickerType] -extension AttachmentPickerTypeX on AttachmentPickerType { - /// Converts the [AttachmentPickerType] to a [FileType]. - FileType get fileType { - switch (this) { - case AttachmentPickerType.images: - return FileType.image; - case AttachmentPickerType.videos: - return FileType.video; - case AttachmentPickerType.files: - return FileType.any; - case AttachmentPickerType.audios: - return FileType.audio; - case AttachmentPickerType.poll: - throw Exception('Polls do not have a file type'); - } - } -} - -/// Useful extensions on [BoxConstraints]. -extension ConstraintsX on BoxConstraints { - /// Returns new box constraints that tightens the max width and max height - /// to the given [size]. - BoxConstraints tightenMaxSize(Size? size) { - if (size == null) return this; - return copyWith( - maxWidth: clampDouble(size.width, minWidth, maxWidth), - maxHeight: clampDouble(size.height, minHeight, maxHeight), - ); - } -} - -/// Useful extensions on [Attachment]. -extension OriginalSizeX on Attachment { - /// Returns the size of the attachment if it is an image or giffy. - /// Otherwise, returns null. - Size? get originalSize { - // Return null if the attachment is not an image or giffy. - if (type != AttachmentType.image && type != AttachmentType.giphy) { - return null; - } - - // Calculate size locally if the attachment is not uploaded yet. - final file = this.file; - if (file != null) { - ImageInput? input; - if (file.bytes != null) { - input = MemoryInput(file.bytes!); - } else if (file.path != null) { - input = FileInput(File(file.path!)); - } - - // Return null if the file does not contain enough information. - if (input == null) return null; - - try { - final size = ImageSizeGetter.getSizeResult(input).size; - if (size.needRotate) { - return Size(size.height.toDouble(), size.width.toDouble()); - } - return Size(size.width.toDouble(), size.height.toDouble()); - } catch (e, stk) { - debugPrint('Error getting image size: $e\n$stk'); - return null; - } - } - - // Otherwise, use the size provided by the server. - final width = originalWidth; - final height = originalHeight; - if (width == null || height == null) return null; - return Size(width.toDouble(), height.toDouble()); - } -} - -/// Useful extensions on [List]. -extension MessageListX on Iterable { - /// Returns the last unread message in the list. - /// Returns null if the list is empty or the userRead is null. - /// - /// The [userRead] is the last read message by the user. - /// - /// The last unread message is the last message in the list that is not - /// sent by the current user and is sent after the last read message. - Message? lastUnreadMessage(Read? userRead) { - if (isEmpty || userRead == null) return null; - - if (first.createdAt.isAfter(userRead.lastRead) && - last.createdAt.isBefore(userRead.lastRead)) { - return lastWhereOrNull( - (it) => - it.user?.id != userRead.user.id && - it.id != userRead.lastReadMessageId && - it.createdAt.compareTo(userRead.lastRead) > 0, - ); - } - - return null; - } -} - -/// Useful extensions on [ChannelModel]. -extension ChannelModelX on ChannelModel { - /// Returns the channel name if exists, or a formatted name based on the - /// members of the channel and the [maxMembers] allowed. - String? formatName({ - User? currentUser, - int maxMembers = 2, - }) { - // If there's an assigned name and it's not empty, we use it. - if (name case final name? when name.isNotEmpty) return name; - - // If there are no members, we return null. - final members = this.members; - if (members == null) return null; - - final otherMembers = members.where((it) => it.userId != currentUser?.id); - - // If there are no other members, we return the name of the current user. - if (otherMembers.isEmpty) return currentUser?.name; - - // Otherwise, we return the names of the first `maxMembers` members sorted - // alphabetically, followed by the number of remaining members if there are - // more than `maxMembers` members. - final memberNames = otherMembers - .map((it) => it.user?.name) - .whereType() - .take(maxMembers) - .sorted(); - - return switch (otherMembers.length <= maxMembers) { - true => memberNames.join(', '), - false => - '${memberNames.join(', ')} + ${otherMembers.length - maxMembers}', - }; - } -} - -/// {@template voiceRecordingAttachmentExtension} -/// Extension on [Attachment] to provide the voice recording attachment specific -/// properties. -/// {@endtemplate} -extension VoiceRecordingAttachmentExtension on Attachment { - /// Returns the duration of the voice recording attachment if available else - /// returns [Duration.zero]. - Duration get duration { - final duration = extraData['duration'] as num?; - if (duration == null) return Duration.zero; - - return Duration(milliseconds: duration.round() * 1000); - } - - /// Returns the waveform data of the voice recording attachment if available - /// else returns an empty list. - List get waveform { - final waveform = extraData['waveform_data'] as List?; - if (waveform == null) return []; - - return [...waveform.map((e) => double.tryParse(e.toString())).nonNulls]; - } -} - -/// {@template attachmentPlaylistExtension} -/// Extension on [Iterable] to provide the playlist specific -/// properties. -/// {@endtemplate} -extension AttachmentPlaylistExtension on Iterable { - /// Converts the list of attachments to a list of [PlaylistTrack]. - List toPlaylist() { - return [ - ...map((it) { - final uri = switch (it.uploadState) { - Preparing() || InProgress() || Failed() => () { - if (CurrentPlatform.isWeb) { - final bytes = it.file?.bytes; - final mimeType = it.file?.mediaType?.mimeType; - if (bytes == null || mimeType == null) return null; - - return Uri.dataFromBytes(bytes, mimeType: mimeType); - } - - final path = it.file?.path; - if (path == null) return null; - - return Uri.file(path, windows: CurrentPlatform.isWindows); - }(), - Success() => () { - final url = it.assetUrl; - if (url == null) return null; - - return Uri.tryParse(url); - }(), - }; - - if (uri == null) return null; - - return PlaylistTrack( - uri: uri, - title: it.title, - waveform: it.waveform, - duration: it.duration, - ); - }).nonNulls, - ]; - } -} diff --git a/packages/stream_chat_flutter/lib/src/utils/helpers.dart b/packages/stream_chat_flutter/lib/src/utils/helpers.dart deleted file mode 100644 index 991bfc3dfd..0000000000 --- a/packages/stream_chat_flutter/lib/src/utils/helpers.dart +++ /dev/null @@ -1,463 +0,0 @@ -import 'dart:async'; -import 'dart:math' as math; - -import 'package:flutter/material.dart'; -import 'package:flutter_portal/flutter_portal.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:synchronized/synchronized.dart'; -import 'package:url_launcher/url_launcher.dart'; - -final _permissionRequestLock = Lock(); - -/// Executes [computation] when [_permissionRequestLock] is available. -/// -/// Only one asynchronous block can run while the [_permissionRequestLock] -/// is retained. -Future runInPermissionRequestLock( - FutureOr Function() computation, { - Duration? timeout, -}) { - return _permissionRequestLock.synchronized( - computation, - timeout: timeout, - ); -} - -/// Launch URL -Future launchURL(BuildContext context, String url) async { - try { - await launchUrl( - Uri.parse(url).withScheme, - mode: LaunchMode.externalApplication, - ); - } catch (e) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(context.translations.launchUrlError)), - ); - } -} - -/// Get centerTitle considering a default and platform specific behaviour -bool getEffectiveCenterTitle( - ThemeData theme, { - bool? centerTitle, - List? actions, -}) { - if (centerTitle != null) return centerTitle; - if (theme.appBarTheme.centerTitle != null) { - return theme.appBarTheme.centerTitle!; - } - switch (theme.platform) { - case TargetPlatform.android: - case TargetPlatform.fuchsia: - case TargetPlatform.linux: - case TargetPlatform.windows: - return false; - case TargetPlatform.iOS: - case TargetPlatform.macOS: - return actions == null || actions.length < 2; - } -} - -/// Shows confirmation bottom sheet -Future showConfirmationBottomSheet( - BuildContext context, { - required String title, - required String okText, - Widget? icon, - String? question, - String? cancelText, -}) { - final chatThemeData = StreamChatTheme.of(context); - return showModalBottomSheet( - backgroundColor: chatThemeData.colorTheme.barsBg, - context: context, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16), - ), - ), - builder: (context) { - final effect = chatThemeData.colorTheme.borderTop; - return SafeArea( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(height: 26), - if (icon != null) icon, - const SizedBox(height: 26), - Text( - title, - style: chatThemeData.textTheme.headlineBold, - ), - const SizedBox(height: 7), - if (question != null) - Text( - question, - textAlign: TextAlign.center, - ), - const SizedBox(height: 36), - Container( - // ignore: deprecated_member_use - color: effect.color!.withOpacity(effect.alpha ?? 1), - height: 1, - ), - Row( - children: [ - if (cancelText != null) - Flexible( - child: Container( - alignment: Alignment.center, - child: TextButton( - onPressed: () { - Navigator.of(context).pop(false); - }, - child: Text( - cancelText, - style: chatThemeData.textTheme.bodyBold.copyWith( - color: chatThemeData.colorTheme.textHighEmphasis - // ignore: deprecated_member_use - .withOpacity(0.5), - ), - ), - ), - ), - ), - Flexible( - child: Container( - alignment: Alignment.center, - child: TextButton( - onPressed: () => Navigator.of(context).pop(true), - child: Text( - okText, - style: chatThemeData.textTheme.bodyBold.copyWith( - color: chatThemeData.colorTheme.accentError, - ), - ), - ), - ), - ), - ], - ), - ], - ), - ); - }, - ); -} - -/// Shows info bottom sheet -Future showInfoBottomSheet( - BuildContext context, { - required String title, - required String okText, - Widget? icon, - String? details, - StreamChatThemeData? theme, -}) { - final chatThemeData = StreamChatTheme.of(context); - return showModalBottomSheet( - backgroundColor: - theme?.colorTheme.barsBg ?? chatThemeData.colorTheme.barsBg, - context: context, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16), - ), - ), - builder: (context) => SafeArea( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox( - height: 26, - ), - if (icon != null) icon, - const SizedBox( - height: 26, - ), - Text( - title, - style: theme?.textTheme.headlineBold ?? - chatThemeData.textTheme.headlineBold, - ), - const SizedBox( - height: 7, - ), - if (details != null) Text(details), - const SizedBox( - height: 36, - ), - Container( - // ignore: deprecated_member_use - color: theme?.colorTheme.textHighEmphasis.withOpacity(0.08) ?? - // ignore: deprecated_member_use - chatThemeData.colorTheme.textHighEmphasis.withOpacity(0.08), - height: 1, - ), - Center( - child: TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: Text( - okText, - style: TextStyle( - // ignore: deprecated_member_use - color: theme?.colorTheme.textHighEmphasis.withOpacity(0.5) ?? - chatThemeData.colorTheme.accentPrimary, - fontWeight: FontWeight.w400, - ), - ), - ), - ), - ], - ), - ), - ); -} - -/// Get random png with initials -String getRandomPicUrl(User user) => - 'https://getstream.io/random_png/?id=${user.id}&name=${user.name}'; - -/// Get websiteName from [hostName] -String? getWebsiteName(String hostName) { - switch (hostName) { - case 'reddit': - return 'Reddit'; - case 'youtube': - return 'Youtube'; - case 'wikipedia': - return 'Wikipedia'; - case 'twitter': - return 'Twitter'; - case 'facebook': - return 'Facebook'; - case 'amazon': - return 'Amazon'; - case 'yelp': - return 'Yelp'; - case 'imdb': - return 'IMDB'; - case 'pinterest': - return 'Pinterest'; - case 'tripadvisor': - return 'TripAdvisor'; - case 'instagram': - return 'Instagram'; - case 'walmart': - return 'Walmart'; - case 'craigslist': - return 'Craigslist'; - case 'ebay': - return 'eBay'; - case 'linkedin': - return 'LinkedIn'; - case 'google': - return 'Google'; - case 'apple': - return 'Apple'; - default: - return null; - } -} - -/// A method returns a human readable string representing a file _size -String fileSize(dynamic size, [int round = 2]) { - if (size == null) return 'Size N/A'; - - /** - * [size] can be passed as number or as string - * - * the optional parameter [round] specifies the number - * of digits after comma/point (default is 2) - */ - const divider = 1024; - int _size; - try { - _size = int.parse(size.toString()); - } catch (e) { - throw ArgumentError('Can not parse the size parameter: $e'); - } - - if (_size < divider) { - return '$_size B'; - } - - if (_size < divider * divider && _size % divider == 0) { - return '${(_size / divider).toStringAsFixed(0)} KB'; - } - - if (_size < divider * divider) { - return '${(_size / divider).toStringAsFixed(round)} KB'; - } - - if (_size < divider * divider * divider && _size % divider == 0) { - return '${(_size / (divider * divider)).toStringAsFixed(0)} MB'; - } - - if (_size < divider * divider * divider) { - return '${(_size / divider / divider).toStringAsFixed(round)} MB'; - } - - if (_size < divider * divider * divider * divider && _size % divider == 0) { - return '${(_size / (divider * divider * divider)).toStringAsFixed(0)} GB'; - } - - if (_size < divider * divider * divider * divider) { - return '${(_size / divider / divider / divider).toStringAsFixed(round)} GB'; - } - - if (_size < divider * divider * divider * divider * divider && - _size % divider == 0) { - final num r = _size / divider / divider / divider / divider; - return '${r.toStringAsFixed(0)} TB'; - } - - if (_size < divider * divider * divider * divider * divider) { - final num r = _size / divider / divider / divider / divider; - return '${r.toStringAsFixed(round)} TB'; - } - - if (_size < divider * divider * divider * divider * divider * divider && - _size % divider == 0) { - final num r = _size / divider / divider / divider / divider / divider; - return '${r.toStringAsFixed(0)} PB'; - } else { - final num r = _size / divider / divider / divider / divider / divider; - return '${r.toStringAsFixed(round)} PB'; - } -} - -// TODO: Use file extension instead of mime type to get the file type icon. -/// Returns a [StreamSvgIcon] based on the [mimeType] of the file. -StreamSvgIcon getFileTypeImage([String? mimeType]) { - return StreamSvgIcon( - size: 40, - icon: switch (mimeType) { - 'audio/mpeg' => StreamSvgIcons.filetypeAudioMp3, - 'audio/aac' => StreamSvgIcons.filetypeAudioAac, - 'audio/wav' || 'audio/x-wav' => StreamSvgIcons.filetypeAudioWav, - 'audio/flac' => StreamSvgIcons.filetypeAudioFlac, - 'audio/mp4' => StreamSvgIcons.filetypeAudioM4a, - 'audio/ogg' => StreamSvgIcons.filetypeAudioOgg, - 'audio/aiff' => StreamSvgIcons.filetypeAudioAiff, - 'audio/alac' => StreamSvgIcons.filetypeAudioAlac, - 'application/zip' => StreamSvgIcons.filetypeCompressionZip, - 'application/x-7z-compressed' => StreamSvgIcons.filetypeCompression7z, - 'application/x-arj' => StreamSvgIcons.filetypeCompressionArj, - 'application/vnd.debian.binary-package' => - StreamSvgIcons.filetypeCompressionDeb, - 'application/x-apple-diskimage' => StreamSvgIcons.filetypeCompressionPkg, - 'application/x-rar-compressed' => StreamSvgIcons.filetypeCompressionRar, - 'application/x-rpm' => StreamSvgIcons.filetypeCompressionRpm, - 'application/x-tar' => StreamSvgIcons.filetypeCodeTar, - 'application/x-compress' => StreamSvgIcons.filetypeCompressionZ, - 'application/vnd.ms-powerpoint' => StreamSvgIcons.filetypePresentationPpt, - 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => - StreamSvgIcons.filetypePresentationPptx, - 'application/vnd.apple.keynote' => StreamSvgIcons.filetypePresentationKey, - 'application/vnd.oasis.opendocument.presentation' => - StreamSvgIcons.filetypePresentationOdp, - 'application/vnd.ms-excel' => StreamSvgIcons.filetypeSpreadsheetXls, - 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => - StreamSvgIcons.filetypeSpreadsheetXlsx, - 'application/vnd.ms-excel.sheet.macroEnabled.12' => - StreamSvgIcons.filetypeSpreadsheetXlsm, - 'application/vnd.oasis.opendocument.spreadsheet' => - StreamSvgIcons.filetypeSpreadsheetOds, - 'application/msword' => StreamSvgIcons.filetypeTextDoc, - 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => - StreamSvgIcons.filetypeTextDocx, - 'application/vnd.oasis.opendocument.text' => - StreamSvgIcons.filetypeTextOdt, - 'text/plain' => StreamSvgIcons.filetypeTextTxt, - 'application/rtf' => StreamSvgIcons.filetypeTextRtf, - 'application/x-tex' => StreamSvgIcons.filetypeTextTex, - 'application/vnd.wordperfect' => StreamSvgIcons.filetypeTextWdp, - 'text/html' => StreamSvgIcons.filetypeCodeHtml, - 'text/csv' => StreamSvgIcons.filetypeCodeCsv, - 'application/xml' => StreamSvgIcons.filetypeCodeXml, - 'text/markdown' => StreamSvgIcons.filetypeCodeMd, - 'application/octet-stream' => StreamSvgIcons.filetypeOtherStandard, - 'application/pdf' => StreamSvgIcons.filetypeOtherPdf, - 'application/x-wiki' => StreamSvgIcons.filetypeOtherWkq, - _ => StreamSvgIcons.filetypeOtherStandard, - }, - ); -} - -/// Wraps attachment widget with custom shape -class WrapAttachmentWidget extends StatelessWidget { - /// Builds a [WrapAttachmentWidget]. - const WrapAttachmentWidget({ - super.key, - required this.attachmentWidget, - this.attachmentShape, - }); - - /// The widget to wrap - final Widget attachmentWidget; - - /// The shape of the wrapper - final ShapeBorder? attachmentShape; - - @override - Widget build(BuildContext context) { - return Material( - clipBehavior: Clip.hardEdge, - shape: attachmentShape, - type: MaterialType.transparency, - child: attachmentWidget, - ); - } -} - -/// Levenshtein algorithm implementation based on: -/// http://en.wikipedia.org/wiki/Levenshtein_distance#Iterative_with_two_matrix_rows -int levenshtein(String s, String t, {bool caseSensitive = true}) { - if (!caseSensitive) { - // ignore: parameter_assignments - s = s.toLowerCase(); - // ignore: parameter_assignments - t = t.toLowerCase(); - } - if (s == t) return 0; - if (s.isEmpty) return t.length; - if (t.isEmpty) return s.length; - - final v0 = List.filled(t.length + 1, 0); - final v1 = List.filled(t.length + 1, 0); - - for (var i = 0; i < t.length + 1; i < i++) { - v0[i] = i; - } - - for (var i = 0; i < s.length; i++) { - v1[0] = i + 1; - - for (var j = 0; j < t.length; j++) { - final cost = (s[i] == t[j]) ? 0 : 1; - v1[j + 1] = math.min(v1[j] + 1, math.min(v0[j + 1] + 1, v0[j] + cost)); - } - - for (var j = 0; j < t.length + 1; j++) { - v0[j] = v1[j]; - } - } - - return v1[t.length]; -} - -/// PortalLabel that refers to [StreamMessageListView] -const kPortalMessageListViewLabel = _PortalMessageListViewLabel(); - -class _PortalMessageListViewLabel extends PortalLabel { - const _PortalMessageListViewLabel() : super(null); - - @override - String toString() => 'PortalLabel.MessageWidget'; -} diff --git a/packages/stream_chat_flutter/lib/src/utils/typedefs.dart b/packages/stream_chat_flutter/lib/src/utils/typedefs.dart deleted file mode 100644 index 46db653482..0000000000 --- a/packages/stream_chat_flutter/lib/src/utils/typedefs.dart +++ /dev/null @@ -1,362 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/message_input/attachment_button.dart'; -import 'package:stream_chat_flutter/src/message_input/command_button.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// {@template inProgressBuilder} -/// A widget builder for representing in-progress attachment uploads. -/// {@endtemplate} -typedef InProgressBuilder = Widget Function(BuildContext, int, int); - -/// {@template failedBuilder} -/// A widget builder for representing failed attachment uploads. -/// {@endtemplate} -typedef FailedBuilder = Widget Function(BuildContext, String); - -/// {@template successBuilder} -/// A widget builder for representing successful attachment uploads. -/// {@endtemplate} -typedef SuccessBuilder = WidgetBuilder; - -/// {@template preparingBuilder} -/// A widget builder for representing pre-upload attachment state. -/// {@endtemplate} -typedef PreparingBuilder = WidgetBuilder; - -/// {@template onAttachmentTap} -/// The action to perform when the attachment is tapped or clicked. -/// {@endtemplate} -typedef OnAttachmentTap = VoidCallback; - -/// {@template showMessageCallback} -/// The action to perform when "show message" is tapped -/// {@endtemplate} -typedef ShowMessageCallback = void Function(Message message, Channel channel); - -/// {@template showMessageCallback} -/// The action to perform when "reply message" is tapped -/// {@endtemplate} -typedef ReplyMessageCallback = void Function(Message message); - -/// {@template onImageGroupAttachmentTap} -/// The action to perform when a specific image attachment in an [ImageGroup] -/// is tapped or clicked. -/// {@endtemplate} -typedef OnImageGroupAttachmentTap = void Function( - Message message, - Attachment attachment, -); - -/// {@template onUserAvatarPress} -/// The action to perform when a user's avatar is tapped, clicked, or -/// long-pressed. -/// {@endtemplate} -typedef OnUserAvatarPress = void Function(User); - -/// {@template placeholderUserImage} -/// A widget builder that will build placeholder user images while loading a -/// user image. -/// {@endtemplate} -typedef PlaceholderUserImage = Widget Function(BuildContext, User); - -/// {@template editMessageInputBuilder} -/// A widget builder for building a pre-populated [MessageInput] for use in -/// editing messages. -/// {@endtemplate} -typedef EditMessageInputBuilder = Widget Function(BuildContext, Message); - -/// {@template channelListHeaderTitleBuilder} -/// A widget builder for custom [ChannelListHeader] title widgets. -/// {@endtemplate} -typedef ChannelListHeaderTitleBuilder = Widget Function( - BuildContext context, - ConnectionStatus status, - StreamChatClient client, -); - -/// {@template channelTapCallback} -/// The action to perform when a channel is tapped or clicked. -/// {@endtemplate} -typedef ChannelTapCallback = void Function(Channel, Widget?); - -/// {@template channelInfoCallback} -/// The action to perform when a particular channel slidable option is tapped -/// or clicked. -/// {@endtemplate} -typedef ChannelInfoCallback = void Function(Channel); - -/// {@template viewInfoCallback} -/// Callback for when 'View Info' is tapped -/// {@endtemplate} -typedef ViewInfoCallback = void Function(Channel); - -/// {@template attachmentActionsBuilder} -/// A widget builder for representing the actions a user can take on an -/// attachment. -/// -/// [defaultActionsModal] is the default [AttachmentActionsModal] configuration. -/// Use [defaultActionsModal.copyWith] to easily customize it -/// {@endtemplate} -typedef AttachmentActionsBuilder = Widget Function( - BuildContext context, - Attachment attachment, - AttachmentActionsModal defaultActionsModal, -); - -/// {@template errorListener} -/// A callback that can be passed to [StreamMessageInput.onError]. -/// -/// This callback should not throw. -/// -/// It exists merely for error reporting, and should not be used otherwise. -/// {@endtemplate} -typedef ErrorListener = void Function( - Object error, - StackTrace? stackTrace, -); - -/// {@template attachmentLimitExceededListener} -/// A callback that can be passed to -/// [StreamMessageInput.onAttachmentLimitExceed]. -/// -/// This callback should not throw. -/// -/// It exists merely for showing custom error, and should not be used otherwise. -/// {@endtemplate} -typedef AttachmentLimitExceedListener = void Function( - int limit, - String error, -); - -/// {@template attachmentThumbnailBuilder} -/// A widget builder for representing attachment thumbnails. -/// {@endtemplate} -typedef AttachmentThumbnailBuilder = Widget Function( - BuildContext, - Attachment, -); - -/// {@template mentionTileBuilder} -/// A widget builder for representing a custom mention tile. -/// {@endtemplate} -typedef MentionTileBuilder = Widget Function( - BuildContext context, - Member member, -); - -/// {@template mentionTileOverlayBuilder} -/// A widget builder for representing a custom mention tile within a -/// [UserMentionsOverlay]. -/// {@endtemplate} -typedef MentionTileOverlayBuilder = Widget Function( - BuildContext context, - User user, -); - -/// {@template userMentionTileBuilder} -/// A builder function for representing a custom user mention tile. -/// -/// Use [UserMentionTile] for the default implementation. -/// {@endtemplate} -typedef UserMentionTileBuilder = Widget Function( - BuildContext context, - User user, -); - -/// {@template actionButtonBuilder} -/// A widget builder for building a custom command button. -/// -/// [commandButton] is the default [CommandButton] configuration, -/// use [commandButton.copyWith] to easily customize it. -/// {@endtemplate} -typedef CommandButtonBuilder = Widget Function( - BuildContext context, - CommandButton commandButton, -); - -/// {@template actionButtonBuilder} -/// A widget builder for building a custom action button. -/// -/// [attachmentButton] is the default [AttachmentButton] configuration, -/// use [attachmentButton.copyWith] to easily customize it. -/// {@endtemplate} -typedef AttachmentButtonBuilder = Widget Function( - BuildContext context, - AttachmentButton attachmentButton, -); - -/// {@template quotedMessageAttachmentThumbnailBuilder} -/// A widget builder for building a custom quoted message attachment thumbnail. -/// {@endtemplate} -typedef QuotedMessageAttachmentThumbnailBuilder = Widget Function( - BuildContext, - Attachment, -); - -/// {@template attachmentBuilder} -/// A widget builder for representing attachments. -/// {@endtemplate} -typedef AttachmentBuilder = Widget Function( - BuildContext, - Message, - List, -); - -/// {@template onQuotedMessageTap} -/// The action to perform when a quoted message is tapped. -/// {@endtemplate} -typedef OnQuotedMessageTap = void Function(String?); - -/// {@template onMessageTap} -/// The action to perform when a message is tapped. -/// {@endtemplate} -typedef OnMessageTap = void Function(Message); - -/// {@template onReactionsTap} -/// The action to perform when a message's reactions are tapped. -/// {@endtemplate} -typedef OnReactionsTap = void Function(Message); - -/// {@template onReactionsHover} -/// The action to perform when a message's reactions are hovered. -/// {@endtemplate} -// ignore: avoid_positional_boolean_parameters -typedef OnReactionsHover = void Function(bool isHovering); - -/// {@template messageSearchItemTapCallback} -/// The action to perform when tapping or clicking on a user in a -/// [MessageSearchListView]. -/// {@endtemplate} -typedef MessageSearchItemTapCallback = void Function(GetMessageResponse); - -/// {@template messageSearchItemBuilder} -/// A widget builder used to create a custom [ListUserItem] from a [User]. -/// {@endtemplate} -typedef MessageSearchItemBuilder = Widget Function( - BuildContext, - GetMessageResponse, -); - -/// {@template messageBuilder} -/// A widget builder for creating custom message UI. -/// -/// [defaultMessageWidget] is the default [StreamMessageWidget] configuration. -/// Use [defaultMessageWidget.copyWith] to customize it. -/// {@endtemplate} -typedef MessageBuilder = Widget Function( - BuildContext, - MessageDetails, - List, - StreamMessageWidget defaultMessageWidget, -); - -/// {@template parentMessageBuilder} -/// A widget builder for creating custom parent message UI. -/// -/// [defaultMessageWidget] is the default [StreamMessageWidget] configuration. -/// Use [defaultMessageWidget.copyWith] to customize it. -/// {@endtemplate} -typedef ParentMessageBuilder = Widget Function( - BuildContext, - Message?, - StreamMessageWidget defaultMessageWidget, -); - -/// {@template systemMessageBuilder} -/// A widget builder for creating custom system messages. -/// {@endtemplate} -typedef SystemMessageBuilder = Widget Function( - BuildContext, - Message, -); - -/// {@template ephemeralMessageBuilder} -/// A widget builder for creating custom ephemeral messages. -/// {@endtemplate} -typedef EphemeralMessageBuilder = Widget Function( - BuildContext, - Message, -); - -/// {@template threadBuilder} -/// A widget builder for creating custom thread UI. -/// {@endtemplate} -typedef ThreadBuilder = Widget Function(BuildContext context, Message? parent); - -/// {@template threadTapCallback} -/// The action to perform when threads are tapped. -/// {@endtemplate} -typedef ThreadTapCallback = void Function(Message, Widget?); - -/// {@template spacingWidgetBuilder} -/// A widget builder for creating certain spacing after widgets. -/// -/// This spacing can be in the form of any widgets you like. -/// -/// A List of [SpacingType] is provided to help inform the decision of -/// what to build after the message (thread, difference in time between -/// current and last message, default spacing, etc). -/// -/// Example: -/// ```dart -/// MessageListView( -/// spacingWidgetBuilder: (context, list) { -/// if(list.contains(SpacingType.defaultSpacing)) { -/// return SizedBox(height: 2.0,); -/// } else { -/// return SizedBox(height: 8.0,); -/// } -/// }, -/// ), -/// ```dart -/// {@endtemplate} -typedef SpacingWidgetBuilder = Widget Function( - BuildContext context, - List spacingTypes, -); - -/// {@template attachmentDownloader} -/// A callback for downloading an attachment asset. -/// {@endtemplate} -/// Callback to download an attachment asset -typedef AttachmentDownloader = Future Function( - Attachment attachment, { - ProgressCallback? onReceiveProgress, - Map? queryParameters, - CancelToken? cancelToken, - bool deleteOnError, - Options? options, -}); - -/// Callback to receive the path once the attachment asset is downloaded -typedef DownloadedPathCallback = void Function(String? path); - -/// {@template userTapCallback} -/// Callback called when tapping on a user -/// {@endtemplate} -typedef UserTapCallback = void Function(User, Widget?); - -/// {@template rawKeyEventPredicate} -/// Callback called to react to a key event -/// {@endtemplate} -typedef KeyEventPredicate = bool Function(FocusNode, KeyEvent); - -/// {@template userItemBuilder} -/// Builder used to create a custom [ListUserItem] from a [User] -/// {@endtemplate} -// ignore: avoid_positional_boolean_parameters -typedef UserItemBuilder = Widget Function(BuildContext, User, bool); - -/// The action to perform when the "scroll to bottom" button is pressed -/// within a [MessageListView]. -typedef OnScrollToBottom = Function(int unreadCount); - -/// Widget builder for widgets that may require data from the -/// [MessageInputController]. -typedef MessageRelatedBuilder = Widget Function( - BuildContext context, - StreamMessageInputController messageInputController, -); - -/// A function that returns true if the message is valid and can be sent. -typedef MessageValidator = bool Function(Message message); diff --git a/packages/stream_chat_flutter/lib/src/utils/utils.dart b/packages/stream_chat_flutter/lib/src/utils/utils.dart deleted file mode 100644 index 34eb6503f8..0000000000 --- a/packages/stream_chat_flutter/lib/src/utils/utils.dart +++ /dev/null @@ -1,4 +0,0 @@ -export 'device_segmentation.dart'; -export 'extensions.dart'; -export 'helpers.dart'; -export 'typedefs.dart'; diff --git a/packages/stream_chat_flutter/lib/src/video/video_service.dart b/packages/stream_chat_flutter/lib/src/video/video_service.dart deleted file mode 100644 index d36183cf4a..0000000000 --- a/packages/stream_chat_flutter/lib/src/video/video_service.dart +++ /dev/null @@ -1,91 +0,0 @@ -import 'dart:async'; -import 'dart:ui' as ui; - -import 'package:flutter/services.dart'; -import 'package:get_thumbnail_video/index.dart'; -import 'package:get_thumbnail_video/video_thumbnail.dart'; -import 'package:stream_chat_flutter/src/utils/device_segmentation.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:thumblr/thumblr.dart' as thumblr; - -/// -// ignore: prefer-match-file-name -class _IVideoService { - _IVideoService._(); - - /// Singleton instance of [_IVideoService] - static final _IVideoService instance = _IVideoService._(); - - /// Generates a thumbnail image data in memory as UInt8List. - /// - /// The video source can be a local video file or a URL. - /// - /// Thumbnails are not supported on Web at this time. - /// - /// If no [video] path is supplied, or if a thumbnail cannot be generated, - /// returns [generatePlaceholderThumbnail]. A stock placeholder image. - /// - /// For desktop, you can specify the position of the video to generate - /// the thumbnail. - /// - /// For mobile, you can specify the maximum height or width for the thumbnail - /// or 0 for same resolution as the original video. The lower quality value - /// creates lower quality of the thumbnail image, but it gets ignored for - /// PNG format. - Future generateVideoThumbnail({ - String? video, - Map? headers, - ImageFormat imageFormat = ImageFormat.PNG, - int maxHeight = 0, - int maxWidth = 0, - int timeMs = 0, - int quality = 10, - }) async { - // If the video path is not supplied, return a placeholder image. - if (video == null) return generatePlaceholderThumbnail(); - - try { - // If the device is a desktop, use thumblr to generate the thumbnail. - if (isDesktopDevice) { - final thumbnail = await thumblr.generateThumbnail(filePath: video); - final byteData = await thumbnail.image.toByteData( - format: ui.ImageByteFormat.png, - ); - - final bytesList = byteData?.buffer.asUint8List(); - if (bytesList != null && bytesList.isNotEmpty) { - return bytesList; - } - - return await generatePlaceholderThumbnail(); - } - - // Otherwise, use the VideoThumbnail package to generate the thumbnail. - return await VideoThumbnail.thumbnailData( - video: video, - headers: headers, - imageFormat: imageFormat, - maxHeight: maxHeight, - maxWidth: maxWidth, - timeMs: timeMs, - quality: quality, - ); - } catch (_) { - // If the thumbnail generation fails, return a placeholder image. - return generatePlaceholderThumbnail(); - } - } - - /// Generates a placeholder thumbnail by loading placeholder.png from assets. - Future generatePlaceholderThumbnail() async { - final placeholder = await rootBundle.load( - 'packages/stream_chat_flutter/lib/assets/images/placeholder.png', - ); - - return placeholder.buffer.asUint8List(); - } -} - -/// Get instance of [_IVideoService] -// ignore: non_constant_identifier_names -_IVideoService get StreamVideoService => _IVideoService.instance; diff --git a/packages/stream_chat_flutter/lib/src/video/video_thumbnail_image.dart b/packages/stream_chat_flutter/lib/src/video/video_thumbnail_image.dart deleted file mode 100644 index 21018113fc..0000000000 --- a/packages/stream_chat_flutter/lib/src/video/video_thumbnail_image.dart +++ /dev/null @@ -1,149 +0,0 @@ -import 'dart:ui' as ui; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:get_thumbnail_video/index.dart'; -import 'package:stream_chat_flutter/src/video/video_service.dart'; - -/// {@template video_thumbnail_image} -/// A custom [ImageProvider] class for loading video thumbnails as images in -/// Flutter. -/// -/// Use this class to load a video thumbnail as an image. It takes a video URL -/// or path and generates a thumbnail image from the video. The generated -/// thumbnail image can be used with the [Image] widget. -/// -/// {@tool snippet} -/// Load a video thumbnail from a URL: -/// -/// ```dart -/// Image( -/// image: StreamVideoThumbnailImage( -/// video: 'https://example.com/video.mp4', -/// maxHeight: 200, -/// maxWidth: 200, -/// ), -/// ) -/// ``` -/// {@end-tool} -/// -/// {@tool snippet} -/// Load a video thumbnail from a local file path: -/// -/// ```dart -/// Image( -/// image: StreamVideoThumbnailImage( -/// video: '/path/to/video.mp4', -/// maxHeight: 200, -/// maxWidth: 200, -/// ), -/// ) -/// ``` -/// {@end-tool} -/// {@endtemplate} -class StreamVideoThumbnailImage - extends ImageProvider { - /// {@macro video_thumbnail_image} - const StreamVideoThumbnailImage({ - required this.video, - this.headers, - this.imageFormat = ImageFormat.PNG, - this.maxHeight = 0, - this.maxWidth = 0, - this.timeMs = 0, - this.quality = 10, - this.scale = 1.0, - }); - - /// The URL or path of the video from which to generate the thumbnail. - final String video; - - /// Additional headers to include in the HTTP request when fetching the video. - final Map? headers; - - /// The format of the generated thumbnail image. - final ImageFormat imageFormat; - - /// The maximum height of the generated thumbnail image. - final int maxHeight; - - /// The maximum width of the generated thumbnail image. - final int maxWidth; - - /// The timestamp in milliseconds at which to generate the thumbnail. - final int timeMs; - - /// The quality of the generated thumbnail image. - final int quality; - - /// The scale to place in the [ImageInfo] object of the image. - final double scale; - - @override - Future obtainKey( - ImageConfiguration configuration, - ) { - return SynchronousFuture(this); - } - - @override - @Deprecated('Will get replaced by loadImage in the next major version.') - ImageStreamCompleter loadBuffer( - StreamVideoThumbnailImage key, - DecoderBufferCallback decode, - ) { - return MultiFrameImageStreamCompleter( - codec: _loadAsync(key, decode), - scale: key.scale, - debugLabel: key.video, - informationCollector: () => [ - DiagnosticsProperty('Image provider', this), - DiagnosticsProperty('Image key', key), - ], - ); - } - - @Deprecated('Will get replaced by loadImage in the next major version.') - Future _loadAsync( - StreamVideoThumbnailImage key, - DecoderBufferCallback decode, - ) async { - assert(key == this, '$key is not $this'); - - final bytes = await StreamVideoService.generateVideoThumbnail( - video: key.video, - headers: key.headers, - imageFormat: key.imageFormat, - maxHeight: key.maxHeight, - maxWidth: key.maxWidth, - timeMs: key.timeMs, - quality: key.quality, - ); - - if (bytes == null || bytes.lengthInBytes == 0) { - throw Exception('VideoThumbnailImage is an empty file: ${key.video}'); - } - - final buffer = await ui.ImmutableBuffer.fromUint8List(bytes); - return decode(buffer); - } - - @override - bool operator ==(Object other) { - if (other.runtimeType != runtimeType) { - return false; - } - return other is StreamVideoThumbnailImage && - other.video == video && - other.scale == scale; - } - - @override - int get hashCode => Object.hash(video, scale); - - @override - String toString() { - final runtimeType = objectRuntimeType(this, 'StreamVideoThumbnailImage'); - return '$runtimeType($video, scale: $scale)'; - } -} diff --git a/packages/stream_chat_flutter/lib/src/video/vlc/vlc_manager.dart b/packages/stream_chat_flutter/lib/src/video/vlc/vlc_manager.dart deleted file mode 100644 index adecfc56b9..0000000000 --- a/packages/stream_chat_flutter/lib/src/video/vlc/vlc_manager.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:stream_chat_flutter/src/video/vlc/vlc_stub.dart' - if (dart.library.io) 'vlc_manager_desktop.dart' - if (dart.library.html) 'vlc_manager_web.dart'; - -/// {@template vlcManager} -/// An abstract class for the purpose of ensuring Flutter applications that -/// target both desktop & web do not crash when building for web targets. -/// {@endtemplate} -abstract class VlcManager { - // ignore: use_late_for_private_fields_and_variables - static VlcManager? _instance; - - /// The current instance of [VlcManager]. - static VlcManager get instance { - _instance = getVlc(); - - return _instance!; - } - - /// Initializes VLC. - void initialize(); -} diff --git a/packages/stream_chat_flutter/lib/src/video/vlc/vlc_manager_desktop.dart b/packages/stream_chat_flutter/lib/src/video/vlc/vlc_manager_desktop.dart deleted file mode 100644 index 4064f40547..0000000000 --- a/packages/stream_chat_flutter/lib/src/video/vlc/vlc_manager_desktop.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:media_kit/media_kit.dart'; -import 'package:stream_chat_flutter/src/video/vlc/vlc_manager.dart'; - -/// The desktop implementation of [VlcManager]. It simply initializes VLC. -class VlcManagerDesktop extends VlcManager { - @override - void initialize() { - MediaKit.ensureInitialized(); - } -} - -/// Allows [VlcManager] to return the correct implementation. -VlcManager getVlc() => VlcManagerDesktop(); diff --git a/packages/stream_chat_flutter/lib/src/video/vlc/vlc_manager_web.dart b/packages/stream_chat_flutter/lib/src/video/vlc/vlc_manager_web.dart deleted file mode 100644 index 3462350b1e..0000000000 --- a/packages/stream_chat_flutter/lib/src/video/vlc/vlc_manager_web.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:flutter/rendering.dart'; -import 'package:stream_chat_flutter/src/video/vlc/vlc_manager.dart'; - -/// The web implementation of [VlcManager]. Naturally, it does nothing. It -/// exists simply to satisfy the requirements of conditional imports. -class VlcManagerWeb extends VlcManager { - @override - void initialize() { - debugPrint('Stub initialization for VLC.'); - } -} - -/// Allows [VlcManager] to return the correct implementation. -VlcManager getVlc() => VlcManagerWeb(); diff --git a/packages/stream_chat_flutter/lib/src/video/vlc/vlc_stub.dart b/packages/stream_chat_flutter/lib/src/video/vlc/vlc_stub.dart deleted file mode 100644 index 289961fd0b..0000000000 --- a/packages/stream_chat_flutter/lib/src/video/vlc/vlc_stub.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'package:stream_chat_flutter/src/video/vlc/vlc_manager.dart'; - -/// Method stub for getting the right instance of [VlcManager] on the right -/// platform. -VlcManager getVlc() => throw UnsupportedError('Cannot create VLC Manager'); diff --git a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart deleted file mode 100644 index 9f42644272..0000000000 --- a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart +++ /dev/null @@ -1,130 +0,0 @@ -export 'package:jiffy/jiffy.dart'; -export 'package:photo_manager/photo_manager.dart' - show ThumbnailSize, ThumbnailFormat; -export 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -export 'src/ai_assistant/ai_typing_indicator_view.dart'; -export 'src/ai_assistant/stream_typewriter_builder.dart'; -export 'src/ai_assistant/streaming_message_view.dart'; -export 'src/attachment/attachment.dart'; -export 'src/attachment/builder/attachment_widget_builder.dart'; -export 'src/attachment/builder/voice_recording_attachment_builder/stream_voice_recording_list_player.dart'; -export 'src/attachment/builder/voice_recording_attachment_builder/stream_voice_recording_loading.dart'; -export 'src/attachment/builder/voice_recording_attachment_builder/stream_voice_recording_player.dart'; -export 'src/attachment/gallery_attachment.dart'; -export 'src/attachment/handler/stream_attachment_handler.dart'; -export 'src/attachment/image_attachment.dart'; -export 'src/attachment/stream_attachment_package.dart'; -export 'src/attachment/thumbnail/file_attachment_thumbnail.dart'; -export 'src/attachment/thumbnail/giphy_attachment_thumbnail.dart'; -export 'src/attachment/thumbnail/image_attachment_thumbnail.dart'; -export 'src/attachment/thumbnail/media_attachment_thumbnail.dart'; -export 'src/attachment/thumbnail/thumbnail_error.dart'; -export 'src/attachment/thumbnail/video_attachment_thumbnail.dart'; -export 'src/attachment/url_attachment.dart'; -export 'src/attachment/video_attachment.dart'; -export 'src/attachment/voice_recording_attachment_playlist.dart'; -export 'src/attachment_actions_modal/attachment_actions_modal.dart'; -export 'src/autocomplete/stream_autocomplete.dart'; -export 'src/avatars/gradient_avatar.dart'; -export 'src/avatars/group_avatar.dart'; -export 'src/avatars/user_avatar.dart'; -export 'src/bottom_sheets/attachment_modal_sheet.dart'; -export 'src/bottom_sheets/edit_message_sheet.dart'; -export 'src/bottom_sheets/error_alert_sheet.dart'; -export 'src/bottom_sheets/stream_channel_info_bottom_sheet.dart'; -export 'src/channel/channel_header.dart'; -export 'src/channel/channel_info.dart'; -export 'src/channel/channel_list_header.dart'; -export 'src/channel/channel_name.dart'; -export 'src/channel/stream_channel_avatar.dart'; -export 'src/channel/stream_channel_name.dart'; -export 'src/channel/stream_message_preview_text.dart'; -export 'src/fullscreen_media/full_screen_media.dart'; -export 'src/fullscreen_media/full_screen_media_builder.dart'; -export 'src/gallery/gallery_footer.dart'; -export 'src/gallery/gallery_header.dart'; -export 'src/icons/stream_svg_icon.dart'; -export 'src/indicators/sending_indicator.dart'; -export 'src/indicators/typing_indicator.dart'; -export 'src/indicators/unread_indicator.dart'; -export 'src/indicators/upload_progress_indicator.dart'; -export 'src/keyboard_shortcuts/keyboard_shortcut_runner.dart'; -export 'src/localization/stream_chat_localizations.dart'; -export 'src/localization/translations.dart' show DefaultTranslations; -export 'src/message_actions_modal/message_action.dart'; -export 'src/message_input/attachment_picker/stream_attachment_picker.dart'; -export 'src/message_input/attachment_picker/stream_attachment_picker_bottom_sheet.dart'; -export 'src/message_input/audio_recorder/audio_recorder_controller.dart'; -export 'src/message_input/audio_recorder/audio_recorder_state.dart'; -export 'src/message_input/audio_recorder/stream_audio_recorder.dart'; -export 'src/message_input/countdown_button.dart'; -export 'src/message_input/enums.dart'; -export 'src/message_input/stream_message_input.dart'; -export 'src/message_input/stream_message_input_attachment_list.dart'; -export 'src/message_input/stream_message_send_button.dart'; -export 'src/message_input/stream_message_text_field.dart'; -export 'src/message_list_view/message_details.dart'; -export 'src/message_list_view/message_list_view.dart'; -export 'src/message_widget/deleted_message.dart'; -export 'src/message_widget/message_text.dart'; -export 'src/message_widget/message_widget.dart'; -export 'src/message_widget/message_widget_content_components.dart'; -export 'src/message_widget/poll_message.dart'; -export 'src/message_widget/reactions/reaction_picker.dart'; -export 'src/message_widget/text_bubble.dart'; -export 'src/misc/animated_circle_border_painter.dart'; -export 'src/misc/back_button.dart'; -export 'src/misc/connection_status_builder.dart'; -export 'src/misc/date_divider.dart'; -export 'src/misc/info_tile.dart'; -export 'src/misc/markdown_message.dart'; -export 'src/misc/option_list_tile.dart'; -export 'src/misc/reaction_icon.dart'; -export 'src/misc/stream_neumorphic_button.dart'; -export 'src/misc/swipeable.dart'; -export 'src/misc/system_message.dart'; -export 'src/misc/thread_header.dart'; -export 'src/misc/visible_footnote.dart'; -export 'src/poll/creator/stream_poll_creator_dialog.dart'; -export 'src/poll/creator/stream_poll_creator_widget.dart'; -export 'src/poll/interactor/stream_poll_interactor.dart'; -export 'src/poll/stream_poll_comments_dialog.dart'; -export 'src/poll/stream_poll_option_votes_dialog.dart'; -export 'src/poll/stream_poll_options_dialog.dart'; -export 'src/poll/stream_poll_results_dialog.dart'; -export 'src/poll/stream_poll_text_field.dart'; -export 'src/scroll_view/channel_scroll_view/stream_channel_grid_tile.dart'; -export 'src/scroll_view/channel_scroll_view/stream_channel_grid_view.dart'; -export 'src/scroll_view/channel_scroll_view/stream_channel_list_tile.dart'; -export 'src/scroll_view/channel_scroll_view/stream_channel_list_view.dart'; -export 'src/scroll_view/member_scroll_view/stream_member_grid_view.dart'; -export 'src/scroll_view/member_scroll_view/stream_member_list_view.dart'; -export 'src/scroll_view/message_search_scroll_view/stream_message_search_grid_view.dart'; -export 'src/scroll_view/message_search_scroll_view/stream_message_search_list_tile.dart'; -export 'src/scroll_view/message_search_scroll_view/stream_message_search_list_view.dart'; -export 'src/scroll_view/photo_gallery/stream_photo_gallery.dart'; -export 'src/scroll_view/photo_gallery/stream_photo_gallery_controller.dart'; -export 'src/scroll_view/photo_gallery/stream_photo_gallery_tile.dart'; -export 'src/scroll_view/poll_vote_scroll_view/stream_poll_vote_list_tile.dart'; -export 'src/scroll_view/poll_vote_scroll_view/stream_poll_vote_list_view.dart'; -export 'src/scroll_view/stream_scroll_view_empty_widget.dart'; -export 'src/scroll_view/stream_scroll_view_indexed_widget_builder.dart'; -export 'src/scroll_view/thread_scroll_view/stream_thread_list_tile.dart'; -export 'src/scroll_view/thread_scroll_view/stream_thread_list_view.dart'; -export 'src/scroll_view/thread_scroll_view/stream_unread_threads_banner.dart'; -export 'src/scroll_view/user_scroll_view/stream_user_grid_tile.dart'; -export 'src/scroll_view/user_scroll_view/stream_user_grid_view.dart'; -export 'src/scroll_view/user_scroll_view/stream_user_list_tile.dart'; -export 'src/scroll_view/user_scroll_view/stream_user_list_view.dart'; -export 'src/stream_chat.dart'; -export 'src/stream_chat_configuration.dart'; -export 'src/theme/stream_chat_theme.dart'; -export 'src/theme/themes.dart'; -export 'src/user/user_mention_tile.dart'; -export 'src/utils/device_segmentation.dart'; -export 'src/utils/extensions.dart'; -export 'src/utils/helpers.dart'; -export 'src/utils/typedefs.dart'; -// TODO: Remove this in favor of StreamVideoAttachmentThumbnail. -export 'src/video/video_thumbnail_image.dart'; diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml deleted file mode 100644 index 3aba54580c..0000000000 --- a/packages/stream_chat_flutter/pubspec.yaml +++ /dev/null @@ -1,91 +0,0 @@ -name: stream_chat_flutter -homepage: https://github.com/GetStream/stream-chat-flutter -description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter. -version: 9.4.0 -repository: https://github.com/GetStream/stream-chat-flutter -issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues - -# Note: The environment configuration and dependency versions are managed by Melos. -# -# Do not edit them manually. -# -# Steps to update dependencies: -# 1. Modify the version in the melos.yaml file. -# 2. Run `melos bootstrap` to apply changes. -# -# Steps to add a new dependency: -# 1. Add the dependency to this list. -# 2. Add it to the melos.yaml file for future updates. - -environment: - sdk: ^3.6.2 - flutter: ">=3.27.4" - -dependencies: - cached_network_image: ^3.3.1 - chewie: ^1.8.1 - collection: ^1.17.2 - contextmenu: ^3.0.0 - desktop_drop: ^0.5.0 - diacritic: ^0.1.5 - dio: ^5.4.3+1 - ezanimation: ^0.6.0 - file_picker: ^8.0.5 - file_selector: ^1.0.3 - flutter: - sdk: flutter - flutter_markdown: ^0.7.2+1 - flutter_portal: ^1.1.4 - flutter_svg: ^2.0.10+1 - gal: ^2.3.1 - get_thumbnail_video: ^0.7.3 - http_parser: ^4.0.2 - image_picker: ^1.1.2 - image_size_getter: ^2.3.0 - jiffy: ^6.2.1 - just_audio: ^0.9.38 - lottie: ^3.1.2 - media_kit: ^1.1.10+1 - media_kit_video: ^1.2.4 - meta: ^1.9.1 - path_provider: ^2.1.3 - photo_manager: ^3.2.0 - photo_view: ^0.15.0 - rate_limiter: ^1.0.0 - record: ^5.2.0 - rxdart: ^0.28.0 - share_plus: ^10.0.2 - shimmer: ^3.0.0 - stream_chat_flutter_core: ^9.4.0 - svg_icon_widget: ^0.0.1 - synchronized: ^3.1.0+1 - thumblr: ^0.0.4 - url_launcher: ^6.3.0 - video_player: ^2.8.7 - -dev_dependencies: - alchemist: ^0.11.0 - faker_dart: ^0.2.1 - flutter_test: - sdk: flutter - mocktail: ^1.0.0 - path: ^1.8.3 - path_provider_platform_interface: ^2.0.0 - plugin_platform_interface: ^2.0.0 - -flutter: - assets: - - lib/assets/animations/ - - lib/assets/icons/ - - lib/assets/icons/colored/ - - lib/assets/images/ - uses-material-design: true - -# This package supports all platforms listed below. -platforms: - android: - ios: - linux: - macos: - web: - windows: \ No newline at end of file diff --git a/packages/stream_chat_flutter/screenshots/channel_header.png b/packages/stream_chat_flutter/screenshots/channel_header.png deleted file mode 100644 index c0a2e29f46..0000000000 Binary files a/packages/stream_chat_flutter/screenshots/channel_header.png and /dev/null differ diff --git a/packages/stream_chat_flutter/screenshots/channel_header_paint.png b/packages/stream_chat_flutter/screenshots/channel_header_paint.png deleted file mode 100644 index 8c552b6541..0000000000 Binary files a/packages/stream_chat_flutter/screenshots/channel_header_paint.png and /dev/null differ diff --git a/packages/stream_chat_flutter/screenshots/channel_image.png b/packages/stream_chat_flutter/screenshots/channel_image.png deleted file mode 100644 index 230c2b61f0..0000000000 Binary files a/packages/stream_chat_flutter/screenshots/channel_image.png and /dev/null differ diff --git a/packages/stream_chat_flutter/screenshots/channel_image_paint.png b/packages/stream_chat_flutter/screenshots/channel_image_paint.png deleted file mode 100644 index d4c5ca370b..0000000000 Binary files a/packages/stream_chat_flutter/screenshots/channel_image_paint.png and /dev/null differ diff --git a/packages/stream_chat_flutter/screenshots/channel_list_view.png b/packages/stream_chat_flutter/screenshots/channel_list_view.png deleted file mode 100644 index c3e4e5e27b..0000000000 Binary files a/packages/stream_chat_flutter/screenshots/channel_list_view.png and /dev/null differ diff --git a/packages/stream_chat_flutter/screenshots/channel_list_view_paint.png b/packages/stream_chat_flutter/screenshots/channel_list_view_paint.png deleted file mode 100644 index 9fb72e6563..0000000000 Binary files a/packages/stream_chat_flutter/screenshots/channel_list_view_paint.png and /dev/null differ diff --git a/packages/stream_chat_flutter/screenshots/channel_preview.png b/packages/stream_chat_flutter/screenshots/channel_preview.png deleted file mode 100644 index c98d4c08b4..0000000000 Binary files a/packages/stream_chat_flutter/screenshots/channel_preview.png and /dev/null differ diff --git a/packages/stream_chat_flutter/screenshots/channel_preview_paint.png b/packages/stream_chat_flutter/screenshots/channel_preview_paint.png deleted file mode 100644 index eda9f14f4e..0000000000 Binary files a/packages/stream_chat_flutter/screenshots/channel_preview_paint.png and /dev/null differ diff --git a/packages/stream_chat_flutter/screenshots/message_input.png b/packages/stream_chat_flutter/screenshots/message_input.png deleted file mode 100644 index 00331e5ec0..0000000000 Binary files a/packages/stream_chat_flutter/screenshots/message_input.png and /dev/null differ diff --git a/packages/stream_chat_flutter/screenshots/message_input2.png b/packages/stream_chat_flutter/screenshots/message_input2.png deleted file mode 100644 index dfe7df5918..0000000000 Binary files a/packages/stream_chat_flutter/screenshots/message_input2.png and /dev/null differ diff --git a/packages/stream_chat_flutter/screenshots/message_input2_paint.png b/packages/stream_chat_flutter/screenshots/message_input2_paint.png deleted file mode 100644 index 915e64cacf..0000000000 Binary files a/packages/stream_chat_flutter/screenshots/message_input2_paint.png and /dev/null differ diff --git a/packages/stream_chat_flutter/screenshots/message_input_paint.png b/packages/stream_chat_flutter/screenshots/message_input_paint.png deleted file mode 100644 index d6cc494760..0000000000 Binary files a/packages/stream_chat_flutter/screenshots/message_input_paint.png and /dev/null differ diff --git a/packages/stream_chat_flutter/screenshots/message_listview.png b/packages/stream_chat_flutter/screenshots/message_listview.png deleted file mode 100644 index b4ca5e7e13..0000000000 Binary files a/packages/stream_chat_flutter/screenshots/message_listview.png and /dev/null differ diff --git a/packages/stream_chat_flutter/screenshots/message_listview_paint.png b/packages/stream_chat_flutter/screenshots/message_listview_paint.png deleted file mode 100644 index 2ae0485639..0000000000 Binary files a/packages/stream_chat_flutter/screenshots/message_listview_paint.png and /dev/null differ diff --git a/packages/stream_chat_flutter/screenshots/message_widget.png b/packages/stream_chat_flutter/screenshots/message_widget.png deleted file mode 100644 index 3d99f0568a..0000000000 Binary files a/packages/stream_chat_flutter/screenshots/message_widget.png and /dev/null differ diff --git a/packages/stream_chat_flutter/screenshots/message_widget_paint.png b/packages/stream_chat_flutter/screenshots/message_widget_paint.png deleted file mode 100644 index 802deb6270..0000000000 Binary files a/packages/stream_chat_flutter/screenshots/message_widget_paint.png and /dev/null differ diff --git a/packages/stream_chat_flutter/screenshots/reaction_picker.png b/packages/stream_chat_flutter/screenshots/reaction_picker.png deleted file mode 100644 index f709c20117..0000000000 Binary files a/packages/stream_chat_flutter/screenshots/reaction_picker.png and /dev/null differ diff --git a/packages/stream_chat_flutter/screenshots/reaction_picker_paint.png b/packages/stream_chat_flutter/screenshots/reaction_picker_paint.png deleted file mode 100644 index f70ed795f1..0000000000 Binary files a/packages/stream_chat_flutter/screenshots/reaction_picker_paint.png and /dev/null differ diff --git a/packages/stream_chat_flutter/screenshots/thread_header.png b/packages/stream_chat_flutter/screenshots/thread_header.png deleted file mode 100644 index a6325e0f63..0000000000 Binary files a/packages/stream_chat_flutter/screenshots/thread_header.png and /dev/null differ diff --git a/packages/stream_chat_flutter/screenshots/thread_header_paint.png b/packages/stream_chat_flutter/screenshots/thread_header_paint.png deleted file mode 100644 index eef1955e85..0000000000 Binary files a/packages/stream_chat_flutter/screenshots/thread_header_paint.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/conditional_parent_builder/conditional_parent_builder_test.dart b/packages/stream_chat_flutter/test/conditional_parent_builder/conditional_parent_builder_test.dart deleted file mode 100644 index c52c28ab4b..0000000000 --- a/packages/stream_chat_flutter/test/conditional_parent_builder/conditional_parent_builder_test.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/conditional_parent_builder/conditional_parent_builder.dart'; - -void main() { - testWidgets('ConditionalParentBuilder builds the parent widget', - (tester) async { - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: ConditionalParentBuilder( - builder: (context, child) => Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - child, - ], - ), - child: const Text('Hello World!'), - ), - ), - ), - ), - ); - expect(find.byType(Column), findsOneWidget); - expect(find.byType(Text), findsOneWidget); - }); - - testWidgets('ConditionalParentBuilder does not build the parent widget', - (tester) async { - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: ConditionalParentBuilder( - builder: (context, child) { - return child; - }, - child: const Text('Hello World!'), - ), - ), - ), - ), - ); - expect(find.byType(Column), findsNothing); - expect(find.byType(Text), findsOneWidget); - }); -} diff --git a/packages/stream_chat_flutter/test/flutter_test_config.dart b/packages/stream_chat_flutter/test/flutter_test_config.dart deleted file mode 100644 index 7b00df9385..0000000000 --- a/packages/stream_chat_flutter/test/flutter_test_config.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:alchemist/alchemist.dart'; - -Future testExecutable(FutureOr Function() testMain) async { - final isRunningInCi = Platform.environment.containsKey('CI') || - Platform.environment.containsKey('GITHUB_ACTIONS'); - - return AlchemistConfig.runWithConfig( - config: AlchemistConfig( - platformGoldensConfig: PlatformGoldensConfig( - enabled: !isRunningInCi, - ), - ), - run: testMain, - ); -} diff --git a/packages/stream_chat_flutter/test/platform_widget_builder/desktop_widget_builder_test.dart b/packages/stream_chat_flutter/test/platform_widget_builder/desktop_widget_builder_test.dart deleted file mode 100644 index a7b867ce38..0000000000 --- a/packages/stream_chat_flutter/test/platform_widget_builder/desktop_widget_builder_test.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'package:flutter/foundation.dart' - show debugDefaultTargetPlatformOverride; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/platform_widget_builder/platform_widget_builder.dart'; - -void main() { - testWidgets( - 'PlatformWidgetBuilder builds the correct widget for mobile', - (tester) async { - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: DesktopWidgetBuilder( - macOS: (context, child) => Text( - '$debugDefaultTargetPlatformOverride', - ), - ), - ), - ), - ), - ); - - expect(find.text('$debugDefaultTargetPlatformOverride'), findsOneWidget); - }, - variant: const TargetPlatformVariant({ - TargetPlatform.macOS, - }), - ); - - testWidgets( - 'PlatformWidgetBuilder builds the correct widget for desktop', - (tester) async { - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: DesktopWidgetBuilder( - windows: (context, child) => Text( - '$debugDefaultTargetPlatformOverride', - ), - ), - ), - ), - ), - ); - - expect(find.text('$debugDefaultTargetPlatformOverride'), findsOneWidget); - }, - variant: const TargetPlatformVariant({ - TargetPlatform.windows, - }), - ); - - testWidgets( - 'PlatformWidgetBuilder builds the correct widget for web', - (tester) async { - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: DesktopWidgetBuilder( - linux: (context, child) => const Text('Web'), - ), - ), - ), - ), - ); - - expect(find.text('Web'), findsOneWidget); - }, - variant: const TargetPlatformVariant({ - TargetPlatform.linux, - }), - ); -} diff --git a/packages/stream_chat_flutter/test/platform_widget_builder/platform_widget_builder_test.dart b/packages/stream_chat_flutter/test/platform_widget_builder/platform_widget_builder_test.dart deleted file mode 100644 index 08425ebc9b..0000000000 --- a/packages/stream_chat_flutter/test/platform_widget_builder/platform_widget_builder_test.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:flutter/foundation.dart' - show debugDefaultTargetPlatformOverride; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/platform_widget_builder/platform_widget_builder.dart'; - -void main() { - testWidgets( - 'PlatformWidgetBuilder builds the correct widget for mobile', - (tester) async { - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: PlatformWidgetBuilder( - mobile: (context, child) => Text( - '$debugDefaultTargetPlatformOverride', - ), - ), - ), - ), - ), - ); - - expect(find.text('$debugDefaultTargetPlatformOverride'), findsOneWidget); - }, - variant: const TargetPlatformVariant({ - TargetPlatform.android, - TargetPlatform.iOS, - }), - ); - - testWidgets( - 'PlatformWidgetBuilder builds the correct widget for desktop', - (tester) async { - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: PlatformWidgetBuilder( - desktop: (context, child) => Text( - '$debugDefaultTargetPlatformOverride', - ), - ), - ), - ), - ), - ); - - expect(find.text('$debugDefaultTargetPlatformOverride'), findsOneWidget); - }, - variant: TargetPlatformVariant.desktop(), - ); - - testWidgets( - 'PlatformWidgetBuilder builds the correct widget for web', - (tester) async { - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: PlatformWidgetBuilder( - web: (context, child) => const Text('Web'), - ), - ), - ), - ), - ); - - expect(find.text('Web'), findsOneWidget); - }, - variant: const TargetPlatformVariant({TargetPlatform.fuchsia}), // hacky :/ - ); -} diff --git a/packages/stream_chat_flutter/test/scrollable_positioned_list/horizontal_scrollable_positioned_list_test.dart b/packages/stream_chat_flutter/test/scrollable_positioned_list/horizontal_scrollable_positioned_list_test.dart deleted file mode 100644 index 6e851ada5c..0000000000 --- a/packages/stream_chat_flutter/test/scrollable_positioned_list/horizontal_scrollable_positioned_list_test.dart +++ /dev/null @@ -1,326 +0,0 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart'; - -const screenHeight = 100.0; -const screenWidth = 400.0; -const itemWidth = screenWidth / 10.0; -const itemCount = 500; -const scrollDuration = Duration(seconds: 1); - -void main() { - Future setUpWidgetTest( - WidgetTester tester, { - ItemScrollController? itemScrollController, - ItemPositionsListener? itemPositionsListener, - bool reverse = false, - EdgeInsets? padding, - int initialScrollIndex = 0, - }) async { - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - await tester.pumpWidget( - MaterialApp( - home: ScrollablePositionedList.builder( - itemCount: itemCount, - itemScrollController: itemScrollController, - itemBuilder: (context, index) => SizedBox( - width: itemWidth, - child: Text('Item $index'), - ), - itemPositionsListener: itemPositionsListener, - scrollDirection: Axis.horizontal, - reverse: reverse, - padding: padding, - initialScrollIndex: initialScrollIndex, - ), - ), - ); - } - - final fadeTransitionFinder = find.descendant( - of: find.byType(ScrollablePositionedList), - matching: find.byType(FadeTransition), - ); - - testWidgets('List positioned with 0 at left', (WidgetTester tester) async { - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, itemPositionsListener: itemPositionsListener); - - expect(tester.getTopLeft(find.text('Item 0')).dx, 0); - expect(tester.getBottomRight(find.text('Item 9')).dx, screenWidth); - expect(find.text('Item 10'), findsNothing); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemTrailingEdge, - 1 / 10); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemTrailingEdge, - 1); - }); - - testWidgets('List positioned with 0 at right', (WidgetTester tester) async { - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemPositionsListener: itemPositionsListener, reverse: true); - - expect(tester.getBottomRight(find.text('Item 0')).dx, screenWidth); - expect(tester.getTopLeft(find.text('Item 9')).dx, 0); - expect(find.text('Item 10'), findsNothing); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemTrailingEdge, - 1 / 10); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemTrailingEdge, - 1); - }); - - testWidgets('Scroll to 2 (already on screen)', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - unawaited( - itemScrollController.scrollTo(index: 2, duration: scrollDuration)); - await tester.pump(); - await tester.pump(scrollDuration); - - expect(find.text('Item 1'), findsNothing); - expect(tester.getTopLeft(find.text('Item 2')).dx, 0); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 2) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 2) - .itemTrailingEdge, - 1 / 10); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 11) - .itemTrailingEdge, - 1); - }); - - testWidgets('Scroll to 100 (not already on screen)', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pumpAndSettle(); - - expect(find.text('Item 99'), findsNothing); - expect(find.text('Item 100'), findsOneWidget); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 100) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 100) - .itemTrailingEdge, - 1 / 10); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 109) - .itemTrailingEdge, - 1); - }); - - testWidgets('Jump to 100', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - itemScrollController.jumpTo(index: 100); - await tester.pumpAndSettle(); - - expect(tester.getTopLeft(find.text('Item 100')).dx, 0); - expect(tester.getBottomRight(find.text('Item 109')).dy, screenHeight); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 100) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 100) - .itemTrailingEdge, - 1 / 10); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 109) - .itemLeadingEdge, - 9 / 10); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 109) - .itemTrailingEdge, - 1); - }); - - testWidgets('Scroll to 20 without fading', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - var fadeTransition = tester.widget(fadeTransitionFinder); - final initialOpacity = fadeTransition.opacity; - - unawaited( - itemScrollController.scrollTo(index: 20, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration ~/ 2); - - fadeTransition = tester.widget(fadeTransitionFinder); - expect(fadeTransition.opacity, initialOpacity); - - await tester.pumpAndSettle(); - - expect(find.text('Item 14'), findsNothing); - expect(find.text('Item 20'), findsOneWidget); - }); - - testWidgets('padding test - centered sliver at left', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - padding: const EdgeInsets.all(10), - ); - - expect(tester.getTopLeft(find.text('Item 0')), const Offset(10, 10)); - expect(tester.getTopLeft(find.text('Item 1')), - const Offset(itemWidth + 10, 10)); - expect(tester.getBottomRight(find.text('Item 1')), - const Offset(10 + itemWidth * 2, screenHeight - 10)); - - unawaited( - itemScrollController.scrollTo(index: 490, duration: scrollDuration)); - await tester.pumpAndSettle(); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(-100, 0)); - await tester.pumpAndSettle(); - - expect(tester.getBottomRight(find.text('Item 499')), - const Offset(screenWidth - 10, screenHeight - 10)); - }); - - testWidgets('padding test - centered sliver not at left', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - initialScrollIndex: 2, - padding: const EdgeInsets.all(10), - ); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(200, 0)); - await tester.pumpAndSettle(); - - expect(tester.getTopLeft(find.text('Item 0')), const Offset(10, 10)); - expect(tester.getTopLeft(find.text('Item 2')), - const Offset(10 + itemWidth * 2, 10)); - expect(tester.getTopLeft(find.text('Item 3')), - const Offset(10 + itemWidth * 3, 10)); - }); - - testWidgets('padding test - reversed - centered sliver at right', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - padding: const EdgeInsets.all(10), - reverse: true, - ); - - expect(tester.getTopRight(find.text('Item 0')), - const Offset(screenWidth - 10, 10)); - expect(tester.getTopRight(find.text('Item 1')), - const Offset(screenWidth - (itemWidth + 10), 10)); - expect(tester.getBottomLeft(find.text('Item 1')), - const Offset(screenWidth - (10 + itemWidth * 2), screenHeight - 10)); - - unawaited( - itemScrollController.scrollTo(index: 490, duration: scrollDuration)); - await tester.pumpAndSettle(); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(100, 0)); - await tester.pumpAndSettle(); - - expect(tester.getTopLeft(find.text('Item 499')), const Offset(10, 10)); - }); - - testWidgets('padding test - reversed - centered sliver not at right', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - initialScrollIndex: 2, - padding: const EdgeInsets.all(10), - reverse: true, - ); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(-200, 0)); - await tester.pumpAndSettle(); - - expect(tester.getTopRight(find.text('Item 0')), - const Offset(screenWidth - 10, 10)); - expect(tester.getTopRight(find.text('Item 2')), - const Offset(screenWidth - (10 + itemWidth * 2), 10)); - expect(tester.getTopRight(find.text('Item 3')), - const Offset(screenWidth - (10 + itemWidth * 3), 10)); - }); -} diff --git a/packages/stream_chat_flutter/test/scrollable_positioned_list/positioned_list_test.dart b/packages/stream_chat_flutter/test/scrollable_positioned_list/positioned_list_test.dart deleted file mode 100644 index 887165a512..0000000000 --- a/packages/stream_chat_flutter/test/scrollable_positioned_list/positioned_list_test.dart +++ /dev/null @@ -1,415 +0,0 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/item_positions_notifier.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/positioned_list.dart'; - -const screenHeight = 400.0; -const screenWidth = 400.0; -const itemHeight = screenHeight / 10.0; -const defaultItemCount = 500; -const cacheExtent = itemHeight * 2; - -void main() { - final itemPositionsNotifier = ItemPositionsListener.create(); - - Future setUpWidgetTest( - WidgetTester tester, { - int topItem = 0, - ScrollController? scrollController, - double anchor = 0, - int itemCount = defaultItemCount, - }) async { - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - await tester.pumpWidget( - MaterialApp( - home: PositionedList( - itemCount: itemCount, - positionedIndex: topItem, - alignment: anchor, - controller: scrollController, - itemBuilder: (context, index) => SizedBox( - height: itemHeight, - child: Text('Item $index'), - ), - itemPositionsNotifier: itemPositionsNotifier as ItemPositionsNotifier, - cacheExtent: cacheExtent, - ), - ), - ); - } - - testWidgets('short list', (WidgetTester tester) async { - await setUpWidgetTest(tester, itemCount: 5); - await tester.pump(); - - expect(find.text('Item 0'), findsOneWidget); - expect(find.text('Item 4'), findsOneWidget); - expect(find.text('Item 5'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 4) - .itemTrailingEdge, - 1 / 2); - }); - - testWidgets('List positioned with 0 at top', (WidgetTester tester) async { - await setUpWidgetTest(tester); - await tester.pump(); - - expect(find.text('Item 0'), findsOneWidget); - expect(find.text('Item 9'), findsOneWidget); - expect(find.text('Item 10'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemTrailingEdge, - 1); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 10) - .itemLeadingEdge, - 1); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 10) - .itemTrailingEdge, - 11 / 10); - }); - - testWidgets('List positioned with 5 at top', (WidgetTester tester) async { - await setUpWidgetTest(tester, topItem: 5); - await tester.pump(); - - expect(find.text('Item 4'), findsNothing); - expect(find.text('Item 5'), findsOneWidget); - expect(find.text('Item 14'), findsOneWidget); - expect(find.text('Item 15'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 4) - .itemLeadingEdge, - -1 / 10); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 4) - .itemTrailingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 5) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 14) - .itemTrailingEdge, - 1); - }); - - testWidgets('List positioned with 20 at bottom', (WidgetTester tester) async { - await setUpWidgetTest(tester, topItem: 20, anchor: 1); - await tester.pump(); - - expect(find.text('Item 20'), findsNothing); - expect(find.text('Item 19'), findsOneWidget); - expect(find.text('Item 10'), findsOneWidget); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 10) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 19) - .itemLeadingEdge, - 9 / 10); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 19) - .itemTrailingEdge, - 1); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 20) - .itemLeadingEdge, - 1); - }); - - testWidgets('List positioned with 20 at halfway', - (WidgetTester tester) async { - await setUpWidgetTest(tester, topItem: 20, anchor: 0.5); - await tester.pump(); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 20) - .itemLeadingEdge, - 0.5); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 20) - .itemTrailingEdge, - 0.5 + itemHeight / screenHeight); - }); - - testWidgets('List positioned with 20 half off top of screen', - (WidgetTester tester) async { - await setUpWidgetTest(tester, - topItem: 20, anchor: -(itemHeight / screenHeight) / 2); - await tester.pump(); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 20) - .itemLeadingEdge, - -(itemHeight / screenHeight) / 2); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 20) - .itemTrailingEdge, - (itemHeight / screenHeight) / 2); - }); - - testWidgets('List positioned with 5 at top then scroll up 2', - (WidgetTester tester) async { - await setUpWidgetTest(tester, topItem: 5); - - await tester.drag( - find.byType(PositionedList), const Offset(0, itemHeight * 2)); - await tester.pump(); - - expect(find.text('Item 2'), findsNothing); - expect(find.text('Item 3'), findsOneWidget); - expect(find.text('Item 12'), findsOneWidget); - expect(find.text('Item 13'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 2) - .itemLeadingEdge, - -1 / 10); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 3) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 12) - .itemTrailingEdge, - 1); - }); - - testWidgets('List positioned with 5 at top then scroll down 1/2', - (WidgetTester tester) async { - await setUpWidgetTest(tester, topItem: 5); - - await tester.drag( - find.byType(PositionedList), const Offset(0, -1 / 2 * itemHeight)); - await tester.pump(); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 5) - .itemTrailingEdge, - 1 / 20); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 14) - .itemLeadingEdge, - 17 / 20); - }); - - testWidgets('List positioned with 0 at top scroll up 5', - (WidgetTester tester) async { - final scrollController = ScrollController(); - await setUpWidgetTest(tester, scrollController: scrollController); - await tester.pump(); - - scrollController.jumpTo(itemHeight * 5); - await tester.pump(); - await tester.pumpAndSettle(); - - expect(find.text('Item 4'), findsNothing); - expect(find.text('Item 5'), findsOneWidget); - expect(find.text('Item 14'), findsOneWidget); - expect(find.text('Item 15'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 5) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 4) - .itemLeadingEdge, - -1 / 10); - }); - - testWidgets('List positioned with 5 at top then scroll up 2 programatically', - (WidgetTester tester) async { - final scrollController = ScrollController(); - await setUpWidgetTest(tester, - topItem: 5, scrollController: scrollController); - - scrollController.jumpTo(-2 * itemHeight); - await tester.pump(); - - expect(find.text('Item 2'), findsNothing); - expect(find.text('Item 3'), findsOneWidget); - expect(find.text('Item 12'), findsOneWidget); - expect(find.text('Item 13'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 2) - .itemLeadingEdge, - -1 / 10); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 3) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 12) - .itemTrailingEdge, - 1); - }); - - testWidgets( - 'List positioned with 5 at top then scroll down 20 programatically', - (WidgetTester tester) async { - final scrollController = ScrollController(); - await setUpWidgetTest(tester, - topItem: 5, scrollController: scrollController); - - scrollController.jumpTo(itemHeight * 20); - await tester.pump(); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 23) - .itemLeadingEdge, - -2 / 10); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 24) - .itemLeadingEdge, - -1 / 10); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 25) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 4) - .itemLeadingEdge, - -21 / 10); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 5) - .itemLeadingEdge, - -20 / 10); - }); - - testWidgets('List positioned with 5 at top and initial scroll offset', - (WidgetTester tester) async { - final scrollController = - ScrollController(initialScrollOffset: -2 * itemHeight); - await setUpWidgetTest(tester, - topItem: 5, scrollController: scrollController); - - expect(find.text('Item 2'), findsNothing); - expect(find.text('Item 3'), findsOneWidget); - expect(find.text('Item 12'), findsOneWidget); - expect(find.text('Item 13'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 3) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 12) - .itemTrailingEdge, - 1); - }); - - testWidgets('Does not crash when updated offscreen', - (WidgetTester tester) async { - late StateSetter setState; - var updated = false; - - // There's 0 relayout boundaries in this subtree. - final widget = StatefulBuilder(builder: (context, stateSetter) { - setState = stateSetter; - return Positioned( - left: 0, - right: 0, - child: PositionedList( - shrinkWrap: true, - itemCount: 1, - // When `updated` becomes true this line inserts a - // RenderIndexedSemantics to the render tree. - addSemanticIndexes: updated, - itemBuilder: (context, index) => const SizedBox(height: itemHeight), - )); - }); - - await tester.pumpWidget(Directionality( - textDirection: TextDirection.ltr, - child: Overlay( - initialEntries: [ - OverlayEntry(builder: (context) => widget, maintainState: true), - ], - ), - )); - - // Insert a new opaque OverlayEntry that would prevent the first - // OverlayEntry from doing re-layout. Since there's no relayout boundaries - // in the first OverlayEntry, no dirty RenderObjects in its render subtree - // can update layout. - final newOverlay = OverlayEntry( - builder: (context) => const SizedBox.expand(), - opaque: true, - ); - tester.state(find.byType(Overlay)).insert(newOverlay); - await tester.pump(); - - // Update the list item's render tree. A new RenderObjectElement is - // inflated, registeredElement.renderObject will point to this new - // RenderObjectElement's RenderObject (RenderIndexedSemantics), which has - // never been laid out. - setState(() { - updated = true; - }); - - await tester.pump(); - expect(tester.takeException(), isNull); - }); -} diff --git a/packages/stream_chat_flutter/test/scrollable_positioned_list/reversed_positioned_list_test.dart b/packages/stream_chat_flutter/test/scrollable_positioned_list/reversed_positioned_list_test.dart deleted file mode 100644 index 828f17e390..0000000000 --- a/packages/stream_chat_flutter/test/scrollable_positioned_list/reversed_positioned_list_test.dart +++ /dev/null @@ -1,269 +0,0 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/item_positions_notifier.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/positioned_list.dart'; - -const screenHeight = 400.0; -const screenWidth = 400.0; -const itemHeight = screenHeight / 10.0; -const defaultItemCount = 500; - -void main() { - final itemPositionsNotifier = ItemPositionsListener.create(); - - Future setUpWidgetTest( - WidgetTester tester, { - int topItem = 0, - ScrollController? scrollController, - double anchor = 0, - int itemCount = defaultItemCount, - }) async { - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - await tester.pumpWidget( - MaterialApp( - home: PositionedList( - itemCount: itemCount, - positionedIndex: topItem, - alignment: anchor, - controller: scrollController, - itemBuilder: (context, index) => SizedBox( - height: itemHeight, - child: Text('Item $index'), - ), - itemPositionsNotifier: itemPositionsNotifier as ItemPositionsNotifier, - reverse: true, - ), - ), - ); - } - - testWidgets('short list', (WidgetTester tester) async { - await setUpWidgetTest(tester, itemCount: 5); - await tester.pump(); - - expect(tester.getBottomRight(find.text('Item 0')).dy, screenHeight); - expect(find.text('Item 4'), findsOneWidget); - expect(find.text('Item 5'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 4) - .itemTrailingEdge, - 1 / 2); - }); - - testWidgets('List positioned with 0 at bottom', (WidgetTester tester) async { - await setUpWidgetTest(tester); - await tester.pump(); - - expect(tester.getBottomRight(find.text('Item 0')).dy, screenHeight); - expect(tester.getTopLeft(find.text('Item 9')).dy, 0); - expect(find.text('Item 10'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemTrailingEdge, - 1); - }); - - testWidgets('List positioned with 5 at bottom', (WidgetTester tester) async { - await setUpWidgetTest(tester, topItem: 5); - await tester.pump(); - - expect(find.text('Item 4'), findsNothing); - expect(find.text('Item 5'), findsOneWidget); - expect(find.text('Item 14'), findsOneWidget); - expect(find.text('Item 15'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 4) - .itemLeadingEdge, - -1 / 10); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 4) - .itemTrailingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 5) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 14) - .itemTrailingEdge, - 1); - }); - - testWidgets('List positioned with 15 at bottom', (WidgetTester tester) async { - await setUpWidgetTest(tester, topItem: 15); - await tester.pump(); - - expect(find.text('Item 14'), findsNothing); - expect(find.text('Item 15'), findsOneWidget); - expect(find.text('Item 24'), findsOneWidget); - expect(find.text('Item 25'), findsNothing); - }); - - testWidgets('List positioned with 15 at top', (WidgetTester tester) async { - await setUpWidgetTest(tester, topItem: 15, anchor: 1); - await tester.pump(); - - expect(find.text('Item 15'), findsNothing); - expect(find.text('Item 14'), findsOneWidget); - expect(find.text('Item 5'), findsOneWidget); - expect(find.text('Item 4'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 15) - .itemLeadingEdge, - 1); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 14) - .itemTrailingEdge, - 1); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 14) - .itemLeadingEdge, - 9 / 10); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 5) - .itemLeadingEdge, - 0); - }); - - testWidgets('List positioned with 5 at bottom then scroll up 2', - (WidgetTester tester) async { - await setUpWidgetTest(tester, topItem: 5); - - await tester.drag( - find.byType(PositionedList), const Offset(0, itemHeight * 2)); - await tester.pump(); - - expect(find.text('Item 6'), findsNothing); - expect(find.text('Item 7'), findsOneWidget); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 7) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 7) - .itemTrailingEdge, - 1 / 10); - }); - - testWidgets('List positioned with 0 at bottom scroll to item 5', - (WidgetTester tester) async { - final scrollController = ScrollController(); - await setUpWidgetTest(tester, scrollController: scrollController); - await tester.pump(); - - scrollController.jumpTo(itemHeight * 5); - await tester.pump(); - await tester.pumpAndSettle(); - - expect(find.text('Item 4'), findsNothing); - expect(find.text('Item 5'), findsOneWidget); - expect(find.text('Item 14'), findsOneWidget); - expect(find.text('Item 15'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 5) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 4) - .itemLeadingEdge, - -1 / 10); - }); - - testWidgets( - 'List positioned with 5 at bottom then scroll up 2 programatically', - (WidgetTester tester) async { - final scrollController = ScrollController(); - await setUpWidgetTest(tester, - topItem: 5, scrollController: scrollController); - - scrollController.jumpTo(itemHeight * 2); - await tester.pump(); - - expect(find.text('Item 6'), findsNothing); - expect(find.text('Item 7'), findsOneWidget); - expect(find.text('Item 16'), findsOneWidget); - expect(find.text('Item 17'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 6) - .itemLeadingEdge, - -1 / 10); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 7) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 16) - .itemTrailingEdge, - 1); - }); - - testWidgets('List positioned with 5 at bottom and initial scroll offset', - (WidgetTester tester) async { - final scrollController = - ScrollController(initialScrollOffset: itemHeight * 2); - await setUpWidgetTest(tester, - topItem: 5, scrollController: scrollController); - - expect(find.text('Item 6'), findsNothing); - expect(find.text('Item 7'), findsOneWidget); - expect(find.text('Item 16'), findsOneWidget); - expect(find.text('Item 17'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 6) - .itemLeadingEdge, - -1 / 10); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 7) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 16) - .itemTrailingEdge, - 1); - }); -} diff --git a/packages/stream_chat_flutter/test/scrollable_positioned_list/reversed_scrollable_positioned_list_test.dart b/packages/stream_chat_flutter/test/scrollable_positioned_list/reversed_scrollable_positioned_list_test.dart deleted file mode 100644 index e2b16d8a4d..0000000000 --- a/packages/stream_chat_flutter/test/scrollable_positioned_list/reversed_scrollable_positioned_list_test.dart +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart'; - -const screenHeight = 400.0; -const screenWidth = 400.0; -const itemHeight = screenHeight / 10.0; -const itemCount = 500; -const scrollDuration = Duration(seconds: 1); - -void main() { - Future setUpWidgetTest( - WidgetTester tester, { - ItemScrollController? itemScrollController, - ItemPositionsListener? itemPositionsListener, - EdgeInsets? padding, - int initialIndex = 0, - }) async { - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - await tester.pumpWidget( - MaterialApp( - home: ScrollablePositionedList.builder( - itemCount: itemCount, - initialScrollIndex: initialIndex, - itemScrollController: itemScrollController, - itemBuilder: (context, index) => SizedBox( - height: itemHeight, - child: Text('Item $index'), - ), - itemPositionsListener: itemPositionsListener, - reverse: true, - padding: padding, - ), - ), - ); - } - - testWidgets('List positioned with 0 at bottom', (WidgetTester tester) async { - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, itemPositionsListener: itemPositionsListener); - - expect(tester.getBottomRight(find.text('Item 0')).dy, screenHeight); - expect(tester.getTopLeft(find.text('Item 9')).dy, 0); - expect(find.text('Item 10'), findsNothing); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemTrailingEdge, - 1); - }); - - testWidgets('Scroll to 1 then 2 (both already on screen)', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - unawaited( - itemScrollController.scrollTo(index: 1, duration: scrollDuration)); - await tester.pump(); - await tester.pump(scrollDuration); - expect(find.text('Item 0'), findsNothing); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 1) - .itemLeadingEdge, - 0); - expect(tester.getBottomRight(find.text('Item 1')).dy, screenHeight); - - unawaited( - itemScrollController.scrollTo(index: 2, duration: scrollDuration)); - await tester.pump(); - await tester.pump(scrollDuration); - - expect(find.text('Item 1'), findsNothing); - expect(tester.getBottomRight(find.text('Item 2')).dy, screenHeight); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 2) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 11) - .itemTrailingEdge, - 1); - }); - - testWidgets('Scroll to 5 (already on screen) and then back to 0', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - unawaited( - itemScrollController.scrollTo(index: 5, duration: scrollDuration)); - await tester.pumpAndSettle(); - unawaited( - itemScrollController.scrollTo(index: 0, duration: scrollDuration)); - await tester.pumpAndSettle(); - - expect(find.text('Item 0'), findsOneWidget); - expect(find.text('Item 9'), findsOneWidget); - expect(find.text('Item 10'), findsNothing); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemTrailingEdge, - 1); - }); - - testWidgets('Scroll to 100 (not already on screen)', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pumpAndSettle(); - - expect(find.text('Item 99'), findsNothing); - expect(find.text('Item 100'), findsOneWidget); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 100) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 109) - .itemTrailingEdge, - 1); - }); - - testWidgets('Jump to 100', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - itemScrollController.jumpTo(index: 100); - await tester.pumpAndSettle(); - - expect(tester.getBottomRight(find.text('Item 100')).dy, screenHeight); - expect(tester.getTopLeft(find.text('Item 109')).dy, 0); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 100) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 109) - .itemTrailingEdge, - 1); - }); - - testWidgets('padding test - centered sliver at bottom', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - padding: const EdgeInsets.all(10), - ); - - expect(tester.getBottomLeft(find.text('Item 0')), - const Offset(10, screenHeight - 10)); - expect(tester.getBottomLeft(find.text('Item 1')), - const Offset(10, screenHeight - (itemHeight + 10))); - expect(tester.getTopRight(find.text('Item 1')), - const Offset(screenWidth - 10, screenHeight - (10 + itemHeight * 2))); - - unawaited( - itemScrollController.scrollTo(index: 490, duration: scrollDuration)); - await tester.pumpAndSettle(); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(0, 100)); - await tester.pumpAndSettle(); - - expect(tester.getTopLeft(find.text('Item 499')), const Offset(10, 10)); - }); - - testWidgets('padding test - centered sliver not at bottom', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - initialIndex: 2, - padding: const EdgeInsets.all(10), - ); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(0, -200)); - await tester.pumpAndSettle(); - - expect(tester.getBottomLeft(find.text('Item 0')), - const Offset(10, screenHeight - 10)); - expect(tester.getBottomLeft(find.text('Item 2')), - const Offset(10, screenHeight - (10 + itemHeight * 2))); - expect(tester.getBottomLeft(find.text('Item 3')), - const Offset(10, screenHeight - (10 + itemHeight * 3))); - }); -} diff --git a/packages/stream_chat_flutter/test/scrollable_positioned_list/scrollable_positioned_list_test.dart b/packages/stream_chat_flutter/test/scrollable_positioned_list/scrollable_positioned_list_test.dart deleted file mode 100644 index d3c19b2f0f..0000000000 --- a/packages/stream_chat_flutter/test/scrollable_positioned_list/scrollable_positioned_list_test.dart +++ /dev/null @@ -1,2273 +0,0 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/scroll_view.dart'; - -const screenHeight = 400.0; -const screenWidth = 400.0; -const itemHeight = screenHeight / 10.0; -const defaultItemCount = 500; -const scrollDuration = Duration(seconds: 1); -const scrollDurationTolerance = Duration(milliseconds: 1); -const tolerance = 1e-3; - -void main() { - Future setUpWidgetTest( - WidgetTester tester, { - Key? key, - ItemScrollController? itemScrollController, - ItemPositionsListener? itemPositionsListener, - int initialIndex = 0, - double initialAlignment = 0.0, - int itemCount = defaultItemCount, - ScrollPhysics? physics, - bool addSemanticIndexes = true, - int? semanticChildCount, - EdgeInsets? padding, - bool addRepaintBoundaries = true, - bool addAutomaticKeepAlives = true, - double? minCacheExtent, - bool variableHeight = false, - }) async { - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - await tester.pumpWidget( - MaterialApp( - home: ScrollablePositionedList.builder( - key: key, - itemCount: itemCount, - itemScrollController: itemScrollController, - itemBuilder: (context, index) { - assert( - index >= 0 && index <= itemCount - 1, - 'index must be in the range of 0 to itemCount - 1', - ); - return SizedBox( - height: - variableHeight ? (itemHeight + (index % 13) * 5) : itemHeight, - child: Text('Item $index'), - ); - }, - itemPositionsListener: itemPositionsListener, - initialScrollIndex: initialIndex, - initialAlignment: initialAlignment, - physics: physics, - addSemanticIndexes: addSemanticIndexes, - semanticChildCount: semanticChildCount, - padding: padding, - addAutomaticKeepAlives: addAutomaticKeepAlives, - addRepaintBoundaries: addRepaintBoundaries, - minCacheExtent: minCacheExtent, - ), - ), - ); - } - - final fadeTransitionFinder = find.descendant( - of: find.byType(ScrollablePositionedList), - matching: find.byType(FadeTransition), - ); - - testWidgets('List positioned with 0 at top', (WidgetTester tester) async { - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, itemPositionsListener: itemPositionsListener); - - expect(find.text('Item 0'), findsOneWidget); - expect(find.text('Item 9'), findsOneWidget); - expect(find.text('Item 10'), findsNothing); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemTrailingEdge, - 1); - expect( - itemPositionsListener.itemPositions.value - .where((position) => position.index == 10), - isEmpty); - }); - - testWidgets('List positioned with 0 at top - use default values', - (WidgetTester tester) async { - final itemPositionsListener = ItemPositionsListener.create(); - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - await tester.pumpWidget( - MaterialApp( - home: ScrollablePositionedList.builder( - itemCount: defaultItemCount, - itemBuilder: (context, index) => SizedBox( - height: itemHeight, - child: Text('Item $index'), - ), - itemPositionsListener: itemPositionsListener, - ), - ), - ); - - expect(find.text('Item 0'), findsOneWidget); - expect(find.text('Item 9'), findsOneWidget); - expect(find.text('Item 10'), findsNothing); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemTrailingEdge, - 1); - }); - - testWidgets('List positioned with 5 at top', (WidgetTester tester) async { - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemPositionsListener: itemPositionsListener, initialIndex: 5); - - expect(find.text('Item 4'), findsNothing); - expect(find.text('Item 5'), findsOneWidget); - - expect( - itemPositionsListener.itemPositions.value - .where((position) => position.index == 4), - isEmpty); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 5) - .itemLeadingEdge, - 0); - }); - - testWidgets('List positioned with 9 at middle', (WidgetTester tester) async { - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemPositionsListener: itemPositionsListener, - initialIndex: 9, - initialAlignment: 0.5); - - expect(tester.getTopLeft(find.text('Item 9')).dy, screenHeight / 2); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemLeadingEdge, - 0.5); - }); - - testWidgets('List positioned with 9 half way off top', - (WidgetTester tester) async { - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemPositionsListener: itemPositionsListener, - initialIndex: 9, - initialAlignment: -(itemHeight / screenHeight) / 2); - - expect(tester.getTopLeft(find.text('Item 9')).dy, -itemHeight / 2); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemLeadingEdge, - -(itemHeight / screenHeight) / 2); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemTrailingEdge, - (itemHeight / screenHeight) / 2); - }); - - testWidgets('Scroll to 9 half way off top', (WidgetTester tester) async { - final itemPositionsListener = ItemPositionsListener.create(); - final itemScrollController = ItemScrollController(); - expect(itemScrollController.isAttached, false); - await setUpWidgetTest(tester, - itemPositionsListener: itemPositionsListener, - itemScrollController: itemScrollController); - expect(itemScrollController.isAttached, true); - - unawaited(itemScrollController.scrollTo( - index: 9, - duration: scrollDuration, - alignment: -(itemHeight / screenHeight) / 2)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration + scrollDurationTolerance); - - expect(tester.getTopLeft(find.text('Item 9')).dy, -itemHeight / 2); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemLeadingEdge, - -(itemHeight / screenHeight) / 2); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemTrailingEdge, - (itemHeight / screenHeight) / 2); - }); - - testWidgets('Jump to 9 half way off top', (WidgetTester tester) async { - final itemPositionsListener = ItemPositionsListener.create(); - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, - itemPositionsListener: itemPositionsListener, - itemScrollController: itemScrollController); - - itemScrollController.jumpTo( - index: 9, alignment: -(itemHeight / screenHeight) / 2); - await tester.pump(); - - expect(tester.getTopLeft(find.text('Item 9')).dy, -itemHeight / 2); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemLeadingEdge, - -(itemHeight / screenHeight) / 2); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemTrailingEdge, - (itemHeight / screenHeight) / 2); - }); - - testWidgets('List positioned with 9 at middle scroll to 15 at bottom', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener, - initialIndex: 9, - initialAlignment: 0.5); - - unawaited(itemScrollController.scrollTo( - index: 16, duration: scrollDuration, alignment: 1)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration + scrollDurationTolerance); - - expect(tester.getBottomRight(find.text('Item 15')).dy, screenHeight); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 15) - .itemTrailingEdge, - 1.0); - }); - - testWidgets('Scroll to 1 (already on screen)', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - unawaited( - itemScrollController.scrollTo(index: 1, duration: scrollDuration)); - await tester.pump(); - - await tester.pump(scrollDuration ~/ 2); - expect(find.text('Item 0'), findsOneWidget); - expect(find.text('Item 1'), findsOneWidget); - expect(find.text('Item 10'), findsOneWidget); - - await tester.pump(scrollDuration ~/ 2); - expect(find.text('Item 0'), findsNothing); - expect(find.text('Item 1'), findsOneWidget); - expect(find.text('Item 10'), findsOneWidget); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 1) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 10) - .itemTrailingEdge, - 1); - expect( - itemPositionsListener.itemPositions.value - .where((position) => position.index == 11), - isEmpty); - }); - - testWidgets('Scroll to 1 then 2 (both already on screen)', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - unawaited( - itemScrollController.scrollTo(index: 1, duration: scrollDuration)); - await tester.pump(); - await tester.pump(scrollDuration); - expect(find.text('Item 0'), findsNothing); - expect(find.text('Item 1'), findsOneWidget); - - unawaited( - itemScrollController.scrollTo(index: 2, duration: scrollDuration)); - await tester.pump(); - await tester.pump(scrollDuration); - - expect(find.text('Item 1'), findsNothing); - expect(find.text('Item 2'), findsOneWidget); - - expect( - itemPositionsListener.itemPositions.value - .where((position) => position.index == 1), - isEmpty); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 2) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 11) - .itemTrailingEdge, - 1); - }); - - testWidgets('Scroll to 5 (already on screen) and then back to 0', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - unawaited( - itemScrollController.scrollTo(index: 5, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration + scrollDurationTolerance); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 5) - .itemLeadingEdge, - 0); - - unawaited( - itemScrollController.scrollTo(index: 0, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration + scrollDurationTolerance); - - expect(find.text('Item 0'), findsOneWidget); - expect(find.text('Item 9'), findsOneWidget); - expect(find.text('Item 10'), findsNothing); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemTrailingEdge, - 1); - }); - - testWidgets('Scroll to 20 without fading', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - var fadeTransition = tester.widget(fadeTransitionFinder); - final initialOpacity = fadeTransition.opacity; - - unawaited( - itemScrollController.scrollTo(index: 20, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration ~/ 2); - - fadeTransition = tester.widget(fadeTransitionFinder); - expect(fadeTransition.opacity, initialOpacity); - - await tester.pumpAndSettle(); - - expect(find.text('Item 14'), findsNothing); - expect(find.text('Item 20'), findsOneWidget); - }); - - testWidgets('Scroll to 100 (not already on screen)', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration + scrollDurationTolerance); - - expect(find.text('Item 99'), findsNothing); - expect(find.text('Item 100'), findsOneWidget); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 100) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 109) - .itemTrailingEdge, - 1); - - await tester.pumpAndSettle(); - }); - - testWidgets('Scroll to 100 (not already on screen) front scroll view', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - expect(fadeTransitionFinder.evaluate().length, 2); - expect( - tester.widget(fadeTransitionFinder.last).opacity.value, - closeTo(0, 0.01), - ); - - await tester.pump(scrollDuration ~/ 2); - - expect(tester.getTopLeft(find.text('Item 10')).dy, 0); - expect(tester.getBottomLeft(find.text('Item 19')).dy, screenHeight); - expect(fadeTransitionFinder.evaluate().length, 2); - expect( - tester.widget(fadeTransitionFinder.last).opacity.value, - closeTo(0.5, 0.01), - ); - - await tester.pump(scrollDuration ~/ 2 - scrollDurationTolerance); - expect(fadeTransitionFinder.evaluate().length, 2); - expect( - tester.widget(fadeTransitionFinder.last).opacity.value, - closeTo(1, 0.01), - ); - - await tester.pumpAndSettle(); - - expect(fadeTransitionFinder.evaluate().length, 1); - expect( - tester.widget(fadeTransitionFinder.last).opacity.value, - closeTo(1, 0.01), - ); - }); - - testWidgets('Scroll to 100 (not already on screen) back scroll view', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration ~/ 2); - - expect(tester.getBottomLeft(find.text('Item 99')).dy, screenHeight); - - await tester.pumpAndSettle(); - expect(find.text('Item 25', skipOffstage: false), findsNothing); - }); - - testWidgets('Scroll to 100 (not already on screen) then back to 0', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration + scrollDurationTolerance); - expect(find.text('Item 0'), findsNothing); - - unawaited( - itemScrollController.scrollTo(index: 0, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - expect( - tester.widget(fadeTransitionFinder.last).opacity.value, - closeTo(0, 0.01), - ); - await tester.pump(scrollDuration + scrollDurationTolerance); - expect( - tester.widget(fadeTransitionFinder.last).opacity.value, - closeTo(1, 0.01), - ); - - expect(find.text('Item 0'), findsOneWidget); - expect(tester.getTopLeft(find.text('Item 0')).dy, 0); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemTrailingEdge, - 1); - }); - - testWidgets('Scroll to 100 then back to 0 back scroll view', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pumpAndSettle(); - - unawaited( - itemScrollController.scrollTo(index: 0, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration ~/ 2); - - expect(tester.getTopLeft(find.text('Item 90')).dy, 0); - expect(tester.getBottomLeft(find.text('Item 99')).dy, screenHeight); - - await tester.pumpAndSettle(); - }); - - testWidgets('Scroll to 100 then back to 0 front scroll view', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration + scrollDurationTolerance); - - unawaited( - itemScrollController.scrollTo(index: 0, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration ~/ 2); - - expect(tester.getTopLeft(find.text('Item 10')).dy, 0); - expect(tester.getBottomLeft(find.text('Item 19')).dy, screenHeight); - expect( - tester.widget(fadeTransitionFinder.last).opacity.value, - closeTo(0.5, 0.01), - ); - - await tester.pumpAndSettle(); - }); - - testWidgets('Scroll 100-0-100', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration + scrollDurationTolerance); - - unawaited( - itemScrollController.scrollTo(index: 0, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration + scrollDurationTolerance); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration ~/ 2); - - expect(tester.getTopLeft(find.text('Item 10')).dy, 0); - expect(tester.getBottomLeft(find.text('Item 19')).dy, screenHeight); - - await tester.pumpAndSettle(); - }); - - testWidgets('Jump to 100', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - itemScrollController.jumpTo(index: 100); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration + scrollDurationTolerance); - - expect(tester.getTopLeft(find.text('Item 100')).dy, 0); - expect(tester.getBottomLeft(find.text('Item 109')).dy, screenHeight); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 100) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 109) - .itemTrailingEdge, - 1); - }); - - testWidgets('Jump to 100 and position at bottom', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - itemScrollController.jumpTo(index: 100, alignment: 1); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration + scrollDurationTolerance); - - expect(tester.getBottomLeft(find.text('Item 99')).dy, screenHeight); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 99) - .itemTrailingEdge, - 1.0); - expect( - itemPositionsListener.itemPositions.value - .where((position) => position.index == 100), - isEmpty); - }); - - testWidgets('Jump to 100 and position at middle', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - itemScrollController.jumpTo(index: 100, alignment: 0.5); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration + scrollDurationTolerance); - - expect(tester.getTopLeft(find.text('Item 100')).dy, screenHeight / 2); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 100) - .itemLeadingEdge, - 0.5); - }); - - testWidgets('Manually scroll a significant distance, jump to 100', - (WidgetTester tester) async { - // Test for https://github.com/google/flutter.widgets/issues/144. - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener, - variableHeight: true); - - final listFinder = find.byType(ScrollablePositionedList); - for (var i = 0; i < 5; i += 1) { - await tester.drag(listFinder, const Offset(0, -screenHeight)); - await tester.pumpAndSettle(); - } - - itemScrollController.jumpTo(index: 100); - await tester.pumpAndSettle(); - - expect(tester.getTopLeft(find.text('Item 100')).dy, 0); - }, skip: true); - - testWidgets('Scroll to 100 and position at bottom', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - unawaited(itemScrollController.scrollTo( - index: 100, alignment: 1, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration + scrollDurationTolerance); - - expect(tester.getBottomLeft(find.text('Item 99')).dy, screenHeight); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 99) - .itemTrailingEdge, - 1.0); - expect( - itemPositionsListener.itemPositions.value - .where((position) => position.index == 100), - isEmpty); - }); - - testWidgets('Scroll to 100 and position at middle', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - unawaited(itemScrollController.scrollTo( - index: 100, alignment: 0.5, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration + scrollDurationTolerance); - - expect(tester.getTopLeft(find.text('Item 100')).dy, screenHeight / 2); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 100) - .itemLeadingEdge, - 0.5); - }); - - testWidgets('Scroll to 9 and position at middle', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - unawaited(itemScrollController.scrollTo( - index: 9, alignment: 0.5, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration + scrollDurationTolerance); - - expect(tester.getTopLeft(find.text('Item 9')).dy, screenHeight / 2); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemLeadingEdge, - 0.5); - }); - - testWidgets('Scroll up a little then jump to 100', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(0, -10)); - await tester.pumpAndSettle(); - - itemScrollController.jumpTo(index: 100); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration + scrollDurationTolerance); - - expect(tester.getTopLeft(find.text('Item 100')).dy, 0); - expect(tester.getBottomLeft(find.text('Item 109')).dy, screenHeight); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 100) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 109) - .itemTrailingEdge, - 1); - }); - - testWidgets('Scroll to 100 Jump to 0 Scroll to 100', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration + scrollDurationTolerance); - - itemScrollController.jumpTo(index: 0); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration + scrollDurationTolerance); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration ~/ 2); - - expect(tester.getTopLeft(find.text('Item 10')).dy, 0); - expect(tester.getBottomLeft(find.text('Item 19')).dy, screenHeight); - - await tester.pumpAndSettle(); - - expect(tester.getTopLeft(find.text('Item 100')).dy, 0); - expect(tester.getBottomLeft(find.text('Item 109')).dy, screenHeight); - }); - - testWidgets('Scroll to 100 stop before half way', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration ~/ 2 - scrollDuration ~/ 20); - - await tester.tap(find.byType(ScrollablePositionedList)); - await tester.pump(); - - expect(tester.getTopLeft(find.text('Item 9')).dy, 0); - final fadeTransition = tester.widget(fadeTransitionFinder); - expect(fadeTransition.opacity.value, 1.0); - - await tester.pumpAndSettle(); - }); - - testWidgets('Scroll to 100 stop half way', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration ~/ 2); - - await tester.tap(find.byType(ScrollablePositionedList)); - await tester.pump(); - - expect(tester.getTopLeft(find.text('Item 10')).dy, 0); - final fadeTransition = tester.widget(fadeTransitionFinder); - expect(fadeTransition.opacity.value, 1.0); - - await tester.pumpAndSettle(); - }); - - testWidgets('Scroll to 0 stop before half way', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pumpAndSettle(); - - unawaited( - itemScrollController.scrollTo(index: 0, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration ~/ 2 - scrollDuration ~/ 20); - - await tester.tap(find.byType(ScrollablePositionedList)); - await tester.pump(); - - expect(tester.getTopLeft(find.text('Item 91')).dy, 0); - expect(fadeTransitionFinder, findsNWidgets(1)); - - await tester.pumpAndSettle(); - }); - - testWidgets('Scroll to 100 stop after half way', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration ~/ 2 + scrollDuration ~/ 20); - - expect(find.text('Item 9', skipOffstage: false), findsOneWidget); - expect(tester.getBottomLeft(find.text('Item 100')).dy, - closeTo(screenHeight, tolerance)); - - await tester.tap(find.byType(ScrollablePositionedList)); - await tester.pump(); - - expect(tester.getBottomLeft(find.text('Item 100')).dy, - closeTo(screenHeight, tolerance)); - expect(find.text('Item 9', skipOffstage: false), findsNothing); - expect(fadeTransitionFinder, findsNWidgets(1)); - - await tester.pumpAndSettle(); - }); - - testWidgets('Scroll to 0 stop after half way', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pumpAndSettle(); - - unawaited( - itemScrollController.scrollTo(index: 0, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration ~/ 2 + scrollDuration ~/ 20); - - await tester.tap(find.byType(ScrollablePositionedList)); - await tester.pump(); - - expect(tester.getTopLeft(find.text('Item 9')).dy, closeTo(0, tolerance)); - final fadeTransition = tester.widget(fadeTransitionFinder); - expect(fadeTransition.opacity.value, 1.0); - - await tester.pumpAndSettle(); - }); - - testWidgets('Scroll to 0 stop half way', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pumpAndSettle(); - - unawaited( - itemScrollController.scrollTo(index: 0, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration ~/ 2); - - await tester.tap(find.byType(ScrollablePositionedList)); - await tester.pump(); - - expect(tester.getTopLeft(find.text('Item 90')).dy, 0); - expect(fadeTransitionFinder, findsNWidgets(1)); - - await tester.pumpAndSettle(); - }); - - testWidgets('Scroll to 100 jump to 250 half way', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration ~/ 2); - - itemScrollController.jumpTo(index: 250); - await tester.pump(); - - expect(tester.getTopLeft(find.text('Item 250')).dy, 0); - - expect(find.text('Item 100'), findsNothing); - - await tester.pumpAndSettle(); - }); - - testWidgets('Scroll to 250, scroll to 100, jump to 0 half way', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - unawaited( - itemScrollController.scrollTo(index: 250, duration: scrollDuration)); - await tester.pumpAndSettle(); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration ~/ 2); - - itemScrollController.jumpTo(index: 0); - await tester.pump(); - - expect(tester.getTopLeft(find.text('Item 0')).dy, 0); - expect(find.text('Item 100'), findsNothing); - - await tester.pumpAndSettle(); - }); - - testWidgets('Scroll to 100 scroll to 250 half way', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration ~/ 2); - - unawaited( - itemScrollController.scrollTo(index: 250, duration: scrollDuration)); - - await tester.pumpAndSettle(); - expect(tester.getTopLeft(find.text('Item 250')).dy, 0); - expect(find.text('Item 100'), findsNothing); - }); - - testWidgets("Second scroll future doesn't complete until scroll is done", - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration ~/ 2); - - final scrollFuture2 = - itemScrollController.scrollTo(index: 250, duration: scrollDuration); - - var futureComplete = false; - unawaited(scrollFuture2.then((_) => futureComplete = true)); - - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration ~/ 2); - - expect(futureComplete, isFalse); - - await tester.pumpAndSettle(); - - expect(futureComplete, isTrue); - }); - - testWidgets('Scroll to 250, scroll to 100, scroll to 0 half way', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - unawaited( - itemScrollController.scrollTo(index: 250, duration: scrollDuration)); - await tester.pumpAndSettle(); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration ~/ 2); - - unawaited( - itemScrollController.scrollTo(index: 0, duration: scrollDuration)); - await tester.pumpAndSettle(); - - expect(tester.getTopLeft(find.text('Item 0')).dy, 0); - expect(find.text('Item 100'), findsNothing); - }); - - testWidgets( - 'Scroll to 100, scroll to 200, then scroll to 300 without waiting', - (WidgetTester tester) async { - // Possibly https://github.com/google/flutter.widgets/issues/171. - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - unawaited( - itemScrollController.scrollTo(index: 200, duration: scrollDuration)); - unawaited( - itemScrollController.scrollTo(index: 300, duration: scrollDuration)); - await tester.pumpAndSettle(); - - expect(find.text('Item 100'), findsNothing); - expect(find.text('Item 200'), findsNothing); - - final itemFinder = find.text('Item 300'); - expect(itemFinder, findsOneWidget); - expect(tester.getTopLeft(itemFinder).dy, 0); - }, skip: true); - - testWidgets( - 'Jump to 400 at bottom, manually scroll, scroll to 100 at bottom and back', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - itemScrollController.jumpTo(index: 400, alignment: 1); - await tester.pumpAndSettle(); - - final listFinder = find.byType(ScrollablePositionedList); - - await tester.drag(listFinder, const Offset(0, -screenHeight)); - await tester.pumpAndSettle(); - - unawaited(itemScrollController.scrollTo( - index: 100, alignment: 1, duration: scrollDuration)); - await tester.pumpAndSettle(); - - unawaited(itemScrollController.scrollTo( - index: 400, alignment: 1, duration: scrollDuration)); - await tester.pumpAndSettle(); - - final itemFinder = find.text('Item 399'); - expect(itemFinder, findsOneWidget); - expect(tester.getBottomLeft(itemFinder).dy, screenHeight); - }, - ); - - testWidgets('physics', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - physics: const BouncingScrollPhysics()); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(0, 50)); - await tester.pump(const Duration(milliseconds: 200)); - - expect(tester.getTopLeft(find.text('Item 0')).dy, greaterThan(0)); - - await tester.pumpAndSettle(); - expect(tester.getTopLeft(find.text('Item 0')).dy, 0); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pumpAndSettle(); - itemScrollController.jumpTo(index: 0); - await tester.pumpAndSettle(); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(0, 50)); - await tester.pump(const Duration(milliseconds: 200)); - - expect(tester.getTopLeft(find.text('Item 0')).dy, greaterThan(0)); - - await tester.pumpAndSettle(); - expect(tester.getTopLeft(find.text('Item 0')).dy, 0); - }); - - testWidgets('correct index sematics', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, initialIndex: 5); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(0, itemHeight * 2)); - await tester.pumpAndSettle(); - - final indexSemantics3 = tester.widget(find.ancestor( - of: find.text('Item 3'), matching: find.byType(IndexedSemantics))); - expect(indexSemantics3.index, 3); - final indexSemantics4 = tester.widget(find.ancestor( - of: find.text('Item 4'), matching: find.byType(IndexedSemantics))); - expect(indexSemantics4.index, 4); - final indexSemantics5 = tester.widget(find.ancestor( - of: find.text('Item 5'), matching: find.byType(IndexedSemantics))); - expect(indexSemantics5.index, 5); - final indexSemantics6 = tester.widget(find.ancestor( - of: find.text('Item 6'), matching: find.byType(IndexedSemantics))); - expect(indexSemantics6.index, 6); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pumpAndSettle(); - itemScrollController.jumpTo(index: 0); - await tester.pumpAndSettle(); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(0, itemHeight * 2)); - await tester.pumpAndSettle(); - - final indexSemantics3b = tester.widget(find.ancestor( - of: find.text('Item 3'), matching: find.byType(IndexedSemantics))); - expect(indexSemantics3b.index, 3); - final indexSemantics4b = tester.widget(find.ancestor( - of: find.text('Item 4'), matching: find.byType(IndexedSemantics))); - expect(indexSemantics4b.index, 4); - final indexSemantics5b = tester.widget(find.ancestor( - of: find.text('Item 5'), matching: find.byType(IndexedSemantics))); - expect(indexSemantics5b.index, 5); - final indexSemantics6b = tester.widget(find.ancestor( - of: find.text('Item 6'), matching: find.byType(IndexedSemantics))); - expect(indexSemantics6b.index, 6); - }); - - testWidgets('addIndexSemantics = false', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - initialIndex: 5, - addSemanticIndexes: false, - ); - - expect(find.byType(IndexedSemantics), findsNothing); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pumpAndSettle(); - itemScrollController.jumpTo(index: 0); - await tester.pumpAndSettle(); - - expect(find.byType(IndexedSemantics), findsNothing); - }); - - testWidgets('semanticChildCount specified', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - - await setUpWidgetTest( - tester, - semanticChildCount: 30, - itemScrollController: itemScrollController, - ); - - final customScrollView = - tester.widget(find.byType(UnboundedCustomScrollView)); - expect(customScrollView.semanticChildCount, 30); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pumpAndSettle(); - - final customScrollView2 = - tester.widget(find.byType(UnboundedCustomScrollView)); - expect(customScrollView2.semanticChildCount, 30); - }); - - testWidgets('semanticChildCount not specified', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - ); - - final customScrollView = - tester.widget(find.byType(UnboundedCustomScrollView)); - expect(customScrollView.semanticChildCount, defaultItemCount); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pumpAndSettle(); - - final customScrollView2 = - tester.widget(find.byType(UnboundedCustomScrollView)); - expect(customScrollView2.semanticChildCount, defaultItemCount); - }); - - // TODO: Fix pipeline owner and add back - // testWidgets('Semantic tree contains items in cache', - // (WidgetTester tester) async { - // final itemPositionsListener = ItemPositionsListener.create(); - //await setUpWidgetTest(tester, itemPositionsListener: itemPositionsListener); - // - // final root = WidgetsBinding - // .instance.pipelineOwner.semanticsOwner!.rootSemanticsNode!; - // - // final semanticNodes = [root]; - // - // collectSemanticNodes(root, semanticNodes); - // - // expect(semanticNodes.where((element) => element.label == 'Item 10'), - // isNotEmpty); - // }); - - testWidgets('padding test - centered at top', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - padding: const EdgeInsets.all(10), - ); - - expect(tester.getTopLeft(find.text('Item 0')), const Offset(10, 10)); - expect(tester.getTopLeft(find.text('Item 1')), - const Offset(10, itemHeight + 10)); - expect(tester.getTopRight(find.text('Item 1')), - const Offset(screenWidth - 10, itemHeight + 10)); - - unawaited( - itemScrollController.scrollTo(index: 490, duration: scrollDuration)); - await tester.pumpAndSettle(); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(0, -100)); - await tester.pumpAndSettle(); - - expect(tester.getBottomRight(find.text('Item 499')), - const Offset(screenWidth - 10, screenHeight - 10)); - }); - - testWidgets('padding test - centered not at top', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - initialIndex: 2, - padding: const EdgeInsets.all(10), - ); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(0, 200)); - await tester.pumpAndSettle(); - - expect(tester.getTopLeft(find.text('Item 0')), const Offset(10, 10)); - expect(tester.getTopLeft(find.text('Item 2')), - const Offset(10, 10 + itemHeight * 2)); - expect(tester.getTopLeft(find.text('Item 3')), - const Offset(10, 10 + itemHeight * 3)); - }); - - testWidgets('padding - first element centered - scroll up', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - padding: const EdgeInsets.all(10), - ); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(0, 100)); - await tester.pumpAndSettle(); - - expect(tester.getTopLeft(find.text('Item 0')), const Offset(10, 10)); - }); - - testWidgets('padding - last element centered - scroll down', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - padding: const EdgeInsets.all(10), - ); - - unawaited(itemScrollController.scrollTo( - index: defaultItemCount - 1, duration: scrollDuration)); - await tester.pumpAndSettle(); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(0, -100)); - await tester.pumpAndSettle(); - - expect( - tester.getBottomLeft(find.text('Item ${defaultItemCount - 1}')), - const Offset(10, screenHeight - 10), - ); - }); - - testWidgets('no repaint boundaries', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - initialIndex: 2, - padding: const EdgeInsets.all(10), - addRepaintBoundaries: false, - ); - - expect( - tester - .widgetList(find.descendant( - of: find.byType(ScrollablePositionedList), - matching: find.byType(RepaintBoundary))) - .length, - lessThan(5)); - }); - - testWidgets('no automatic keep alives', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - initialIndex: 2, - padding: const EdgeInsets.all(10), - addAutomaticKeepAlives: false, - ); - - expect( - find.descendant( - of: find.byType(ScrollablePositionedList), - matching: find.byType(AutomaticKeepAlive)), - findsNothing); - }); - - testWidgets('Jump to end of list', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - itemScrollController.jumpTo(index: defaultItemCount - 1); - await tester.pumpAndSettle(); - - expect(tester.getBottomLeft(find.text('Item ${defaultItemCount - 1}')).dy, - screenHeight); - }); - - testWidgets('Scroll to end of list', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - unawaited(itemScrollController.scrollTo( - index: defaultItemCount - 1, duration: scrollDuration)); - await tester.pumpAndSettle(); - - expect(tester.getBottomLeft(find.text('Item ${defaultItemCount - 1}')).dy, - screenHeight); - }); - - testWidgets('Scroll to end of list, jump to beginning, jump to end', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - unawaited(itemScrollController.scrollTo( - index: defaultItemCount - 1, duration: scrollDuration)); - await tester.pumpAndSettle(); - itemScrollController.jumpTo(index: 0); - await tester.pumpAndSettle(); - itemScrollController.jumpTo(index: defaultItemCount - 1); - await tester.pumpAndSettle(); - - expect(tester.getBottomLeft(find.text('Item ${defaultItemCount - 1}')).dy, - screenHeight); - }); - - testWidgets('Jump to end of list, scroll to beginning, scroll to end', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - itemScrollController.jumpTo(index: defaultItemCount - 1); - await tester.pumpAndSettle(); - - unawaited( - itemScrollController.scrollTo(index: 0, duration: scrollDuration)); - await tester.pumpAndSettle(); - unawaited(itemScrollController.scrollTo( - index: defaultItemCount - 1, duration: scrollDuration)); - await tester.pumpAndSettle(); - - expect(tester.getBottomLeft(find.text('Item ${defaultItemCount - 1}')).dy, - screenHeight); - }); - - testWidgets( - 'Jump to end of list, jump to beginning with alignment not at top', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, itemScrollController: itemScrollController); - - itemScrollController.jumpTo(index: defaultItemCount - 1); - await tester.pumpAndSettle(); - - itemScrollController.jumpTo(index: 0, alignment: 0.3); - await tester.pumpAndSettle(); - - expect(tester.getTopLeft(find.text('Item 0')).dy, 0); - }); - - testWidgets("Short list, can't scroll past end", (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, itemCount: 3); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(0, -10)); - await tester.pumpAndSettle(); - - expect(tester.getTopLeft(find.text('Item 0')).dy, 0); - }); - - testWidgets('List can be keyed', (WidgetTester tester) async { - const key = ValueKey('key'); - - await setUpWidgetTest(tester, key: key); - - expect(find.byKey(key), findsOneWidget); - }); - - testWidgets( - 'Maintain programmatic position (9 half way off top) in page view', - (WidgetTester tester) async { - final itemPositionsListener = ItemPositionsListener.create(); - final itemScrollController = ItemScrollController(); - - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - await tester.pumpWidget( - MaterialApp( - home: PageView( - children: [ - KeyedSubtree( - key: const PageStorageKey('key'), - child: ScrollablePositionedList.builder( - itemCount: defaultItemCount, - itemScrollController: itemScrollController, - itemBuilder: (context, index) => SizedBox( - height: itemHeight, - child: Text('Item $index'), - ), - itemPositionsListener: itemPositionsListener, - ), - ), - const Center( - child: Text('Test'), - ) - ], - ), - ), - ); - - itemScrollController.jumpTo( - index: 9, alignment: -(itemHeight / screenHeight) / 2); - await tester.pump(); - - await tester.drag(find.byType(PageView), const Offset(-500, 0)); - await tester.pumpAndSettle(); - - await tester.drag(find.byType(PageView), const Offset(500, 0)); - await tester.pumpAndSettle(); - - expect(tester.getTopLeft(find.text('Item 9')).dy, -itemHeight / 2); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemLeadingEdge, - -(itemHeight / screenHeight) / 2); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemTrailingEdge, - (itemHeight / screenHeight) / 2); - }); - - testWidgets('Maintain user scroll position (1 half way off top) in page view', - (WidgetTester tester) async { - final itemPositionsListener = ItemPositionsListener.create(); - final itemScrollController = ItemScrollController(); - - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - await tester.pumpWidget( - MaterialApp( - home: PageView( - children: [ - KeyedSubtree( - key: const PageStorageKey('key'), - child: ScrollablePositionedList.builder( - itemCount: defaultItemCount, - itemScrollController: itemScrollController, - itemBuilder: (context, index) => SizedBox( - height: itemHeight, - child: Text('Item $index'), - ), - itemPositionsListener: itemPositionsListener, - ), - ), - const Center( - child: Text('Test'), - ) - ], - ), - ), - ); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(0, -itemHeight)); - await tester.pumpAndSettle(); - - final item0Bottom = tester.getBottomRight(find.text('Item 0')).dy; - expect(item0Bottom, lessThan(itemHeight)); - - await tester.drag(find.byType(PageView), const Offset(-500, 0)); - await tester.pumpAndSettle(); - - await tester.drag(find.byType(PageView), const Offset(500, 0)); - await tester.pumpAndSettle(); - - expect(tester.getBottomRight(find.text('Item 0')).dy, item0Bottom); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - -(itemHeight / screenHeight) / 2); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemTrailingEdge, - (itemHeight / screenHeight) / 2); - }); - - testWidgets( - 'Maintain programmatic and user position (9 half way off top) in page view', - (WidgetTester tester) async { - final itemPositionsListener = ItemPositionsListener.create(); - final itemScrollController = ItemScrollController(); - - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - await tester.pumpWidget( - MaterialApp( - home: PageView( - children: [ - KeyedSubtree( - key: const PageStorageKey('key'), - child: ScrollablePositionedList.builder( - itemCount: defaultItemCount, - itemScrollController: itemScrollController, - itemBuilder: (context, index) => SizedBox( - height: itemHeight, - child: Text('Item $index'), - ), - itemPositionsListener: itemPositionsListener, - ), - ), - const Center( - child: Text('Test'), - ) - ], - ), - ), - ); - - itemScrollController.jumpTo(index: 9); - await tester.pump(); - - expect(tester.getBottomRight(find.text('Item 9')).dy, itemHeight); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(0, -itemHeight)); - await tester.pumpAndSettle(); - - final item9Bottom = tester.getBottomRight(find.text('Item 9')).dy; - expect(item9Bottom, lessThan(itemHeight)); - - await tester.drag(find.byType(PageView), const Offset(-500, 0)); - await tester.pumpAndSettle(); - - await tester.drag(find.byType(PageView), const Offset(500, 0)); - await tester.pumpAndSettle(); - - expect(tester.getBottomRight(find.text('Item 9')).dy, item9Bottom); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemLeadingEdge, - -(itemHeight / screenHeight) / 2); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemTrailingEdge, - (itemHeight / screenHeight) / 2); - }, - ); - - testWidgets('List with no items', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, itemCount: 0); - - expect(find.text('Item 0'), findsNothing); - }); - - testWidgets('Jump to 100 then set itemCount to 0', - (WidgetTester tester) async { - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - final itemCount = ValueNotifier(defaultItemCount); - - await tester.pumpWidget( - MaterialApp( - home: ValueListenableBuilder( - valueListenable: itemCount, - builder: (context, itemCount, child) { - return ScrollablePositionedList.builder( - initialScrollIndex: min(100, itemCount), - itemCount: itemCount, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener, - itemBuilder: (context, index) { - assert( - index >= 0 && index <= itemCount - 1, - 'index must be in the range of 0 to itemCount - 1', - ); - return SizedBox( - height: itemHeight, - child: Text('Item $index'), - ); - }, - ); - }, - ), - ), - ); - - await tester.pumpAndSettle(); - itemScrollController.jumpTo(index: 100); - await tester.pumpAndSettle(); - expect(find.text('Item 100'), findsOneWidget); - - itemCount.value = 0; - await tester.pumpAndSettle(); - - expect(find.text('Item 0'), findsNothing); - expect(find.text('Item 100'), findsNothing); - expect(itemPositionsListener.itemPositions.value.isEmpty, isTrue); - }); - - testWidgets('List positioned with 100 at top then set itemCount to 100', - (WidgetTester tester) async { - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - final itemCount = ValueNotifier(defaultItemCount); - - await tester.pumpWidget( - MaterialApp( - home: ValueListenableBuilder( - valueListenable: itemCount, - builder: (context, itemCount, child) { - return ScrollablePositionedList.builder( - initialScrollIndex: min(100, itemCount - 1), - itemCount: itemCount, - itemBuilder: (context, index) { - assert( - index >= 0 && index <= itemCount - 1, - 'index must be in the range of 0 to itemCount - 1', - ); - return SizedBox( - height: itemHeight, - child: Text('Item $index'), - ); - }, - ); - }, - ), - ), - ); - - await tester.pumpAndSettle(); - expect(tester.getTopLeft(find.text('Item 100')).dy, 0); - - itemCount.value = 100; - await tester.pumpAndSettle(); - - expect(find.text('Item 100'), findsNothing); - expect(tester.getBottomLeft(find.text('Item 99')).dy, screenHeight); - }); - - testWidgets('List positioned with 499 at bottom then set itemCount to 100', - (WidgetTester tester) async { - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - final itemCount = ValueNotifier(defaultItemCount); - - await tester.pumpWidget( - MaterialApp( - home: ValueListenableBuilder( - valueListenable: itemCount, - builder: (context, itemCount, child) { - return ScrollablePositionedList.builder( - initialScrollIndex: itemCount - 1, - itemCount: itemCount, - itemBuilder: (context, index) { - assert( - index >= 0 && index <= itemCount - 1, - 'index must be in the range of 0 to itemCount - 1', - ); - return SizedBox( - height: itemHeight, - child: Text('Item $index'), - ); - }, - ); - }, - ), - ), - ); - - await tester.pumpAndSettle(); - expect(tester.getBottomRight(find.text('Item 499')).dy, screenHeight); - - itemCount.value = 100; - await tester.pumpAndSettle(); - - expect(find.text('Item 100'), findsNothing); - expect(tester.getBottomLeft(find.text('Item 99')).dy, screenHeight); - }); - - testWidgets('Large minCacheExtent', (WidgetTester tester) async { - await setUpWidgetTest(tester, minCacheExtent: itemHeight * 200); - - expect(find.text('Item 100', skipOffstage: false), findsOneWidget); - }); - - testWidgets('Scroll to 20 without fading small minCacheExtent', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener, - minCacheExtent: 10, - ); - - var fadeTransition = tester.widget(fadeTransitionFinder); - final initialOpacity = fadeTransition.opacity; - - unawaited( - itemScrollController.scrollTo(index: 20, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration ~/ 2); - - fadeTransition = tester.widget(fadeTransitionFinder); - expect(fadeTransition.opacity, initialOpacity); - - await tester.pumpAndSettle(); - - expect(find.text('Item 14'), findsNothing); - expect(find.text('Item 20'), findsOneWidget); - }); - - testWidgets('Scroll to 100 without fading for large minCacheExtent', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - minCacheExtent: itemHeight * 200, - ); - - var fadeTransition = tester.widget( - fadeTransitionFinder, - ); - final initialOpacity = fadeTransition.opacity; - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration ~/ 2); - - fadeTransition = tester.widget(fadeTransitionFinder); - expect(fadeTransition.opacity, initialOpacity); - - await tester.pumpAndSettle(); - - expect(find.text('Item 100'), findsOneWidget); - }); - - testWidgets('Position list when not enough above top item to fill viewport', - (WidgetTester tester) async { - const alignment = 0.8; - - await setUpWidgetTest( - tester, - itemCount: 2, - initialAlignment: alignment, - ); - - await tester.pumpAndSettle(); - - expect(tester.getTopLeft(find.text('Item 0')).dy, screenHeight * alignment); - }); - - testWidgets('Rebuild with scroll controller', (WidgetTester tester) async { - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - final key = ValueNotifier(const ValueKey('key')); - final itemScrollController = ItemScrollController(); - - await tester.pumpWidget( - MaterialApp( - home: ValueListenableBuilder( - valueListenable: key, - builder: (context, key, child) { - return Container( - key: key, - child: ScrollablePositionedList.builder( - itemCount: 200, - itemScrollController: itemScrollController, - itemBuilder: (context, index) { - return SizedBox( - height: itemHeight, - child: Text('Item $index'), - ); - }, - ), - ); - }, - ), - ), - ); - - await tester.pumpAndSettle(); - - key.value = const ValueKey('newKey'); - await tester.pumpAndSettle(); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pumpAndSettle(); - - expect(find.text('Item 100'), findsOneWidget); - }); - - testWidgets('Double rebuild with scroll controller', - (WidgetTester tester) async { - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - final outerKey = ValueNotifier(const ValueKey('outerKey')); - final innerKey = GlobalKey(); - final listKey = ValueNotifier(const ValueKey(null)); - final itemScrollController = ItemScrollController(); - - await tester.pumpWidget( - MaterialApp( - home: ValueListenableBuilder( - valueListenable: outerKey, - builder: (context, outerKey, child) => ValueListenableBuilder( - valueListenable: listKey, - builder: (context, listKey, child) => Container( - key: outerKey, - child: Builder( - builder: (context) => Container( - key: innerKey, - child: ScrollablePositionedList.builder( - key: listKey, - itemCount: 200, - itemScrollController: itemScrollController, - itemBuilder: (context, index) => SizedBox( - height: itemHeight, - child: Text('Item $index'), - ), - ), - ), - ), - ), - ), - ), - ), - ); - - await tester.pumpAndSettle(); - - outerKey.value = const ValueKey('newOuterKey'); - listKey.value = const ValueKey('newListKey'); - await tester.pumpAndSettle(); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pumpAndSettle(); - - expect(find.text('Item 100'), findsOneWidget); - }); - - testWidgets('Key change with scroll controller', (WidgetTester tester) async { - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - final key = ValueNotifier(const ValueKey('key')); - final itemScrollController = ItemScrollController(); - - await tester.pumpWidget( - MaterialApp( - home: ValueListenableBuilder( - valueListenable: key, - builder: (context, key, child) { - return ScrollablePositionedList.builder( - key: key, - itemCount: 10, - itemScrollController: itemScrollController, - itemBuilder: (context, index) { - return SizedBox( - height: itemHeight, - child: Text('Item $index'), - ); - }, - ); - }, - ), - ), - ); - - await tester.pumpAndSettle(); - - key.value = const ValueKey('newKey'); - await tester.pumpAndSettle(); - }); - - testWidgets('Scroll after rebuild', (WidgetTester tester) async { - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - final key = ValueNotifier(const ValueKey('key')); - final itemScrollController = ItemScrollController(); - - await tester.pumpWidget( - MaterialApp( - home: ValueListenableBuilder( - valueListenable: key, - builder: (context, key, child) { - return Container( - key: key, - child: ScrollablePositionedList.builder( - itemCount: 100, - itemScrollController: itemScrollController, - itemBuilder: (context, index) { - return SizedBox( - height: itemHeight, - child: Text('Item $index'), - ); - }, - ), - ); - }, - ), - ), - ); - - await tester.pumpAndSettle(); - - key.value = const ValueKey('newKey'); - await tester.pumpAndSettle(); - - unawaited( - itemScrollController.scrollTo(index: 70, duration: scrollDuration)); - await tester.pumpAndSettle(); - - expect(find.text('Item 70'), findsOneWidget); - }); - - testWidgets('Scroll after rebuild when resusing state', - (WidgetTester tester) async { - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - final containerKey = ValueNotifier(const ValueKey('key')); - final scrollKey = GlobalKey(); - final itemScrollController = ItemScrollController(); - - await tester.pumpWidget( - MaterialApp( - home: ValueListenableBuilder( - valueListenable: containerKey, - builder: (context, key, child) { - return Container( - key: key, - child: ScrollablePositionedList.builder( - key: scrollKey, - itemCount: 100, - itemScrollController: itemScrollController, - itemBuilder: (context, index) { - return SizedBox( - height: itemHeight, - child: Text('Item $index'), - ); - }, - ), - ); - }, - ), - ), - ); - - await tester.pumpAndSettle(); - - containerKey.value = const ValueKey('newKey'); - await tester.pumpAndSettle(); - - unawaited( - itemScrollController.scrollTo(index: 70, duration: scrollDuration)); - await tester.pumpAndSettle(); - - expect(find.text('Item 70'), findsOneWidget); - }); - - testWidgets('Scroll after changing scroll controller', - (WidgetTester tester) async { - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - final itemScrollController0 = ItemScrollController(); - final itemScrollController1 = ItemScrollController(); - final itemScrollControllerListenable = ValueNotifier(itemScrollController0); - - await tester.pumpWidget( - MaterialApp( - home: ValueListenableBuilder( - valueListenable: itemScrollControllerListenable, - builder: (context, itemScrollController, child) { - return ScrollablePositionedList.builder( - itemCount: 100, - itemScrollController: itemScrollController, - itemBuilder: (context, index) { - return SizedBox( - height: itemHeight, - child: Text('Item $index'), - ); - }, - ); - }, - ), - ), - ); - - await tester.pumpAndSettle(); - expect(itemScrollController0.isAttached, true); - expect(itemScrollController1.isAttached, false); - - itemScrollControllerListenable.value = itemScrollController1; - await tester.pumpAndSettle(); - - expect(itemScrollController0.isAttached, false); - expect(itemScrollController1.isAttached, true); - - unawaited( - itemScrollController1.scrollTo(index: 70, duration: scrollDuration)); - await tester.pumpAndSettle(); - - expect(find.text('Item 70'), findsOneWidget); - }); - - testWidgets('Scroll after swapping scroll controllers', - (WidgetTester tester) async { - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - final itemScrollController0 = ItemScrollController(); - final itemScrollController1 = ItemScrollController(); - final topItemScrollControllerListenable = - ValueNotifier(itemScrollController0); - final bottomItemScrollControllerListenable = - ValueNotifier(itemScrollController1); - - await tester.pumpWidget(MaterialApp( - home: Column( - children: [ - Expanded( - child: ValueListenableBuilder( - valueListenable: topItemScrollControllerListenable, - builder: (context, itemScrollController, child) { - return ScrollablePositionedList.builder( - itemCount: 100, - itemScrollController: itemScrollController, - itemBuilder: (context, index) { - return SizedBox( - height: itemHeight, - child: Text('Item $index'), - ); - }, - ); - }, - ), - ), - Expanded( - child: ValueListenableBuilder( - valueListenable: bottomItemScrollControllerListenable, - builder: (context, itemScrollController, child) { - return ScrollablePositionedList.builder( - itemCount: 100, - itemScrollController: itemScrollController, - itemBuilder: (context, index) { - return SizedBox( - height: itemHeight, - child: Text('Item $index'), - ); - }, - ); - }, - ), - ), - ], - ), - )); - - await tester.pumpAndSettle(); - expect(itemScrollController0.isAttached, true); - expect(itemScrollController1.isAttached, true); - - topItemScrollControllerListenable.value = itemScrollController1; - bottomItemScrollControllerListenable.value = itemScrollController0; - await tester.pumpAndSettle(); - - expect(itemScrollController0.isAttached, true); - expect(itemScrollController1.isAttached, true); - - unawaited( - itemScrollController1.scrollTo(index: 70, duration: scrollDuration)); - unawaited( - itemScrollController0.scrollTo(index: 50, duration: scrollDuration)); - await tester.pumpAndSettle(); - - expect(find.text('Item 70'), findsOneWidget); - expect(find.text('Item 50'), findsOneWidget); - }); -} - -bool collectSemanticNodes(SemanticsNode root, List nodes) { - nodes.add(root); - if (!root.hasChildren) return true; - root.visitChildren((child) => collectSemanticNodes(child, nodes)); - return true; -} diff --git a/packages/stream_chat_flutter/test/scrollable_positioned_list/separated_positioned_list_test.dart b/packages/stream_chat_flutter/test/scrollable_positioned_list/separated_positioned_list_test.dart deleted file mode 100644 index f6dc2b5cef..0000000000 --- a/packages/stream_chat_flutter/test/scrollable_positioned_list/separated_positioned_list_test.dart +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/item_positions_notifier.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/positioned_list.dart'; - -const screenHeight = 400.0; -const screenWidth = 400.0; -const itemHeight = screenHeight / 10.0; -const separatorHeight = screenHeight / 20.0; -const defaultItemCount = 500; -const cacheExtent = itemHeight * 2; - -void main() { - final itemPositionsNotifier = ItemPositionsListener.create(); - - Future setUpWidgetTest( - WidgetTester tester, { - int topItem = 0, - ScrollController? scrollController, - double anchor = 0, - int itemCount = defaultItemCount, - }) async { - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - await tester.pumpWidget( - MaterialApp( - home: PositionedList( - itemCount: itemCount, - positionedIndex: topItem, - alignment: anchor, - controller: scrollController, - itemBuilder: (context, index) => SizedBox( - height: itemHeight, - child: Text('Item $index'), - ), - separatorBuilder: (context, index) => SizedBox( - height: separatorHeight, - child: Text('Separator $index'), - ), - itemPositionsNotifier: itemPositionsNotifier as ItemPositionsNotifier, - cacheExtent: cacheExtent, - ), - ), - ); - } - - testWidgets('Empty list', (WidgetTester tester) async { - await setUpWidgetTest(tester, itemCount: 0); - - expect(find.text('Item 0'), findsNothing); - expect(find.text('Separator 0'), findsNothing); - }); - - testWidgets('Short list', (WidgetTester tester) async { - await setUpWidgetTest(tester, itemCount: 3); - - expect(find.text('Item 0'), findsOneWidget); - expect(find.text('Separator 0'), findsOneWidget); - expect(find.text('Item 1'), findsOneWidget); - expect(find.text('Separator 1'), findsOneWidget); - expect(find.text('Item 2'), findsOneWidget); - expect(find.text('Separator 2'), findsNothing); - expect(find.text('Item 3'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 2) - .itemTrailingEdge, - _screenProportion(numberOfItems: 3, numberOfSeparators: 2)); - }); - - testWidgets('Short list centered at 1 scrolled up', - (WidgetTester tester) async { - await setUpWidgetTest(tester, itemCount: 3, topItem: 1); - - await tester.drag( - find.byType(PositionedList), const Offset(0, itemHeight * 2)); - await tester.pumpAndSettle(); - - expect(find.text('Item 0'), findsOneWidget); - expect(find.text('Separator 0'), findsOneWidget); - expect(find.text('Item 1'), findsOneWidget); - expect(find.text('Separator 1'), findsOneWidget); - expect(find.text('Item 2'), findsOneWidget); - expect(find.text('Separator 2'), findsNothing); - expect(find.text('Item 3'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 2) - .itemTrailingEdge, - _screenProportion(numberOfItems: 3, numberOfSeparators: 2)); - }); - - testWidgets('List positioned with 0 at top', (WidgetTester tester) async { - await setUpWidgetTest(tester); - await tester.pump(); - - expect(find.text('Item 0'), findsOneWidget); - expect(find.text('Separator 5'), findsOneWidget); - expect(find.text('Item 6'), findsOneWidget); - expect(find.text('Separator 6'), findsNothing); - expect(find.text('Item 7'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 5) - .itemTrailingEdge, - 1 - _screenProportion(numberOfItems: 1, numberOfSeparators: 1)); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 6) - .itemTrailingEdge, - 1); - }); - - testWidgets('List positioned with 5 at top', (WidgetTester tester) async { - await setUpWidgetTest(tester, topItem: 5); - await tester.pump(); - - expect(find.text('Item 4'), findsNothing); - expect(find.text('Separator 4'), findsNothing); - expect(find.text('Item 5'), findsOneWidget); - expect(find.text('Separator 5'), findsOneWidget); - - expect(find.text('Separator 10'), findsOneWidget); - expect(find.text('Item 11'), findsOneWidget); - expect(find.text('Separator 11'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 5) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 6) - .itemLeadingEdge, - _screenProportion(numberOfItems: 1, numberOfSeparators: 1)); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 11) - .itemTrailingEdge, - 1); - }); - - testWidgets('List positioned with 20 at bottom', (WidgetTester tester) async { - await setUpWidgetTest(tester, topItem: 20, anchor: 1); - await tester.pump(); - - expect(find.text('Item 20'), findsNothing); - expect(find.text('Item 19'), findsOneWidget); - expect(find.text('Separator 19'), findsOneWidget); - expect(find.text('Item 14'), findsOneWidget); - expect(find.text('Separator 13'), findsOneWidget); - expect(find.text('Item 13'), findsOneWidget); - expect(find.text('Separator 12'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 19) - .itemTrailingEdge, - 1 - _screenProportion(numberOfItems: 0, numberOfSeparators: 1)); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 20) - .itemLeadingEdge, - 1); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 13) - .itemLeadingEdge, - _screenProportion(numberOfItems: -0.5, numberOfSeparators: 0)); - }); - - testWidgets('List positioned with item 20 at halfway', - (WidgetTester tester) async { - await setUpWidgetTest(tester, topItem: 20, anchor: 0.5); - await tester.pump(); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 20) - .itemLeadingEdge, - 0.5); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 20) - .itemTrailingEdge, - 0.5 + itemHeight / screenHeight); - }); - - testWidgets('List positioned with item 20 half off top of screen', - (WidgetTester tester) async { - await setUpWidgetTest(tester, - topItem: 20, anchor: -(itemHeight / screenHeight) / 2); - await tester.pump(); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 20) - .itemLeadingEdge, - _screenProportion(numberOfItems: -0.5, numberOfSeparators: 0)); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 20) - .itemTrailingEdge, - _screenProportion(numberOfItems: 0.5, numberOfSeparators: 0)); - }); - - testWidgets('List positioned with 5 at top then scroll up 2 items', - (WidgetTester tester) async { - await setUpWidgetTest(tester, topItem: 5); - - await tester.drag(find.byType(PositionedList), - const Offset(0, 2 * (itemHeight + separatorHeight))); - await tester.pump(); - - expect(find.text('Separator 2'), findsNothing); - expect(find.text('Item 3'), findsOneWidget); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 2) - .itemLeadingEdge, - _screenProportion(numberOfItems: -1, numberOfSeparators: -1)); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 3) - .itemLeadingEdge, - 0); - }); -} - -double _screenProportion( - {required double numberOfItems, required double numberOfSeparators}) => - (numberOfItems * itemHeight + numberOfSeparators * separatorHeight) / - screenHeight; diff --git a/packages/stream_chat_flutter/test/scrollable_positioned_list/separated_scrollable_positioned_list_test.dart b/packages/stream_chat_flutter/test/scrollable_positioned_list/separated_scrollable_positioned_list_test.dart deleted file mode 100644 index d4d99595dd..0000000000 --- a/packages/stream_chat_flutter/test/scrollable_positioned_list/separated_scrollable_positioned_list_test.dart +++ /dev/null @@ -1,599 +0,0 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/scroll_view.dart'; - -const screenHeight = 400.0; -const screenWidth = 400.0; -const itemHeight = screenHeight / 10.0; -const separatorHeight = screenHeight / 20.0; -const defaultItemCount = 500; -const scrollDuration = Duration(seconds: 1); -const scrollDurationTolerance = Duration(milliseconds: 1); -const tolerance = 1e-3; - -void main() { - Future setUpWidgetTest( - WidgetTester tester, { - Key? key, - ItemScrollController? itemScrollController, - ItemPositionsListener? itemPositionsListener, - int initialIndex = 0, - double initialAlignment = 0.0, - int? itemCount, - ScrollPhysics? physics, - bool addSemanticIndexes = true, - int? semanticChildCount, - EdgeInsets? padding, - bool addRepaintBoundaries = true, - bool addAutomaticKeepAlives = true, - }) async { - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - await tester.pumpWidget( - MaterialApp( - home: ScrollablePositionedList.separated( - itemCount: itemCount ?? defaultItemCount, - itemScrollController: itemScrollController, - itemBuilder: (context, index) => SizedBox( - height: itemHeight, - child: Text('Item $index'), - ), - separatorBuilder: (context, index) => SizedBox( - height: separatorHeight, - child: Text('Separator $index'), - ), - key: key, - itemPositionsListener: itemPositionsListener, - initialScrollIndex: initialIndex, - initialAlignment: initialAlignment, - physics: physics, - addSemanticIndexes: addSemanticIndexes, - semanticChildCount: semanticChildCount, - padding: padding, - addAutomaticKeepAlives: addAutomaticKeepAlives, - addRepaintBoundaries: addRepaintBoundaries, - ), - ), - ); - } - - testWidgets('List positioned with 0 at top', (WidgetTester tester) async { - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, itemPositionsListener: itemPositionsListener); - - expect(find.text('Item 0'), findsOneWidget); - expect(find.text('Separator 5'), findsOneWidget); - expect(find.text('Item 6'), findsOneWidget); - expect(find.text('Separator 6'), findsNothing); - expect(find.text('Item 7'), findsNothing); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 5) - .itemTrailingEdge, - 1 - _screenProportion(numberOfItems: 1, numberOfSeparators: 1)); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 6) - .itemTrailingEdge, - 1); - expect( - itemPositionsListener.itemPositions.value - .where((position) => position.index == 7), - isEmpty); - }); - - testWidgets('List positioned with 0 at top - use default values', - (WidgetTester tester) async { - final itemPositionsListener = ItemPositionsListener.create(); - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - await tester.pumpWidget( - MaterialApp( - home: ScrollablePositionedList.separated( - itemCount: defaultItemCount, - itemBuilder: (context, index) => SizedBox( - height: itemHeight, - child: Text('Item $index'), - ), - separatorBuilder: (context, index) => SizedBox( - height: separatorHeight, - child: Text('Separator $index'), - ), - itemPositionsListener: itemPositionsListener, - ), - ), - ); - - expect(find.text('Item 0'), findsOneWidget); - expect(find.text('Separator 5'), findsOneWidget); - expect(find.text('Item 6'), findsOneWidget); - expect(find.text('Separator 6'), findsNothing); - expect(find.text('Item 7'), findsNothing); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 5) - .itemTrailingEdge, - 1 - _screenProportion(numberOfItems: 1, numberOfSeparators: 1)); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 6) - .itemTrailingEdge, - 1); - expect( - itemPositionsListener.itemPositions.value - .where((position) => position.index == 7), - isEmpty); - }); - - testWidgets('List positioned with 5 at top', (WidgetTester tester) async { - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemPositionsListener: itemPositionsListener, initialIndex: 5); - - expect(find.text('Item 4'), findsNothing); - expect(find.text('Separator 4'), findsNothing); - expect(find.text('Item 5'), findsOneWidget); - expect(find.text('Separator 5'), findsOneWidget); - expect(find.text('Separator 10'), findsOneWidget); - expect(find.text('Item 11'), findsOneWidget); - expect(find.text('Separator 11'), findsNothing); - - expect( - itemPositionsListener.itemPositions.value - .where((position) => position.index == 4), - isEmpty); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 5) - .itemLeadingEdge, - 0); - }); - - testWidgets('List positioned with 9 at middle', (WidgetTester tester) async { - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemPositionsListener: itemPositionsListener, - initialIndex: 9, - initialAlignment: 0.5); - - expect(tester.getTopLeft(find.text('Item 9')).dy, screenHeight / 2); - expect(tester.getTopLeft(find.text('Item 8')).dy, - screenHeight / 2 - itemHeight - separatorHeight); - expect(tester.getTopLeft(find.text('Item 10')).dy, - screenHeight / 2 + itemHeight + separatorHeight); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemLeadingEdge, - 0.5); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 8) - .itemLeadingEdge, - 0.5 - _screenProportion(numberOfItems: 1, numberOfSeparators: 1)); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 10) - .itemLeadingEdge, - 0.5 + _screenProportion(numberOfItems: 1, numberOfSeparators: 1)); - }); - - testWidgets('Scroll to 9 half way off top', (WidgetTester tester) async { - final itemPositionsListener = ItemPositionsListener.create(); - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, - itemPositionsListener: itemPositionsListener, - itemScrollController: itemScrollController); - - unawaited(itemScrollController.scrollTo( - index: 9, - duration: scrollDuration, - alignment: -(itemHeight / screenHeight) / 2)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration + scrollDurationTolerance); - - expect(tester.getTopLeft(find.text('Item 9')).dy, -itemHeight / 2); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemLeadingEdge, - _screenProportion(numberOfItems: -0.5, numberOfSeparators: 0)); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemTrailingEdge, - _screenProportion(numberOfItems: 0.5, numberOfSeparators: 0)); - }); - - testWidgets('Jump to 9 half way off top', (WidgetTester tester) async { - final itemPositionsListener = ItemPositionsListener.create(); - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, - itemPositionsListener: itemPositionsListener, - itemScrollController: itemScrollController); - - itemScrollController.jumpTo( - index: 9, alignment: -(itemHeight / screenHeight) / 2); - await tester.pump(); - - expect(tester.getTopLeft(find.text('Item 9')).dy, -itemHeight / 2); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemLeadingEdge, - _screenProportion(numberOfItems: -0.5, numberOfSeparators: 0)); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemTrailingEdge, - _screenProportion(numberOfItems: 0.5, numberOfSeparators: 0)); - }); - - testWidgets('List positioned with 9 at middle scroll to 16 at bottom', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener, - initialIndex: 9, - initialAlignment: 0.5); - - unawaited(itemScrollController.scrollTo( - index: 16, duration: scrollDuration, alignment: 1)); - await tester.pump(); - await tester.pump(); - await tester.pump(scrollDuration + scrollDurationTolerance); - - expect(tester.getBottomRight(find.text('Item 15')).dy, - screenHeight - separatorHeight); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 15) - .itemTrailingEdge, - 1 - _screenProportion(numberOfItems: 0, numberOfSeparators: 1)); - }); - - testWidgets('physics', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - physics: const BouncingScrollPhysics()); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(0, 50)); - await tester.pump(const Duration(milliseconds: 200)); - - expect(tester.getTopLeft(find.text('Item 0')).dy, greaterThan(0)); - - await tester.pumpAndSettle(); - expect(tester.getTopLeft(find.text('Item 0')).dy, 0); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pumpAndSettle(); - itemScrollController.jumpTo(index: 0); - await tester.pumpAndSettle(); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(0, 50)); - await tester.pump(const Duration(milliseconds: 200)); - - expect(tester.getTopLeft(find.text('Item 0')).dy, greaterThan(0)); - - await tester.pumpAndSettle(); - expect(tester.getTopLeft(find.text('Item 0')).dy, 0); - }); - - testWidgets('correct index semantics', (WidgetTester tester) async { - await setUpWidgetTest(tester, initialIndex: 5); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(0, itemHeight * 4)); - await tester.pumpAndSettle(); - - final indexSemantics3 = tester.widget(find.ancestor( - of: find.text('Item 3'), matching: find.byType(IndexedSemantics))); - expect(indexSemantics3.index, 3); - final indexSemantics4 = tester.widget(find.ancestor( - of: find.text('Item 4'), matching: find.byType(IndexedSemantics))); - expect(indexSemantics4.index, 4); - }); - - testWidgets('addIndexSemantics = false', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - initialIndex: 5, - addSemanticIndexes: false, - ); - - expect(find.byType(IndexedSemantics), findsNothing); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pumpAndSettle(); - - expect(find.byType(IndexedSemantics), findsNothing); - }); - - testWidgets('semanticChildCount specified', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - - await setUpWidgetTest( - tester, - semanticChildCount: 30, - itemScrollController: itemScrollController, - ); - - final customScrollView = - tester.widget(find.byType(UnboundedCustomScrollView)); - expect(customScrollView.semanticChildCount, 30); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pumpAndSettle(); - - final customScrollView2 = - tester.widget(find.byType(UnboundedCustomScrollView)); - expect(customScrollView2.semanticChildCount, 30); - }); - - testWidgets('semanticChildCount not specified', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - ); - - final customScrollView = - tester.widget(find.byType(UnboundedCustomScrollView)); - expect(customScrollView.semanticChildCount, defaultItemCount); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pumpAndSettle(); - - final customScrollView2 = - tester.widget(find.byType(UnboundedCustomScrollView)); - expect(customScrollView2.semanticChildCount, defaultItemCount); - }); - - testWidgets('padding test - centered at top', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - padding: const EdgeInsets.all(10), - ); - - expect(tester.getTopLeft(find.text('Item 0')), const Offset(10, 10)); - expect(tester.getTopLeft(find.text('Item 1')), - const Offset(10, itemHeight + 10 + separatorHeight)); - expect(tester.getTopRight(find.text('Item 1')), - const Offset(screenWidth - 10, itemHeight + 10 + separatorHeight)); - - unawaited( - itemScrollController.scrollTo(index: 494, duration: scrollDuration)); - await tester.pumpAndSettle(); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(0, -500)); - await tester.pumpAndSettle(); - - expect(tester.getBottomRight(find.text('Item 499')), - const Offset(screenWidth - 10, screenHeight - 10)); - }); - - testWidgets('padding test - centered sliver not at top', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - initialIndex: 2, - padding: const EdgeInsets.all(10), - ); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(0, 200)); - await tester.pumpAndSettle(); - - expect(tester.getTopLeft(find.text('Item 0')), const Offset(10, 10)); - expect(tester.getTopLeft(find.text('Item 2')), - const Offset(10, 10 + 2 * (separatorHeight + itemHeight))); - expect( - tester.getTopRight(find.text('Item 3')), - const Offset( - screenWidth - 10, 10 + 3 * (itemHeight + separatorHeight))); - }); - - testWidgets('no repaint bounderies', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - initialIndex: 2, - padding: const EdgeInsets.all(10), - addRepaintBoundaries: false, - ); - - expect( - tester - .widgetList(find.descendant( - of: find.byType(ScrollablePositionedList), - matching: find.byType(RepaintBoundary))) - .length, - lessThan(5)); - }); - - testWidgets('no automatic keep alives', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - initialIndex: 2, - padding: const EdgeInsets.all(10), - addAutomaticKeepAlives: false, - ); - - expect( - find.descendant( - of: find.byType(ScrollablePositionedList), - matching: find.byType(AutomaticKeepAlive)), - findsNothing); - }); - - testWidgets('List can be keyed', (WidgetTester tester) async { - const key = ValueKey('key'); - - await setUpWidgetTest(tester, key: key); - - expect(find.byKey(key), findsOneWidget); - }); - - testWidgets('Empty list then update to single item list', - (WidgetTester tester) async { - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - final itemCount = ValueNotifier(0); - - await tester.pumpWidget( - MaterialApp( - home: ValueListenableBuilder( - valueListenable: itemCount, - builder: (context, itemCount, child) { - return ScrollablePositionedList.separated( - itemCount: itemCount, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener, - itemBuilder: (context, index) => SizedBox( - height: itemHeight, - child: Text('Item $index'), - ), - separatorBuilder: (context, index) => SizedBox( - height: separatorHeight, - child: Text('Separator $index'), - ), - ); - }, - ), - ), - ); - - await tester.pumpAndSettle(); - - itemCount.value = 1; - await tester.pumpAndSettle(); - - expect(find.text('Item 0'), findsOneWidget); - expect(find.text('Separator 0'), findsNothing); - }); - - testWidgets('ItemPositions: Empty list then update to 10 items list', - (WidgetTester tester) async { - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - final itemCount = ValueNotifier(0); - - await tester.pumpWidget( - MaterialApp( - home: ValueListenableBuilder( - valueListenable: itemCount, - builder: (context, itemCount, child) { - return ScrollablePositionedList.separated( - itemCount: itemCount, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener, - itemBuilder: (context, index) => SizedBox( - height: itemHeight, - child: Text('Item $index'), - ), - separatorBuilder: (context, index) => SizedBox( - height: separatorHeight, - child: Text('Separator $index'), - ), - ); - }, - ), - ), - ); - - await tester.pumpAndSettle(); - - expect(find.text('Item 0'), findsNothing); - expect(find.text('Separator 0'), findsNothing); - expect(itemPositionsListener.itemPositions.value, []); - - itemCount.value = 10; - await tester.pumpAndSettle(); - - expect(find.text('Item 0'), findsOneWidget); - expect(find.text('Separator 5'), findsOneWidget); - expect(find.text('Item 6'), findsOneWidget); - expect(find.text('Separator 6'), findsNothing); - expect(find.text('Item 7'), findsNothing); - - expect(itemPositionsListener.itemPositions.value, isNotEmpty); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 5) - .itemTrailingEdge, - 1 - _screenProportion(numberOfItems: 1, numberOfSeparators: 1)); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 6) - .itemTrailingEdge, - 1); - expect( - itemPositionsListener.itemPositions.value - .where((position) => position.index == 7), - isEmpty); - }); -} - -double _screenProportion( - {required double numberOfItems, required double numberOfSeparators}) => - (numberOfItems * itemHeight + numberOfSeparators * separatorHeight) / - screenHeight; diff --git a/packages/stream_chat_flutter/test/scrollable_positioned_list/seperated_horizontal_scrollable_positioned_list_test.dart b/packages/stream_chat_flutter/test/scrollable_positioned_list/seperated_horizontal_scrollable_positioned_list_test.dart deleted file mode 100644 index 88b0fae0ac..0000000000 --- a/packages/stream_chat_flutter/test/scrollable_positioned_list/seperated_horizontal_scrollable_positioned_list_test.dart +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart'; - -const screenHeight = 400.0; -const screenWidth = 400.0; -const itemWidth = screenWidth / 10.0; -const separatorWidth = screenWidth / 20.0; -const itemCount = 500; -const scrollDuration = Duration(seconds: 1); -const tolerance = 10e-5; - -void main() { - Future setUpWidgetTest( - WidgetTester tester, { - ItemScrollController? itemScrollController, - ItemPositionsListener? itemPositionsListener, - bool reverse = false, - EdgeInsets? padding, - int initialScrollIndex = 0, - }) async { - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - await tester.pumpWidget( - MaterialApp( - home: ScrollablePositionedList.separated( - itemCount: itemCount, - itemScrollController: itemScrollController, - itemBuilder: (context, index) => SizedBox( - width: itemWidth, - child: Text('Item $index'), - ), - separatorBuilder: (context, index) => SizedBox( - width: separatorWidth, - child: Text('Separator $index'), - ), - itemPositionsListener: itemPositionsListener, - scrollDirection: Axis.horizontal, - reverse: reverse, - padding: padding, - initialScrollIndex: initialScrollIndex, - ), - ), - ); - } - - testWidgets('List positioned with 0 at left', (WidgetTester tester) async { - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, itemPositionsListener: itemPositionsListener); - - expect(tester.getTopLeft(find.text('Item 0')).dx, 0); - expect(tester.getBottomLeft(find.text('Item 1')).dx, - itemWidth + separatorWidth); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 1) - .itemLeadingEdge, - _screenProportion(numberOfItems: 1, numberOfSeparators: 1)); - }); - - testWidgets('Scroll to 2 (already on screen)', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - unawaited( - itemScrollController.scrollTo(index: 2, duration: scrollDuration)); - await tester.pump(); - await tester.pump(scrollDuration); - - expect(find.text('Item 1'), findsNothing); - expect(tester.getTopLeft(find.text('Item 2')).dx, 0); - expect( - tester.getTopLeft(find.text('Item 3')).dx, itemWidth + separatorWidth); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 2) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 3) - .itemLeadingEdge, - _screenProportion(numberOfItems: 1, numberOfSeparators: 1)); - }); - - testWidgets('Scroll to 100 (not already on screen)', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pumpAndSettle(); - - expect(find.text('Item 99'), findsNothing); - expect(find.text('Item 100'), findsOneWidget); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 100) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 101) - .itemLeadingEdge, - _screenProportion(numberOfItems: 1, numberOfSeparators: 1)); - }); - - testWidgets('Jump to 100', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - itemScrollController.jumpTo(index: 100); - await tester.pumpAndSettle(); - - expect(tester.getTopLeft(find.text('Item 100')).dx, 0); - expect(tester.getTopLeft(find.text('Item 101')).dx, - itemWidth + separatorWidth); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 100) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 101) - .itemLeadingEdge, - _screenProportion(numberOfItems: 1, numberOfSeparators: 1)); - }); - - testWidgets('padding test - centered sliver at left', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - padding: const EdgeInsets.all(10), - ); - - expect(tester.getTopLeft(find.text('Item 0')), const Offset(10, 10)); - expect(tester.getTopLeft(find.text('Item 1')), - const Offset(itemWidth + 10 + separatorWidth, 10)); - expect(tester.getBottomRight(find.text('Item 1')), - const Offset(10 + itemWidth * 2 + separatorWidth, screenHeight - 10)); - - unawaited( - itemScrollController.scrollTo(index: 494, duration: scrollDuration)); - await tester.pumpAndSettle(); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(-500, 0)); - await tester.pumpAndSettle(); - - expect(tester.getBottomRight(find.text('Item 499')), - const Offset(screenWidth - 10, screenHeight - 10)); - }); - - testWidgets('padding test - centered sliver not at left', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener, - initialScrollIndex: 2, - padding: const EdgeInsets.all(10), - ); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(300, 0)); - await tester.pumpAndSettle(); - - expect(tester.getTopLeft(find.text('Item 0')), const Offset(10, 10)); - expect(tester.getTopLeft(find.text('Item 2')), - const Offset(10 + 2 * (itemWidth + separatorWidth), 10)); - expect(tester.getTopLeft(find.text('Item 3')), - const Offset(10 + 3 * (itemWidth + separatorWidth), 10)); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 2) - .itemLeadingEdge, - closeTo( - 10 / screenWidth + 2 * ((itemWidth + separatorWidth) / screenWidth), - tolerance)); - }); -} - -double _screenProportion( - {required double numberOfItems, required double numberOfSeparators}) => - (numberOfItems * itemWidth + numberOfSeparators * separatorWidth) / - screenHeight; diff --git a/packages/stream_chat_flutter/test/scrollable_positioned_list/shrink_wrap_position_list_test.dart b/packages/stream_chat_flutter/test/scrollable_positioned_list/shrink_wrap_position_list_test.dart deleted file mode 100644 index 8cf4303c4f..0000000000 --- a/packages/stream_chat_flutter/test/scrollable_positioned_list/shrink_wrap_position_list_test.dart +++ /dev/null @@ -1,475 +0,0 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/item_positions_notifier.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/src/positioned_list.dart'; - -const screenHeight = 400.0; -const screenWidth = 400.0; -const itemHeight = screenHeight / 10.0; -const defaultItemCount = 500; - -void main() { - final itemPositionsNotifier = ItemPositionsListener.create(); - - Future setUpWidgetTest( - WidgetTester tester, { - int topItem = 0, - Key? key, - ScrollController? scrollController, - double anchor = 0, - int itemCount = defaultItemCount, - bool reverse = false, - }) async { - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - await tester.pumpWidget( - MaterialApp( - // Use flex layout to ensure that the minimum height is not limited to - // screenHeight. - home: Column(children: [ - // Use Constrained to make max height not more than screenHeight - ConstrainedBox( - constraints: const BoxConstraints( - maxHeight: screenHeight, maxWidth: screenWidth), - child: PositionedList( - key: key, - itemCount: itemCount, - positionedIndex: topItem, - alignment: anchor, - controller: scrollController, - itemBuilder: (context, index) => SizedBox( - height: itemHeight, - child: Text('Item $index'), - ), - itemPositionsNotifier: - itemPositionsNotifier as ItemPositionsNotifier, - shrinkWrap: true, - reverse: reverse, - ), - ), - ]), - ), - ); - } - - testWidgets('short list with shrink wrap', (WidgetTester tester) async { - const itemCount = 5; - const key = Key('short_list'); - await setUpWidgetTest(tester, itemCount: itemCount, key: key); - await tester.pump(); - - expect( - tester.getBottomRight(find.text('Item 4')).dy, itemHeight * itemCount); - expect(find.text('Item 4'), findsOneWidget); - expect(find.text('Item 5'), findsNothing); - - final positionList = find.byKey(key); - expect(tester.getBottomRight(positionList).dy, itemHeight * itemCount); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 4) - .itemTrailingEdge, - 1.0); - }); - - testWidgets('List positioned with 0 at top and shrink wrap', - (WidgetTester tester) async { - await setUpWidgetTest(tester); - await tester.pump(); - - expect(find.text('Item 0'), findsOneWidget); - expect(find.text('Item 9'), findsOneWidget); - expect(find.text('Item 10'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemTrailingEdge, - 1); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 10) - .itemLeadingEdge, - 1); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 10) - .itemTrailingEdge, - 11 / 10); - }); - - testWidgets('List positioned with 5 at top and shrink wrap', - (WidgetTester tester) async { - await setUpWidgetTest(tester, topItem: 5); - await tester.pump(); - - expect(find.text('Item 4'), findsNothing); - expect(find.text('Item 5'), findsOneWidget); - expect(find.text('Item 14'), findsOneWidget); - expect(find.text('Item 15'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 4) - .itemLeadingEdge, - -1 / 10); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 4) - .itemTrailingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 5) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 14) - .itemTrailingEdge, - 1); - }); - - testWidgets('List positioned with 20 at bottom and shrink wrap', - (WidgetTester tester) async { - await setUpWidgetTest(tester, topItem: 20, anchor: 1); - await tester.pump(); - - expect(find.text('Item 20'), findsNothing); - expect(find.text('Item 19'), findsOneWidget); - expect(find.text('Item 10'), findsOneWidget); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 10) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 19) - .itemLeadingEdge, - 9 / 10); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 19) - .itemTrailingEdge, - 1); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 20) - .itemLeadingEdge, - 1); - }); - - testWidgets('List positioned with 20 at halfway and shrink wrap', - (WidgetTester tester) async { - await setUpWidgetTest(tester, topItem: 20, anchor: 0.5); - await tester.pump(); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 20) - .itemLeadingEdge, - 0.5); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 20) - .itemTrailingEdge, - 0.5 + itemHeight / screenHeight); - }); - - testWidgets('List positioned with 20 half off top of screen and shrink wrap', - (WidgetTester tester) async { - await setUpWidgetTest(tester, - topItem: 20, anchor: -(itemHeight / screenHeight) / 2); - await tester.pump(); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 20) - .itemLeadingEdge, - -(itemHeight / screenHeight) / 2); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 20) - .itemTrailingEdge, - (itemHeight / screenHeight) / 2); - }); - - testWidgets('List positioned with 5 at top then scroll up 2 and shrink wrap', - (WidgetTester tester) async { - await setUpWidgetTest(tester, topItem: 5); - - await tester.drag( - find.byType(PositionedList), const Offset(0, itemHeight * 2)); - await tester.pump(); - - expect(find.text('Item 2'), findsNothing); - expect(find.text('Item 3'), findsOneWidget); - expect(find.text('Item 12'), findsOneWidget); - expect(find.text('Item 13'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 2) - .itemLeadingEdge, - -1 / 10); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 3) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 12) - .itemTrailingEdge, - 1); - }); - - testWidgets( - 'List positioned with 5 at top then scroll down 1/2 and shrink wrap', - (WidgetTester tester) async { - await setUpWidgetTest(tester, topItem: 5); - - await tester.drag( - find.byType(PositionedList), const Offset(0, -1 / 2 * itemHeight)); - await tester.pump(); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 5) - .itemTrailingEdge, - 1 / 20); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 14) - .itemLeadingEdge, - 17 / 20); - }); - - testWidgets('List positioned with 0 at top scroll up 5 and shrink wrap', - (WidgetTester tester) async { - final scrollController = ScrollController(); - await setUpWidgetTest(tester, scrollController: scrollController); - await tester.pump(); - - scrollController.jumpTo(itemHeight * 5); - await tester.pump(); - await tester.pumpAndSettle(); - - expect(find.text('Item 4'), findsNothing); - expect(find.text('Item 5'), findsOneWidget); - expect(find.text('Item 14'), findsOneWidget); - expect(find.text('Item 15'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 5) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 4) - .itemLeadingEdge, - -1 / 10); - }); - - testWidgets( - '''List positioned with 5 at top then scroll up 2 programatically and shrink wrap''', - (WidgetTester tester) async { - final scrollController = ScrollController(); - await setUpWidgetTest(tester, - topItem: 5, scrollController: scrollController); - - scrollController.jumpTo(-2 * itemHeight); - await tester.pump(); - - expect(find.text('Item 2'), findsNothing); - expect(find.text('Item 3'), findsOneWidget); - expect(find.text('Item 12'), findsOneWidget); - expect(find.text('Item 13'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 2) - .itemLeadingEdge, - -1 / 10); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 3) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 12) - .itemTrailingEdge, - 1); - }, - ); - - testWidgets( - '''List positioned with 5 at top then scroll down 20 programatically and shrink wrap''', - (WidgetTester tester) async { - final scrollController = ScrollController(); - await setUpWidgetTest(tester, - topItem: 5, scrollController: scrollController); - - scrollController.jumpTo(itemHeight * 20); - await tester.pump(); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 23) - .itemLeadingEdge, - -2 / 10); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 24) - .itemLeadingEdge, - -1 / 10); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 25) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 4) - .itemLeadingEdge, - -21 / 10); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 5) - .itemLeadingEdge, - -20 / 10); - }, - ); - - testWidgets( - 'List positioned with 5 at top and initial scroll offset and shrink wrap', - (WidgetTester tester) async { - final scrollController = - ScrollController(initialScrollOffset: -2 * itemHeight); - await setUpWidgetTest(tester, - topItem: 5, scrollController: scrollController); - - expect(find.text('Item 2'), findsNothing); - expect(find.text('Item 3'), findsOneWidget); - expect(find.text('Item 12'), findsOneWidget); - expect(find.text('Item 13'), findsNothing); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 3) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 12) - .itemTrailingEdge, - 1); - }); - - testWidgets('short List with reverse and shrink wrap', - (WidgetTester tester) async { - const itemCount = 5; - const key = Key('short_list'); - await setUpWidgetTest(tester, - itemCount: itemCount, key: key, reverse: true); - await tester.pump(); - - expect(find.text('Item 4'), findsOneWidget); - expect(find.text('Item 5'), findsNothing); - expect( - tester.getBottomRight(find.text('Item 0')).dy, itemHeight * itemCount); - expect(tester.getTopLeft(find.text('Item 4')).dy, 0); - - final positionList = find.byKey(key); - expect(tester.getBottomRight(positionList).dy, itemHeight * itemCount); - expect(tester.getTopLeft(positionList).dy, 0); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 4) - .itemTrailingEdge, - 1.0); - }); - - testWidgets('test nested positioned list', (WidgetTester tester) async { - const itemCount = 50; - const key = Key('short_list'); - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - await tester.pumpWidget( - MaterialApp( - // Use flex layout to ensure that the minimum height is not limited to - // screenHeight. - home: PositionedList( - itemCount: 5, - itemBuilder: (context, index) { - if (index == 0) { - return PositionedList( - key: key, - itemCount: itemCount, - shrinkWrap: true, - itemBuilder: (context, idx) => SizedBox( - height: itemHeight, - child: Text('Item $idx'), - )); - } else { - return SizedBox( - height: itemHeight, - child: Text('Item ${itemCount + index - 1}'), - ); - } - }, - itemPositionsNotifier: itemPositionsNotifier as ItemPositionsNotifier, - ), - ), - ); - await tester.pump(); - - expect(find.text('Item 0'), findsOneWidget); - expect(find.text('Item 50'), findsNothing); - expect(tester.getTopLeft(find.text('Item 0')).dy, 0); - expect(tester.getBottomRight(find.text('Item 9')).dy, screenHeight); - - final positionList = find.byKey(key); - expect(tester.getBottomRight(positionList).dy, itemHeight * itemCount); - expect(tester.getTopLeft(positionList).dy, 0); - - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsNotifier.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemTrailingEdge, - 5.0); - }); -} diff --git a/packages/stream_chat_flutter/test/scrollable_positioned_list/shrink_wrap_scrollable_position_list_test.dart b/packages/stream_chat_flutter/test/scrollable_positioned_list/shrink_wrap_scrollable_position_list_test.dart deleted file mode 100644 index 37830e00c9..0000000000 --- a/packages/stream_chat_flutter/test/scrollable_positioned_list/shrink_wrap_scrollable_position_list_test.dart +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright 2019 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart'; - -const screenHeight = 400.0; -const screenWidth = 400.0; -const itemHeight = screenHeight / 10.0; -const itemCount = 500; -const scrollDuration = Duration(seconds: 1); - -void main() { - Future setUpWidgetTest( - WidgetTester tester, { - ItemScrollController? itemScrollController, - ItemPositionsListener? itemPositionsListener, - EdgeInsets? padding, - int initialIndex = 0, - }) async { - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(screenWidth, screenHeight); - - await tester.pumpWidget( - MaterialApp( - // Use flex layout to ensure that the minimum height is not limited to - // screenHeight. - home: Column(children: [ - // Use Constrained to make max height not more than screenHeight - ConstrainedBox( - constraints: const BoxConstraints( - maxHeight: screenHeight, maxWidth: screenWidth), - child: ScrollablePositionedList.builder( - itemCount: itemCount, - initialScrollIndex: initialIndex, - itemScrollController: itemScrollController, - itemBuilder: (context, index) => SizedBox( - height: itemHeight, - child: Text('Item $index'), - ), - itemPositionsListener: itemPositionsListener, - shrinkWrap: true, - padding: padding, - ), - ), - ]), - ), - ); - } - - testWidgets('List positioned with 0 at top and shrink wrap', - (WidgetTester tester) async { - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, itemPositionsListener: itemPositionsListener); - - expect(tester.getTopLeft(find.text('Item 0')).dy, 0); - expect(tester.getBottomRight(find.text('Item 9')).dy, screenHeight); - expect(find.text('Item 10'), findsNothing); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemTrailingEdge, - 1); - }); - - testWidgets('Scroll to 1 then 2 (both already on screen) with shrink wrap', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - unawaited( - itemScrollController.scrollTo(index: 1, duration: scrollDuration)); - await tester.pump(); - await tester.pump(scrollDuration); - expect(find.text('Item 0'), findsNothing); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 1) - .itemLeadingEdge, - 0); - expect(tester.getTopLeft(find.text('Item 1')).dy, 0); - - unawaited( - itemScrollController.scrollTo(index: 2, duration: scrollDuration)); - await tester.pump(); - await tester.pump(scrollDuration); - - expect(find.text('Item 1'), findsNothing); - expect(tester.getTopLeft(find.text('Item 2')).dy, 0); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 2) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 11) - .itemTrailingEdge, - 1); - }); - - testWidgets( - 'Scroll to 5 (already on screen) and then back to 0 with shrink wrap', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - unawaited( - itemScrollController.scrollTo(index: 5, duration: scrollDuration)); - await tester.pumpAndSettle(); - unawaited( - itemScrollController.scrollTo(index: 0, duration: scrollDuration)); - await tester.pumpAndSettle(); - - expect(find.text('Item 0'), findsOneWidget); - expect(find.text('Item 9'), findsOneWidget); - expect(find.text('Item 10'), findsNothing); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 0) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 9) - .itemTrailingEdge, - 1); - }); - - testWidgets('Scroll to 100 (not already on screen) with shrink wrap', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - unawaited( - itemScrollController.scrollTo(index: 100, duration: scrollDuration)); - await tester.pumpAndSettle(); - - expect(find.text('Item 99'), findsNothing); - expect(find.text('Item 100'), findsOneWidget); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 100) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 109) - .itemTrailingEdge, - 1); - }); - - testWidgets('Jump to 100 with shrink wrap', (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - final itemPositionsListener = ItemPositionsListener.create(); - await setUpWidgetTest(tester, - itemScrollController: itemScrollController, - itemPositionsListener: itemPositionsListener); - - itemScrollController.jumpTo(index: 100); - await tester.pumpAndSettle(); - - expect(tester.getTopLeft(find.text('Item 100')).dy, 0); - expect(tester.getBottomRight(find.text('Item 109')).dy, screenHeight); - - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 100) - .itemLeadingEdge, - 0); - expect( - itemPositionsListener.itemPositions.value - .firstWhere((position) => position.index == 109) - .itemTrailingEdge, - 1); - }); - - testWidgets('padding test - centered sliver at bottom with shrink wrap', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - padding: const EdgeInsets.all(10), - ); - - expect(tester.getTopLeft(find.text('Item 0')), const Offset(10, 10)); - expect(tester.getTopLeft(find.text('Item 1')), - const Offset(10, itemHeight + 10)); - expect(tester.getBottomRight(find.text('Item 1')), - const Offset(screenWidth - 10, 10 + itemHeight * 2)); - - unawaited( - itemScrollController.scrollTo(index: 490, duration: scrollDuration)); - await tester.pumpAndSettle(); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(0, -100)); - await tester.pumpAndSettle(); - - expect(tester.getTopLeft(find.text('Item 499')), - const Offset(10, screenHeight - itemHeight - 10)); - }); - - testWidgets('padding test - centered sliver not at bottom', - (WidgetTester tester) async { - final itemScrollController = ItemScrollController(); - await setUpWidgetTest( - tester, - itemScrollController: itemScrollController, - initialIndex: 2, - padding: const EdgeInsets.all(10), - ); - - await tester.drag( - find.byType(ScrollablePositionedList), const Offset(0, 200)); - await tester.pumpAndSettle(); - - expect(tester.getTopLeft(find.text('Item 0')), const Offset(10, 10)); - expect(tester.getTopLeft(find.text('Item 2')), - const Offset(10, 10 + itemHeight * 2)); - expect(tester.getTopLeft(find.text('Item 3')), - const Offset(10, 10 + itemHeight * 3)); - }); -} diff --git a/packages/stream_chat_flutter/test/src/ai_assistant/ai_typing_indicator_view_test.dart b/packages/stream_chat_flutter/test/src/ai_assistant/ai_typing_indicator_view_test.dart deleted file mode 100644 index 9e14a993c9..0000000000 --- a/packages/stream_chat_flutter/test/src/ai_assistant/ai_typing_indicator_view_test.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/src/ai_assistant/ai_typing_indicator_view.dart'; - -void main() { - group('AITypingIndicatorView', () { - testWidgets('displays the provided text', (WidgetTester tester) async { - const dotCount = 5; - const testText = 'AI is thinking'; - - await tester.pumpWidget( - const MaterialApp( - home: Scaffold( - body: AITypingIndicatorView( - text: testText, - dotCount: dotCount, - ), - ), - ), - ); - - await tester.pump(const Duration(milliseconds: 200 * dotCount)); - - expect(find.text(testText), findsOneWidget); - }); - - testWidgets('applies the provided textStyle', (WidgetTester tester) async { - const dotCount = 5; - const testText = 'AI is thinking'; - const textStyle = TextStyle(fontSize: 20, color: Colors.blue); - - await tester.pumpWidget( - const MaterialApp( - home: Scaffold( - body: AITypingIndicatorView( - text: testText, - textStyle: textStyle, - dotCount: dotCount, - ), - ), - ), - ); - - await tester.pump(const Duration(milliseconds: 200 * dotCount)); - - final textWidget = tester.widget(find.text(testText)); - expect(textWidget.style?.fontSize, equals(20)); - expect(textWidget.style?.color, equals(Colors.blue)); - }); - }); -} diff --git a/packages/stream_chat_flutter/test/src/ai_assistant/streaming_message_view_test.dart b/packages/stream_chat_flutter/test/src/ai_assistant/streaming_message_view_test.dart deleted file mode 100644 index 1236a138ab..0000000000 --- a/packages/stream_chat_flutter/test/src/ai_assistant/streaming_message_view_test.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/src/ai_assistant/streaming_message_view.dart'; - -void main() { - group('StreamingMessageView Tests', () { - testWidgets( - 'displays initial text', - (WidgetTester tester) async { - const testText = 'Hello, world!'; - - await tester.pumpWidget( - const MaterialApp( - home: Scaffold( - body: StreamingMessageView(text: testText), - ), - ), - ); - - expect(find.text(testText), findsOneWidget); - }, - ); - - testWidgets( - 'updates text progressively like a typewriter', - (WidgetTester tester) async { - const testText = 'Hello, world!'; - const typingSpeed = Duration(milliseconds: 20); - - await tester.pumpWidget( - const MaterialApp( - home: Scaffold( - body: StreamingMessageView( - text: testText, - typingSpeed: typingSpeed, - ), - ), - ), - ); - - expect(find.text(testText), findsOneWidget); - - const updatedText = 'Hello, world! How are you?'; - await tester.pumpWidget( - const MaterialApp( - home: Scaffold( - body: StreamingMessageView( - text: updatedText, - typingSpeed: typingSpeed, - ), - ), - ), - ); - - await tester.pump(typingSpeed * updatedText.length); - - expect(find.text(updatedText), findsOneWidget); - }, - ); - - testWidgets( - 'handles links correctly', - (WidgetTester tester) async { - const testText = '[Click me](https://example.com)'; - const typingSpeed = Duration(milliseconds: 20); - String? tappedLink; - - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: StreamingMessageView( - text: testText, - typingSpeed: typingSpeed, - onTapLink: (String link, String? href, String title) { - tappedLink = href; - }, - ), - ), - ), - ); - - await tester.pump(typingSpeed * testText.length); - - final linkFinder = find.text('Click me'); - expect(linkFinder, findsOneWidget); - await tester.tap(linkFinder); - - expect(tappedLink, equals('https://example.com')); - }, - ); - }); -} diff --git a/packages/stream_chat_flutter/test/src/attachment/attachment_handler_test.dart b/packages/stream_chat_flutter/test/src/attachment/attachment_handler_test.dart deleted file mode 100644 index 6765434452..0000000000 --- a/packages/stream_chat_flutter/test/src/attachment/attachment_handler_test.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - group('AttachmentHandler Downloads', () { - test('AttachmentHandler downloads image', () async { - final attachment = Attachment( - title: 'test image attachment', - type: 'image', - extraData: const { - 'mime_type': 'png', - }, - ); - - final attachmentHandler = MockAttachmentHandler(); - - when(() => attachmentHandler.downloadAttachment(attachment)) - .thenAnswer((invocation) async => 'filePath'); - - expect( - await attachmentHandler.downloadAttachment(attachment), - 'filePath', - ); - }); - - test('AttachmentHandler downloads giphy', () async { - final attachment = Attachment( - title: 'test giphy attachment', - type: 'giphy', - extraData: const { - 'original': - 'https://giphy.com/gifs/nrkp3-dance-happy-3o7TKnCdBx5cMg0qti', - }, - ); - - final attachmentHandler = MockAttachmentHandler(); - - when(() => attachmentHandler.downloadAttachment(attachment)) - .thenAnswer((invocation) async => 'filePath'); - - expect( - await attachmentHandler.downloadAttachment(attachment), - 'filePath', - ); - }); - - test('AttachmentHandler downloads video', () async { - final attachment = Attachment( - title: 'test video attachment', - type: 'video', - assetUrl: 'https://www.youtube.com/watch?v=lytQi-slT5Y', - ); - - final attachmentHandler = MockAttachmentHandler(); - - when(() => attachmentHandler.downloadAttachment(attachment)) - .thenAnswer((invocation) async => 'filePath'); - - expect( - await attachmentHandler.downloadAttachment(attachment), - 'filePath', - ); - }); - }); -} diff --git a/packages/stream_chat_flutter/test/src/attachment/attachment_upload_state_builder_test.dart b/packages/stream_chat_flutter/test/src/attachment/attachment_upload_state_builder_test.dart deleted file mode 100644 index f87bcdedf5..0000000000 --- a/packages/stream_chat_flutter/test/src/attachment/attachment_upload_state_builder_test.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - testWidgets( - 'AttachmentUploadStateBuilder returns Offstage when message is sent', - (tester) async { - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: StreamChannel( - channel: MockChannel(), - child: StreamAttachmentUploadStateBuilder( - attachment: Attachment( - id: 'test', - ), - message: Message( - id: 'test', - ), - ), - ), - ), - ), - ), - ); - - expect(find.byType(Offstage), findsOneWidget); - }); -} diff --git a/packages/stream_chat_flutter/test/src/attachment/builder/voice_recording_attachment_playlist_builder.dart b/packages/stream_chat_flutter/test/src/attachment/builder/voice_recording_attachment_playlist_builder.dart deleted file mode 100644 index eef7c8279e..0000000000 --- a/packages/stream_chat_flutter/test/src/attachment/builder/voice_recording_attachment_playlist_builder.dart +++ /dev/null @@ -1,86 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -void main() { - group( - 'VoiceRecordingAttachmentPlaylistBuilder', - () { - const builder = VoiceRecordingAttachmentPlaylistBuilder(); - - test('canHandle returns true when voice recordings exist', () { - final attachments = { - AttachmentType.voiceRecording: [Attachment(), Attachment()], - }; - - expect(builder.canHandle(Message(), attachments), isTrue); - }); - - test('canHandle returns false when no voice recordings exist', () { - final attachments = >{}; - - expect(builder.canHandle(Message(), attachments), isFalse); - }); - - test('canHandle returns false for other attachment types', () { - final attachments = { - AttachmentType.image: [Attachment()], - AttachmentType.giphy: [Attachment()], - AttachmentType.video: [Attachment()], - }; - - expect(builder.canHandle(Message(), attachments), isFalse); - }); - - test('canHandle returns false when voice recording list is empty', () { - final attachments = {AttachmentType.voiceRecording: []}; - - expect(builder.canHandle(Message(), attachments), isFalse); - }); - - testWidgets( - 'build returns correct widget', - (WidgetTester tester) async { - final attachments = { - AttachmentType.voiceRecording: [Attachment(), Attachment()], - }; - - await tester.pumpWidget( - _wrapWithStreamChatApp( - Builder( - builder: (context) => builder.build( - context, - Message(), - attachments, - ), - ), - ), - ); - - expect( - find.byType(StreamVoiceRecordingAttachmentPlaylist), - findsOneWidget, - ); - }, - ); - }, - ); -} - -Widget _wrapWithStreamChatApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: Builder(builder: (context) { - final theme = StreamChatTheme.of(context); - return Scaffold( - backgroundColor: theme.colorTheme.appBg, - body: Center(child: widget), - ); - }), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/attachment/file_attachment_test.dart b/packages/stream_chat_flutter/test/src/attachment/file_attachment_test.dart deleted file mode 100644 index 39aae1d011..0000000000 --- a/packages/stream_chat_flutter/test/src/attachment/file_attachment_test.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - testWidgets( - 'Shows the file name', - (WidgetTester tester) async { - final channel = MockChannel(); - final channelState = MockChannelState(); - - when(() => channel.state).thenReturn(channelState); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - await tester.pumpWidget( - MaterialApp( - home: StreamChatTheme( - data: streamTheme, - child: StreamChannel( - channel: channel, - child: SizedBox( - child: StreamFileAttachment( - constraints: BoxConstraints.tight(const Size( - 300, - 300, - )), - message: Message(), - file: Attachment( - type: 'file', - title: 'example.pdf', - extraData: const { - 'mime_type': 'pdf', - }, - ), - ), - ), - ), - ), - ), - ); - - expect(find.text('example.pdf'), findsOneWidget); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/attachment/gallery_attachment_test.dart b/packages/stream_chat_flutter/test/src/attachment/gallery_attachment_test.dart deleted file mode 100644 index 189e1e4a33..0000000000 --- a/packages/stream_chat_flutter/test/src/attachment/gallery_attachment_test.dart +++ /dev/null @@ -1,76 +0,0 @@ -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - testWidgets( - 'Shows 2 images', - (WidgetTester tester) async { - final channel = MockChannel(); - final channelState = MockChannelState(); - - when(() => channel.state).thenReturn(channelState); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - final attachments = [ - Attachment( - type: 'image', - title: 'example.png', - imageUrl: - 'https://logowik.com/content/uploads/images/flutter5786.jpg', - extraData: const { - 'mime_type': 'png', - }, - ), - Attachment( - type: 'image', - title: 'example.png', - imageUrl: - 'https://logowik.com/content/uploads/images/flutter5786.jpg', - extraData: const { - 'mime_type': 'png', - }, - ), - ]; - - await tester.pumpWidget( - MaterialApp( - home: StreamChatTheme( - data: streamTheme, - child: StreamChannel( - channel: channel, - child: SizedBox( - child: StreamGalleryAttachment( - constraints: BoxConstraints.tight(const Size( - 300, - 300, - )), - message: Message(), - attachments: attachments, - itemBuilder: (context, index) { - final attachment = attachments[index]; - - return StreamImageAttachmentThumbnail( - image: attachment, - width: double.infinity, - height: double.infinity, - fit: BoxFit.cover, - ); - }, - ), - ), - ), - ), - ), - ); - - expect(find.byType(CachedNetworkImage), findsNWidgets(2)); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/attachment/giphy_attachment_test.dart b/packages/stream_chat_flutter/test/src/attachment/giphy_attachment_test.dart deleted file mode 100644 index 755686c711..0000000000 --- a/packages/stream_chat_flutter/test/src/attachment/giphy_attachment_test.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - testWidgets( - 'Shows GIPHY text', - (WidgetTester tester) async { - final channel = MockChannel(); - final channelState = MockChannelState(); - - when(() => channel.state).thenReturn(channelState); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - await tester.pumpWidget( - MaterialApp( - home: StreamChatTheme( - data: streamTheme, - child: StreamChannel( - channel: channel, - child: SizedBox( - child: StreamGiphyAttachment( - constraints: BoxConstraints.tight(const Size( - 300, - 300, - )), - message: Message(), - giphy: Attachment( - type: 'giphy', - title: 'example.gif', - imageUrl: - 'https://media.giphy.com/media/35H0pwQNaO2iLTnnBf/giphy.gif', - extraData: const { - 'mime_type': 'gif', - }, - ), - ), - ), - ), - ), - ), - ); - - expect(find.text('GIPHY'), findsOneWidget); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_idle_dark.png b/packages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_idle_dark.png deleted file mode 100644 index cf951c9a8d..0000000000 Binary files a/packages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_idle_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_idle_light.png b/packages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_idle_light.png deleted file mode 100644 index 247512100d..0000000000 Binary files a/packages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_idle_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_playing_dark.png b/packages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_playing_dark.png deleted file mode 100644 index 7a927e33ca..0000000000 Binary files a/packages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_playing_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_playing_light.png b/packages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_playing_light.png deleted file mode 100644 index 8e26369492..0000000000 Binary files a/packages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_playing_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_playlist_dark.png b/packages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_playlist_dark.png deleted file mode 100644 index c00c4952f6..0000000000 Binary files a/packages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_playlist_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_playlist_light.png b/packages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_playlist_light.png deleted file mode 100644 index d4e41db472..0000000000 Binary files a/packages/stream_chat_flutter/test/src/attachment/goldens/ci/stream_voice_recording_attachment_playlist_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/attachment/image_attachment_test.dart b/packages/stream_chat_flutter/test/src/attachment/image_attachment_test.dart deleted file mode 100644 index 03ec5fa8bf..0000000000 --- a/packages/stream_chat_flutter/test/src/attachment/image_attachment_test.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - testWidgets( - 'Shows the image', - (WidgetTester tester) async { - final channel = MockChannel(); - final channelState = MockChannelState(); - - when(() => channel.state).thenReturn(channelState); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - await tester.pumpWidget( - MaterialApp( - home: StreamChatTheme( - data: streamTheme, - child: StreamChannel( - channel: channel, - child: SizedBox( - child: StreamImageAttachment( - constraints: BoxConstraints.tight(const Size( - 300, - 300, - )), - message: Message(), - image: Attachment( - type: 'image', - title: 'example.png', - imageUrl: - 'https://logowik.com/content/uploads/images/flutter5786.jpg', - extraData: const { - 'mime_type': 'png', - }, - ), - ), - ), - ), - ), - ), - ); - - expect(find.byType(CachedNetworkImage), findsOneWidget); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/attachment/url_attachment_test.dart b/packages/stream_chat_flutter/test/src/attachment/url_attachment_test.dart deleted file mode 100644 index cf12e07e7d..0000000000 --- a/packages/stream_chat_flutter/test/src/attachment/url_attachment_test.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - testWidgets( - 'Shows the attachment title', - (WidgetTester tester) async { - final channel = MockChannel(); - final channelState = MockChannelState(); - - when(() => channel.state).thenReturn(channelState); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - await tester.pumpWidget( - MaterialApp( - home: StreamChatTheme( - data: streamTheme, - child: StreamChannel( - channel: channel, - child: SizedBox( - child: StreamUrlAttachment( - messageTheme: streamTheme.ownMessageTheme, - message: Message(), - hostDisplayName: 'Test', - urlAttachment: Attachment( - title: 'Flutter', - titleLink: 'https://flutter.dev', - ), - ), - ), - ), - ), - ), - ); - - expect(find.text('Flutter'), findsOneWidget); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/attachment/voice_recording_attachment_playlist_test.dart b/packages/stream_chat_flutter/test/src/attachment/voice_recording_attachment_playlist_test.dart deleted file mode 100644 index 0f6786f3bb..0000000000 --- a/packages/stream_chat_flutter/test/src/attachment/voice_recording_attachment_playlist_test.dart +++ /dev/null @@ -1,268 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/src/attachment/voice_recording_attachment.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - group('StreamVoiceRecordingAttachmentPlaylist', () { - final fakeAudioRecording1 = Attachment( - type: AttachmentType.voiceRecording, - title: 'test1.m4a', - file: AttachmentFile( - size: 10000, - path: 'voice_recordings/test1.m4a', - ), - extraData: { - 'duration': 300, - 'waveform_data': List.filled(50, 0.5), - }, - ); - - final fakeAudioRecording2 = Attachment( - type: AttachmentType.voiceRecording, - title: 'test2.m4a', - file: AttachmentFile( - size: 30000, - path: 'voice_recordings/test2.m4a', - ), - extraData: { - 'duration': 900, - 'waveform_data': List.filled(50, 0.5), - }, - ); - - testWidgets( - 'renders playlist with correct number of attachments', - (WidgetTester tester) async { - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamVoiceRecordingAttachmentPlaylist( - message: MockMessage(), - voiceRecordings: [fakeAudioRecording1, fakeAudioRecording2], - ), - ), - ); - - expect(find.byType(StreamVoiceRecordingAttachment), findsNWidgets(2)); - }, - ); - - testWidgets( - 'uses custom shape when provided', - (WidgetTester tester) async { - final customShape = RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), - ); - - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamVoiceRecordingAttachmentPlaylist( - message: MockMessage(), - voiceRecordings: [fakeAudioRecording1], - shape: customShape, - ), - ), - ); - - expect(find.byType(StreamVoiceRecordingAttachment), findsOneWidget); - - final attachment = tester.widget( - find.byType(StreamVoiceRecordingAttachment), - ); - - expect(attachment.shape, customShape); - }, - ); - - testWidgets( - 'updates playlist when recordings change', - (WidgetTester tester) async { - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamVoiceRecordingAttachmentPlaylist( - message: MockMessage(), - voiceRecordings: [fakeAudioRecording1], - ), - ), - ); - - expect(find.byType(StreamVoiceRecordingAttachment), findsOneWidget); - - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamVoiceRecordingAttachmentPlaylist( - message: MockMessage(), - // Add a new recording - voiceRecordings: [fakeAudioRecording1, fakeAudioRecording2], - ), - ), - ); - - expect(find.byType(StreamVoiceRecordingAttachment), findsNWidgets(2)); - - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamVoiceRecordingAttachmentPlaylist( - message: MockMessage(), - // Add a new recording - voiceRecordings: [fakeAudioRecording1], - ), - ), - ); - - expect(find.byType(StreamVoiceRecordingAttachment), findsNWidgets(1)); - }, - ); - - testWidgets( - 'respects provided constraints', - (WidgetTester tester) async { - const constraints = BoxConstraints( - minWidth: 100, - maxWidth: 300, - minHeight: 50, - maxHeight: 200, - ); - - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamVoiceRecordingAttachmentPlaylist( - message: MockMessage(), - voiceRecordings: [fakeAudioRecording1], - constraints: constraints, - ), - ), - ); - - expect(find.byType(StreamVoiceRecordingAttachment), findsOneWidget); - - final attachment = tester.widget( - find.byType(StreamVoiceRecordingAttachment), - ); - - expect(attachment.constraints, constraints); - }, - ); - - testWidgets( - 'respects provided padding', - (WidgetTester tester) async { - const padding = EdgeInsets.all(16); - - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamVoiceRecordingAttachmentPlaylist( - message: MockMessage(), - voiceRecordings: [fakeAudioRecording1], - padding: padding, - ), - ), - ); - - expect(find.byType(StreamVoiceRecordingAttachment), findsOneWidget); - - final playlist = tester.widget( - find.ancestor( - of: find.byType(StreamVoiceRecordingAttachment), - matching: find.byType(ListView), - ), - ); - - expect(playlist.padding, padding); - }, - ); - - testWidgets( - 'allows custom item', - (WidgetTester tester) async { - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamVoiceRecordingAttachmentPlaylist( - message: MockMessage(), - voiceRecordings: [fakeAudioRecording1], - itemBuilder: (context, index) { - return const Text('Custom Item'); - }, - ), - ), - ); - - expect(find.byType(Text), findsOneWidget); - expect(find.text('Custom Item'), findsOneWidget); - }, - ); - - testWidgets( - 'allows custom separator', - (WidgetTester tester) async { - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamVoiceRecordingAttachmentPlaylist( - message: MockMessage(), - voiceRecordings: [fakeAudioRecording1, fakeAudioRecording2], - separatorBuilder: (context, index) => const Divider( - color: Colors.red, - ), - ), - ), - ); - - expect(find.byType(Divider), findsNWidgets(1)); - }, - ); - - testWidgets( - 'handles empty voice recordings', - (WidgetTester tester) async { - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamVoiceRecordingAttachmentPlaylist( - message: MockMessage(), - voiceRecordings: const [], - ), - ), - ); - - expect(find.byType(StreamVoiceRecordingAttachment), findsNothing); - }, - ); - for (final brightness in Brightness.values) { - final theme = brightness.name; - goldenTest( - '[$theme] -> should look fine', - fileName: 'stream_voice_recording_attachment_playlist_$theme', - constraints: const BoxConstraints.tightFor(width: 412, height: 400), - builder: () => _wrapWithStreamChatApp( - brightness: brightness, - StreamVoiceRecordingAttachmentPlaylist( - message: MockMessage(), - voiceRecordings: [fakeAudioRecording1, fakeAudioRecording2], - padding: const EdgeInsets.all(16), - separatorBuilder: (context, index) => const SizedBox(height: 8), - ), - ), - ); - } - }); -} - -Widget _wrapWithStreamChatApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: Builder(builder: (context) { - final theme = StreamChatTheme.of(context); - return Scaffold( - backgroundColor: theme.colorTheme.appBg, - body: widget, - ); - }), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/attachment/voice_recording_attachment_test.dart b/packages/stream_chat_flutter/test/src/attachment/voice_recording_attachment_test.dart deleted file mode 100644 index ef95e4b220..0000000000 --- a/packages/stream_chat_flutter/test/src/attachment/voice_recording_attachment_test.dart +++ /dev/null @@ -1,297 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/src/attachment/voice_recording_attachment.dart'; -import 'package:stream_chat_flutter/src/audio/audio_playlist_state.dart'; -import 'package:stream_chat_flutter/src/misc/audio_waveform.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; -import '../utils/finders.dart'; - -void main() { - group( - 'StreamVoiceRecordingAttachment', - () { - final fakePlaylistTrack = PlaylistTrack( - title: 'test.m4a', - uri: Uri.file('voice_recordings/test.m4a'), - duration: const Duration(seconds: 30), - waveform: List.filled(50, 0.5), - ); - - testWidgets( - 'renders basic components', - (WidgetTester tester) async { - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamVoiceRecordingAttachment( - track: fakePlaylistTrack, - speed: PlaybackSpeed.regular, - ), - ), - ); - - // Verify key components are present - expect(find.byType(AudioControlButton), findsOneWidget); - expect(find.byType(StreamAudioWaveformSlider), findsOneWidget); - expect( - find.bySvgIcon(StreamSvgIcons.filetypeAudioM4a), findsOneWidget); - }, - ); - - testWidgets( - 'shows title when enabled', - (WidgetTester tester) async { - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamVoiceRecordingAttachment( - showTitle: true, - track: fakePlaylistTrack, - speed: PlaybackSpeed.regular, - ), - ), - ); - - // Verify key components are present - expect(find.text('test.m4a'), findsOneWidget); - expect(find.byType(AudioTitleText), findsOneWidget); - }, - ); - - testWidgets( - 'shows title when enabled', - (WidgetTester tester) async { - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamVoiceRecordingAttachment( - showTitle: true, - track: fakePlaylistTrack, - speed: PlaybackSpeed.regular, - ), - ), - ); - - // Verify key components are present - expect(find.text('test.m4a'), findsOneWidget); - expect(find.byType(AudioTitleText), findsOneWidget); - }, - ); - - testWidgets( - 'shows control speed button when playing', - (WidgetTester tester) async { - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamVoiceRecordingAttachment( - track: fakePlaylistTrack.copyWith(state: TrackState.playing), - speed: PlaybackSpeed.regular, - ), - ), - ); - - expect(find.text('x1.0'), findsOneWidget); - expect(find.byType(SpeedControlButton), findsOneWidget); - }, - ); - - testWidgets( - 'handles track play callback', - (WidgetTester tester) async { - for (final state in [TrackState.idle, TrackState.paused]) { - final onTrackPlay = MockVoidCallback(); - - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamVoiceRecordingAttachment( - track: fakePlaylistTrack.copyWith(state: state), - speed: PlaybackSpeed.regular, - onTrackPlay: onTrackPlay, - ), - ), - ); - - // Simulate play/pause button tap - await tester.tap(find.byType(AudioControlButton)); - - // Verify callbacks - verify(onTrackPlay).called(1); - } - }, - ); - - testWidgets( - 'handles track pause callback', - (WidgetTester tester) async { - final onTrackPause = MockVoidCallback(); - - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamVoiceRecordingAttachment( - track: fakePlaylistTrack.copyWith(state: TrackState.playing), - speed: PlaybackSpeed.regular, - onTrackPause: onTrackPause, - ), - ), - ); - - // Simulate play/pause button tap - await tester.tap(find.byType(AudioControlButton)); - - // Verify callbacks - verify(onTrackPause).called(1); - }, - ); - - testWidgets( - 'handles track seek callback', - (WidgetTester tester) async { - final onTrackSeekStart = MockValueChanged(); - final onTrackSeekChanged = MockValueChanged(); - final onTrackSeekEnd = MockValueChanged(); - - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamVoiceRecordingAttachment( - track: fakePlaylistTrack.copyWith(state: TrackState.playing), - speed: PlaybackSpeed.regular, - onTrackSeekStart: onTrackSeekStart, - onTrackSeekChanged: onTrackSeekChanged, - onTrackSeekEnd: onTrackSeekEnd, - ), - ), - ); - - final sliderFinder = find.byType(StreamAudioWaveformSlider); - final topLeft = tester.getTopLeft(sliderFinder); - final sliderSize = tester.getSize(sliderFinder); - - // Start gesture - final gesture = await tester.startGesture(topLeft); - verify(() => onTrackSeekStart(0)).called(1); - - // Move gesture to the middle of the slider - await gesture.moveBy(Offset(sliderSize.width * 0.5, 0)); - verify(() => onTrackSeekChanged(0.5)).called(1); - - // Move gesture to the end of the slider - await gesture.moveBy(Offset(sliderSize.width, 0)); - verify(() => onTrackSeekChanged(1)).called(1); - - // End gesture - await gesture.up(); - verify(() => onTrackSeekEnd(1)).called(1); - }, - ); - - testWidgets( - 'handles speed change callback', - (WidgetTester tester) async { - for (final speed in PlaybackSpeed.values) { - final onChangeSpeed = MockValueChanged(); - - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamVoiceRecordingAttachment( - track: fakePlaylistTrack.copyWith(state: TrackState.playing), - speed: speed, - onChangeSpeed: onChangeSpeed, - ), - ), - ); - - await tester.tap(find.byType(SpeedControlButton)); - verify(() => onChangeSpeed(speed.next)).called(1); - } - }, - ); - - testWidgets( - 'custom trailing builder works', - (WidgetTester tester) async { - Widget customTrailingBuilder( - BuildContext context, - PlaylistTrack track, - PlaybackSpeed speed, - ValueChanged? onChangeSpeed, - ) { - return const StreamSvgIcon(icon: StreamSvgIcons.closeSmall); - } - - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamVoiceRecordingAttachment( - track: fakePlaylistTrack, - speed: PlaybackSpeed.regular, - trailingBuilder: customTrailingBuilder, - ), - ), - ); - - // Verify custom trailing widget is rendered - expect(find.bySvgIcon(StreamSvgIcons.closeSmall), findsOneWidget); - expect(find.bySvgIcon(StreamSvgIcons.filetypeAudioM4a), findsNothing); - }, - ); - - for (final brightness in Brightness.values) { - final theme = brightness.name; - goldenTest( - '[$theme] -> should look fine in idle state', - fileName: 'stream_voice_recording_attachment_idle_$theme', - constraints: const BoxConstraints.tightFor(width: 412, height: 200), - builder: () => _wrapWithStreamChatApp( - brightness: brightness, - Padding( - padding: const EdgeInsets.all(8), - child: StreamVoiceRecordingAttachment( - showTitle: true, - track: fakePlaylistTrack, - speed: PlaybackSpeed.regular, - ), - ), - ), - ); - - goldenTest( - '[$theme] -> should look fine in playing state', - fileName: 'stream_voice_recording_attachment_playing_$theme', - constraints: const BoxConstraints.tightFor(width: 412, height: 200), - builder: () => _wrapWithStreamChatApp( - brightness: brightness, - Padding( - padding: const EdgeInsets.all(8), - child: StreamVoiceRecordingAttachment( - showTitle: true, - track: fakePlaylistTrack.copyWith( - state: TrackState.playing, - position: const Duration(seconds: 10), - ), - speed: PlaybackSpeed.regular, - ), - ), - ), - ); - } - }, - ); -} - -Widget _wrapWithStreamChatApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: Builder(builder: (context) { - final theme = StreamChatTheme.of(context); - return Scaffold( - backgroundColor: theme.colorTheme.appBg, - body: Center(child: widget), - ); - }), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/attachment_actions_modal/attachment_actions_modal_test.dart b/packages/stream_chat_flutter/test/src/attachment_actions_modal/attachment_actions_modal_test.dart deleted file mode 100644 index 08213b0981..0000000000 --- a/packages/stream_chat_flutter/test/src/attachment_actions_modal/attachment_actions_modal_test.dart +++ /dev/null @@ -1,412 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -class MockAttachmentDownloader extends Mock { - ProgressCallback? onReceiveProgress; - Completer completer = Completer(); - - Future call( - Attachment attachment, { - ProgressCallback? onReceiveProgress, - Map? queryParameters, - CancelToken? cancelToken, - bool deleteOnError = true, - Options? options, - }) { - this.onReceiveProgress = onReceiveProgress; - return completer.future; - } -} - -void main() { - setUpAll(() { - registerFallbackValue( - MaterialPageRoute(builder: (context) => const SizedBox())); - registerFallbackValue(Message()); - }); - - testWidgets( - 'it should show all the actions', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - final attachment = Attachment( - type: 'image', - title: 'text.jpg', - ); - final message = Message( - text: 'test', - user: User( - id: 'user-id', - ), - attachments: [ - attachment, - ], - ); - await tester.pumpWidget( - MaterialApp( - theme: themeData, - home: StreamChat( - streamChatThemeData: streamTheme, - client: client, - child: AttachmentActionsModal( - message: message, - attachment: attachment, - ), - ), - ), - ); - expect(find.text('Reply'), findsOneWidget); - expect(find.text('Show in Chat'), findsOneWidget); - expect(find.text('Save Image'), findsOneWidget); - expect(find.text('Delete'), findsOneWidget); - }, - ); - - testWidgets( - "it should hide delete if it's not my message", - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id2')); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - final attachment = Attachment( - type: 'image', - title: 'text.jpg', - ); - final message = Message( - text: 'test', - user: User( - id: 'user-id', - ), - attachments: [ - attachment, - ], - ); - await tester.pumpWidget( - MaterialApp( - theme: themeData, - home: StreamChat( - streamChatThemeData: streamTheme, - client: client, - child: AttachmentActionsModal( - message: message, - attachment: attachment, - ), - ), - ), - ); - expect(find.text('Reply'), findsOneWidget); - expect(find.text('Show in Chat'), findsOneWidget); - expect(find.text('Save Image'), findsOneWidget); - expect(find.text('Delete'), findsNothing); - }, - ); - - testWidgets( - 'it should show save video for videos', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - final attachment = Attachment( - type: 'video', - title: 'video.mp4', - ); - final message = Message( - text: 'test', - user: User( - id: 'user-id', - ), - attachments: [ - attachment, - ], - ); - await tester.pumpWidget( - MaterialApp( - theme: themeData, - home: StreamChat( - streamChatThemeData: streamTheme, - client: client, - child: SizedBox( - child: AttachmentActionsModal( - message: message, - attachment: attachment, - ), - ), - ), - ), - ); - expect(find.text('Save Video'), findsOneWidget); - }, - ); - - testWidgets( - 'tapping on reply should invoke callback', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - final mockCallback = MockVoidCallback(); - - final attachment = Attachment( - type: 'image', - title: 'image.jpg', - ); - final message = Message( - text: 'test', - user: User( - id: 'user-id', - ), - attachments: [ - attachment, - ], - ); - await tester.pumpWidget( - MaterialApp( - theme: themeData, - home: StreamChat( - streamChatThemeData: streamTheme, - client: client, - child: SizedBox( - child: AttachmentActionsModal( - message: message, - attachment: attachment, - onReply: mockCallback, - ), - ), - ), - ), - ); - await tester.tap(find.text('Reply')); - verify(mockCallback.call); - }, - ); - - testWidgets( - 'tapping on show in chat should call onShowMessage', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - final onShowMessage = MockVoidCallback(); - - final attachment = Attachment( - type: 'image', - title: 'image.jpg', - ); - final message = Message( - text: 'test', - user: User( - id: 'user-id', - ), - attachments: [ - attachment, - ], - ); - - await tester.pumpWidget( - MaterialApp( - theme: themeData, - home: StreamChat( - streamChatThemeData: streamTheme, - client: client, - child: SizedBox( - child: AttachmentActionsModal( - onShowMessage: onShowMessage, - message: message, - attachment: attachment, - ), - ), - ), - ), - ); - await tester.tap(find.text('Show in Chat')); - verify(onShowMessage.call).called(1); - }, - ); - - testWidgets( - 'tapping on delete in chat should remove the attachment', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final mockChannel = MockChannel(); - - when(() => mockChannel.updateMessage(any())) - .thenAnswer((_) async => UpdateMessageResponse()); - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final targetAttachment = Attachment( - type: 'image', - title: 'image.jpg', - ); - final remainingAttachment = Attachment( - type: 'image', - title: 'image.jpg', - ); - final message = Message( - text: 'test', - user: User( - id: 'user-id', - ), - attachments: [ - targetAttachment, - remainingAttachment, - ], - ); - - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: client, - child: child, - ), - home: StreamChannel( - showLoading: false, - channel: mockChannel, - child: AttachmentActionsModal( - message: message, - attachment: targetAttachment, - ), - ), - ), - ); - await tester.tap(find.text('Delete')); - verify(() => mockChannel.updateMessage(message.copyWith( - attachments: [ - message.attachments[1], - ], - ))).called(1); - }, - ); - - testWidgets( - 'tapping on delete in chat should remove the attachment if there is text', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final mockChannel = MockChannel(); - - when(() => mockChannel.updateMessage(any())) - .thenAnswer((_) async => UpdateMessageResponse()); - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final attachment = Attachment( - type: 'image', - title: 'image.jpg', - ); - final message = Message( - text: 'test', - user: User( - id: 'user-id', - ), - attachments: [ - attachment, - ], - ); - - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: client, - child: child, - ), - home: StreamChannel( - showLoading: false, - channel: mockChannel, - child: AttachmentActionsModal( - message: message, - attachment: attachment, - ), - ), - ), - ); - await tester.tap(find.text('Delete')); - verify(() => mockChannel.updateMessage(message.copyWith( - attachments: [], - ))).called(1); - }, - ); - - testWidgets( - // ignore: lines_longer_than_80_chars - "tapping on delete in chat should remove the message if that's the only attachment and there is no text", - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final mockChannel = MockChannel(); - - when(() => mockChannel.deleteMessage(any())) - .thenAnswer((_) async => EmptyResponse()); - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final attachment = Attachment( - type: 'image', - title: 'image.jpg', - ); - final message = Message( - user: User( - id: 'user-id', - ), - attachments: [ - attachment, - ], - ); - - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: client, - child: child, - ), - home: StreamChannel( - showLoading: false, - channel: mockChannel, - child: AttachmentActionsModal( - message: message, - attachment: attachment, - ), - ), - ), - ); - await tester.tap(find.text('Delete')); - verify(() => mockChannel.deleteMessage(message)).called(1); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/audio/audio_playlist_controller_test.dart b/packages/stream_chat_flutter/test/src/audio/audio_playlist_controller_test.dart deleted file mode 100644 index 2a5cb09b5c..0000000000 --- a/packages/stream_chat_flutter/test/src/audio/audio_playlist_controller_test.dart +++ /dev/null @@ -1,244 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:just_audio/just_audio.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:rxdart/rxdart.dart'; -import 'package:stream_chat_flutter/src/audio/audio_playlist_controller.dart'; -import 'package:stream_chat_flutter/src/audio/audio_playlist_state.dart'; - -class MockAudioPlayer extends Mock implements AudioPlayer {} - -class FakeAudioSource extends Fake implements AudioSource {} - -void main() { - group('StreamAudioPlaylistController', () { - late MockAudioPlayer mockPlayer; - late StreamAudioPlaylistController controller; - late PublishSubject stateController; - late PublishSubject positionController; - late PublishSubject speedController; - - final fakeTrack1 = PlaylistTrack( - title: 'test1.m4a', - uri: Uri.file('voice_recordings/test1.m4a'), - waveform: List.filled(50, 0.5), - duration: const Duration(seconds: 300), - ); - - final fakeTrack2 = PlaylistTrack( - title: 'test2.m4a', - uri: Uri.file('voice_recordings/test2.m4a'), - waveform: List.filled(50, 0.5), - duration: const Duration(seconds: 900), - ); - - Future _setAudioSource() { - return mockPlayer.setAudioSource( - any(), - initialPosition: any(named: 'initialPosition'), - ); - } - - setUpAll(() { - registerFallbackValue(Duration.zero); - registerFallbackValue(FakeAudioSource()); - }); - - setUp(() { - mockPlayer = MockAudioPlayer(); - when(() => mockPlayer.dispose()).thenAnswer((_) async {}); - - stateController = PublishSubject(); - positionController = PublishSubject(); - speedController = PublishSubject(); - - // Default mock behaviors - when(() => mockPlayer.playerStateStream) - .thenAnswer((_) => stateController.stream); - when(() => mockPlayer.positionStream) - .thenAnswer((_) => positionController.stream); - when(() => mockPlayer.speedStream) - .thenAnswer((_) => speedController.stream); - - controller = StreamAudioPlaylistController.raw( - player: mockPlayer, - state: AudioPlaylistState(tracks: [fakeTrack1, fakeTrack2]), - )..initialize(); - }); - - tearDown(() { - stateController.close(); - positionController.close(); - speedController.close(); - controller.dispose(); - }); - - test('controller initializes with correct default state', () { - expect(controller.value.tracks.length, equals(2)); - expect(controller.value.currentIndex, isNull); - expect(controller.value.speed, equals(PlaybackSpeed.regular)); - expect(controller.value.loopMode, equals(PlaylistLoopMode.off)); - }); - - group('Track Navigation', () { - test('skipToItem selects correct track', () async { - when(_setAudioSource).thenAnswer((_) async => null); - when(() => mockPlayer.play()).thenAnswer((_) async {}); - - await controller.skipToItem(1); - - expect(controller.value.currentIndex, equals(1)); - verify(_setAudioSource).called(1); - verify(() => mockPlayer.play()).called(1); - }); - - test('skipToNext cycles through tracks', () async { - when(_setAudioSource).thenAnswer((_) async => null); - when(() => mockPlayer.play()).thenAnswer((_) async {}); - - await controller.skipToItem(0); - await controller.skipToNext(); - - expect(controller.value.currentIndex, equals(1)); - verify(_setAudioSource).called(2); - verify(() => mockPlayer.play()).called(2); - }); - - test('skipToPrevious cycles through tracks', () async { - when(_setAudioSource).thenAnswer((_) async => null); - when(() => mockPlayer.play()).thenAnswer((_) async {}); - - await controller.skipToItem(1); - await controller.skipToPrevious(); - - expect(controller.value.currentIndex, equals(0)); - verify(_setAudioSource).called(2); - verify(() => mockPlayer.play()).called(2); - }); - }); - - group('Playlist Management', () { - test('updatePlaylist replaces tracks', () async { - when(() => mockPlayer.stop()).thenAnswer((_) async {}); - - final newTracks = [ - PlaylistTrack( - title: 'new-track.mp3', - uri: Uri.parse('https://example.com/new-track.mp3'), - ) - ]; - - await controller.updatePlaylist(newTracks); - - expect(controller.value.currentIndex, isNull); - expect(controller.value.tracks, equals(newTracks)); - verify(() => mockPlayer.stop()).called(1); - }); - - test('setLoopMode updates playlist loop configuration', () async { - await controller.setLoopMode(PlaylistLoopMode.all); - expect(controller.value.loopMode, equals(PlaylistLoopMode.all)); - - await controller.setLoopMode(PlaylistLoopMode.one); - expect(controller.value.loopMode, equals(PlaylistLoopMode.one)); - - await controller.setLoopMode(PlaylistLoopMode.off); - expect(controller.value.loopMode, equals(PlaylistLoopMode.off)); - }); - }); - - group('Playback Control', () { - test('play invokes audio player', () async { - when(() => mockPlayer.play()).thenAnswer((_) async {}); - - await controller.play(); - - verify(() => mockPlayer.play()).called(1); - }); - - test('pause stops playback', () async { - when(() => mockPlayer.pause()).thenAnswer((_) async {}); - - await controller.pause(); - - verify(() => mockPlayer.pause()).called(1); - }); - - test('setSpeed changes playback rate', () async { - when(() => mockPlayer.setSpeed(any())).thenAnswer((_) async {}); - - const playbackSpeed = PlaybackSpeed.faster; - await controller.setSpeed(playbackSpeed); - - verify(() => mockPlayer.setSpeed(playbackSpeed.speed)).called(1); - }); - }); - - group('Stream Interactions', () { - setUp(() { - when(_setAudioSource).thenAnswer((_) async => null); - when(() => mockPlayer.play()).thenAnswer((_) async {}); - when(() => mockPlayer.pause()).thenAnswer((_) async {}); - when(() => mockPlayer.seek(any())).thenAnswer((_) async {}); - }); - - test('playerStateStream updates track state', () async { - await controller.skipToItem(0); - - // Simulate track idle - stateController.add(PlayerState(false, ProcessingState.idle)); - await Future.delayed(Duration.zero); - expect(controller.value.tracks[0].state, equals(TrackState.idle)); - - // Simulate track loading - stateController.add(PlayerState(false, ProcessingState.loading)); - await Future.delayed(Duration.zero); - expect(controller.value.tracks[0].state, equals(TrackState.loading)); - - // Simulate track paused - stateController.add(PlayerState(false, ProcessingState.ready)); - await Future.delayed(Duration.zero); - expect(controller.value.tracks[0].state, equals(TrackState.paused)); - - // Simulate track playing - stateController.add(PlayerState(true, ProcessingState.ready)); - await Future.delayed(Duration.zero); - expect(controller.value.tracks[0].state, equals(TrackState.playing)); - }); - - test('positionStream updates track position', () async { - await controller.skipToItem(0); - - const testPosition = Duration(seconds: 30); - positionController.add(testPosition); - await Future.delayed(Duration.zero); - - expect(controller.value.tracks[0].position, equals(testPosition)); - }); - - test('speedStream updates playback speed', () async { - speedController.add(1.5); - await Future.delayed(Duration.zero); - expect(controller.value.speed, equals(PlaybackSpeed.faster)); - - speedController.add(2); - await Future.delayed(Duration.zero); - expect(controller.value.speed, equals(PlaybackSpeed.fastest)); - - speedController.add(1); - await Future.delayed(Duration.zero); - expect(controller.value.speed, equals(PlaybackSpeed.regular)); - }); - - test('track completes and auto-advances', () async { - await controller.skipToItem(0); - - // Simulate track completion - stateController.add(PlayerState(true, ProcessingState.completed)); - await Future.delayed(Duration.zero); - - // Verify auto-advance occurs - expect(controller.value.currentIndex, equals(1)); - }); - }); - }); -} diff --git a/packages/stream_chat_flutter/test/src/audio/audio_sampling_test.dart b/packages/stream_chat_flutter/test/src/audio/audio_sampling_test.dart deleted file mode 100644 index a1f2918274..0000000000 --- a/packages/stream_chat_flutter/test/src/audio/audio_sampling_test.dart +++ /dev/null @@ -1,120 +0,0 @@ -import 'dart:math'; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/src/audio/audio_sampling.dart'; - -void main() { - group('resampleWaveformData', () { - test('returns original data when target size equals input size', () { - final input = [1.0, 2.0, 3.0, 4.0]; - expect(resampleWaveformData(input, 4), equals(input)); - }); - - test('downsamples when target size is smaller', () { - final input = List.generate(10, (i) => i.toDouble()); - final result = resampleWaveformData(input, 5); - expect(result.length, equals(5)); - // First and last points should be preserved - expect(result.first, equals(input.first)); - expect(result.last, equals(input.last)); - }); - - test('upsamples when target size is larger', () { - final input = [1.0, 2.0, 3.0]; - final result = resampleWaveformData(input, 6); - expect(result.length, equals(6)); - // Check if values are repeated appropriately - expect(result.where((x) => x == 1.0).length, equals(2)); - expect(result.where((x) => x == 2.0).length, equals(2)); - expect(result.where((x) => x == 3.0).length, equals(2)); - }); - }); - - group('downSample', () { - test('handles single point target size', () { - final input = [1.0, 2.0, 3.0, 4.0]; - final result = downSample(input, 1); - expect(result.length, equals(1)); - expect(result[0], equals(2.5)); // Mean of all values - }); - - test('preserves first and last points', () { - final input = List.generate(100, (i) => i.toDouble()); - final result = downSample(input, 10); - expect(result.first, equals(input.first)); - expect(result.last, equals(input.last)); - expect(result.length, equals(10)); - }); - - test('returns original data when target size is larger', () { - final input = [1.0, 2.0, 3.0]; - expect(downSample(input, 5), equals(input)); - }); - - test('handles empty input', () { - expect(downSample([], 5), isEmpty); - }); - }); - - group('upSample', () { - test('handles empty input', () { - expect(upSample([], 5), equals([0.0, 0.0, 0.0, 0.0, 0.0])); - }); - - test('maintains original values when target size equals input size', () { - final input = [1.0, 2.0, 3.0]; - expect(upSample(input, 3), equals(input)); - }); - - test('correctly distributes remainder', () { - final input = [1.0, 2.0, 3.0]; - final result = upSample(input, 7); - expect(result.length, equals(7)); - // Check if the remainder is distributed correctly - final countMap = {}; - for (final value in result) { - countMap[value] = (countMap[value] ?? 0) + 1; - } - // Each value should appear either 2 or 3 times - expect( - countMap.values.every((count) => count == 2 || count == 3), isTrue); - }); - - test('returns original data when target size is smaller', () { - final input = [1.0, 2.0, 3.0, 4.0]; - expect(upSample(input, 2), equals(input)); - }); - }); - - group('edge cases', () { - test('handles negative values', () { - final input = [-1.0, -2.0, -3.0, -4.0]; - final result = resampleWaveformData(input, 2); - expect(result.length, equals(2)); - expect(result.every((x) => x < 0), isTrue); - }); - - test('handles repeated values', () { - final input = [1.0, 1.0, 1.0, 1.0]; - final result = resampleWaveformData(input, 2); - expect(result.length, equals(2)); - expect(result.every((x) => x == 1.0), isTrue); - }); - - test('handles very large numbers', () { - final input = List.generate(5, (i) => pow(10, i).toDouble()); - final result = resampleWaveformData(input, 3); - expect(result.length, equals(3)); - expect(result.first, equals(input.first)); - expect(result.last, equals(input.last)); - }); - - test('handles very small numbers', () { - final input = List.generate(5, (i) => pow(0.1, i).toDouble()); - final result = resampleWaveformData(input, 3); - expect(result.length, equals(3)); - expect(result.first, equals(input.first)); - expect(result.last, equals(input.last)); - }); - }); -} diff --git a/packages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_0.png b/packages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_0.png deleted file mode 100644 index dab41d6d78..0000000000 Binary files a/packages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_0.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_1.png b/packages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_1.png deleted file mode 100644 index b80de3c983..0000000000 Binary files a/packages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_1.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_2.png b/packages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_2.png deleted file mode 100644 index cd98086709..0000000000 Binary files a/packages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_2.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_3.png b/packages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_3.png deleted file mode 100644 index 3aea9bc2d1..0000000000 Binary files a/packages/stream_chat_flutter/test/src/avatars/goldens/ci/gradient_avatar_3.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/avatars/goldens/ci/group_avatar_0.png b/packages/stream_chat_flutter/test/src/avatars/goldens/ci/group_avatar_0.png deleted file mode 100644 index 447e0c39e8..0000000000 Binary files a/packages/stream_chat_flutter/test/src/avatars/goldens/ci/group_avatar_0.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/avatars/goldens/ci/user_avatar_0.png b/packages/stream_chat_flutter/test/src/avatars/goldens/ci/user_avatar_0.png deleted file mode 100644 index 4e8db61604..0000000000 Binary files a/packages/stream_chat_flutter/test/src/avatars/goldens/ci/user_avatar_0.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/avatars/goldens/ci/user_avatar_1.png b/packages/stream_chat_flutter/test/src/avatars/goldens/ci/user_avatar_1.png deleted file mode 100644 index 5a43da106d..0000000000 Binary files a/packages/stream_chat_flutter/test/src/avatars/goldens/ci/user_avatar_1.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/avatars/gradient_avatar_test.dart b/packages/stream_chat_flutter/test/src/avatars/gradient_avatar_test.dart deleted file mode 100644 index c4d1961636..0000000000 --- a/packages/stream_chat_flutter/test/src/avatars/gradient_avatar_test.dart +++ /dev/null @@ -1,115 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; -import '../mocks.dart'; - -void main() { - testWidgets( - 'control test', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: const Scaffold( - body: Center( - child: SizedBox( - width: 100, - height: 100, - child: StreamGradientAvatar( - name: 'demo user', userId: 'demo123'), - ), - ), - ), - ), - ), - ); - - expect(find.byType(StreamGradientAvatar), findsOneWidget); - }, - ); - - goldenTest( - 'golden test for the name "demo user"', - fileName: 'gradient_avatar_0', - constraints: const BoxConstraints.tightFor(width: 300, height: 300), - builder: () => MaterialAppWrapper( - home: const Scaffold( - body: Center( - child: SizedBox( - width: 100, - height: 100, - child: StreamGradientAvatar(name: 'demo user', userId: 'demo123'), - ), - ), - ), - ), - ); - - goldenTest( - 'golden test for the name "demo"', - fileName: 'gradient_avatar_1', - constraints: const BoxConstraints.tightFor(width: 300, height: 300), - builder: () => MaterialAppWrapper( - home: const Scaffold( - body: Center( - child: SizedBox( - width: 100, - height: 100, - child: StreamGradientAvatar(name: 'demo', userId: 'demo1'), - ), - ), - ), - ), - ); - - goldenTest( - 'control special character test', - fileName: 'gradient_avatar_2', - constraints: const BoxConstraints.tightFor(width: 300, height: 300), - builder: () => MaterialAppWrapper( - home: const Scaffold( - body: Center( - child: SizedBox( - width: 100, - height: 100, - child: StreamGradientAvatar( - name: r'd123@/d de:$as', - userId: 'demo123', - ), - ), - ), - ), - ), - ); - - goldenTest( - 'control special character test 2', - fileName: 'gradient_avatar_3', - constraints: const BoxConstraints.tightFor(width: 300, height: 300), - builder: () => MaterialAppWrapper( - home: const Scaffold( - body: Center( - child: SizedBox( - width: 100, - height: 100, - child: StreamGradientAvatar( - name: r'123@/d $as', - userId: 'demo123', - ), - ), - ), - ), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/avatars/group_avatar_test.dart b/packages/stream_chat_flutter/test/src/avatars/group_avatar_test.dart deleted file mode 100644 index 17f8341e64..0000000000 --- a/packages/stream_chat_flutter/test/src/avatars/group_avatar_test.dart +++ /dev/null @@ -1,130 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; -import '../mocks.dart'; - -void main() { - late MockClient client; - late MockChannel channel; - late MockChannelState channelState; - late MockMember member; - late MockUser user; - late MockMember member2; - late MockUser user2; - const methodChannel = - MethodChannel('dev.fluttercommunity.plus/connectivity_status'); - - setUpAll(() { - client = MockClient(); - channel = MockChannel(); - channelState = MockChannelState(); - member = MockMember(); - user = MockUser(); - member2 = MockMember(); - user2 = MockUser(); - - when(() => channel.state!).thenReturn(channelState); - when(() => channelState.membersStream) - .thenAnswer((_) => Stream>.value([member, member2])); - when(() => member.user).thenReturn(user); - when(() => user.name).thenReturn('user123'); - when(() => user.id).thenReturn('123'); - when(() => member2.user).thenReturn(user2); - when(() => user2.name).thenReturn('user456'); - when(() => user2.id).thenReturn('456'); - }); - - setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler( - methodChannel, - (MethodCall methodCall) async { - if (methodCall.method == 'listen') { - try { - await TestDefaultBinaryMessengerBinding - .instance.defaultBinaryMessenger - .handlePlatformMessage( - methodChannel.name, - methodChannel.codec.encodeSuccessEnvelope(['wifi']), - (_) {}, - ); - } catch (e) { - print(e); - } - } - - return null; - }, - ); - }); - - tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(methodChannel, null); - }); - - testWidgets( - 'control test', - (WidgetTester tester) async { - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - streamChatThemeData: StreamChatThemeData.light(), - child: StreamChannel( - channel: channel, - child: Scaffold( - body: Center( - child: StreamGroupAvatar( - members: [ - member, - member2, - ], - ), - ), - ), - ), - ), - ), - ); - - expect(find.byType(StreamUserAvatar), findsNWidgets(2)); - }, - ); - - goldenTest( - 'golden test for the group with "user123" and "user456"', - fileName: 'group_avatar_0', - constraints: const BoxConstraints.tightFor(width: 300, height: 300), - builder: () { - return MaterialAppWrapper( - home: StreamChat( - client: client, - streamChatThemeData: StreamChatThemeData.light(), - child: StreamChannel( - channel: channel, - child: Scaffold( - body: Center( - child: SizedBox( - width: 100, - height: 100, - child: StreamGroupAvatar( - members: [ - member, - member2, - ], - ), - ), - ), - ), - ), - ), - ); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/avatars/user_avatar_test.dart b/packages/stream_chat_flutter/test/src/avatars/user_avatar_test.dart deleted file mode 100644 index 6a95d3637e..0000000000 --- a/packages/stream_chat_flutter/test/src/avatars/user_avatar_test.dart +++ /dev/null @@ -1,109 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; -import '../mocks.dart'; - -void main() { - late MockClient client; - late MockUser user; - - setUpAll(() { - client = MockClient(); - user = MockUser(); - - when(() => user.name).thenReturn('user123'); - when(() => user.id).thenReturn('123'); - }); - - testWidgets( - 'control test', - (WidgetTester tester) async { - when(() => user.online).thenReturn(true); - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - streamChatThemeData: StreamChatThemeData.light(), - child: Builder(builder: (context) { - return Scaffold( - body: Center( - child: StreamUserAvatar( - user: user, - ), - ), - ); - }), - ), - ), - ); - - expect(find.byType(StreamUserAvatar), findsOneWidget); - }, - ); - - goldenTest( - 'golden test for online user "user123"', - fileName: 'user_avatar_0', - constraints: const BoxConstraints.tightFor(width: 300, height: 300), - builder: () { - when(() => user.online).thenReturn(true); - return MaterialAppWrapper( - builder: (context, child) { - return StreamChatConfiguration( - data: StreamChatConfigurationData(), - child: child!, - ); - }, - home: StreamChatTheme( - data: StreamChatThemeData.light(), - child: Builder( - builder: (context) { - return Scaffold( - body: Center( - child: StreamUserAvatar( - user: user, - ), - ), - ); - }, - ), - ), - ); - }, - ); - - goldenTest( - 'golden test for offline user "user123"', - fileName: 'user_avatar_1', - constraints: const BoxConstraints.tightFor(width: 300, height: 300), - builder: () { - when(() => user.online).thenReturn(false); - return MaterialAppWrapper( - builder: (context, child) { - return StreamChatConfiguration( - data: StreamChatConfigurationData(), - child: child!, - ); - }, - home: StreamChatTheme( - data: StreamChatThemeData.light(), - child: Builder( - builder: (context) { - return Scaffold( - body: Center( - child: StreamUserAvatar( - user: user, - ), - ), - ); - }, - ), - ), - ); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/bottom_sheets/attachment_modal_sheet_test.dart b/packages/stream_chat_flutter/test/src/bottom_sheets/attachment_modal_sheet_test.dart deleted file mode 100644 index 0779c67e87..0000000000 --- a/packages/stream_chat_flutter/test/src/bottom_sheets/attachment_modal_sheet_test.dart +++ /dev/null @@ -1,137 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; - -void main() { - group('AttachmentModalSheet tests', () { - testWidgets('Appears on tap', (tester) async { - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Builder(builder: (context) { - return Center( - child: ElevatedButton( - child: const Text('Show Modal'), - onPressed: () => showModalBottomSheet( - context: context, - builder: (_) => AttachmentModalSheet( - onFileTap: () {}, - onPhotoTap: () {}, - onVideoTap: () {}, - ), - ), - ), - ); - }), - ), - ), - ); - - final button = find.byType(ElevatedButton); - await tester.tap(button); - await tester.pumpAndSettle(); - expect(find.byType(AttachmentModalSheet), findsOneWidget); - expect(find.byType(ListTile), findsNWidgets(4)); - }); - - testWidgets('onPhotoTap works', (tester) async { - var called = 0; - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Builder(builder: (context) { - return Center( - child: AttachmentModalSheet( - onPhotoTap: () => called = 1, - onFileTap: () {}, - onVideoTap: () {}, - ), - ); - }), - ), - ), - ); - - expect(find.byType(AttachmentModalSheet), findsOneWidget); - final photoTile = find.widgetWithIcon(ListTile, Icons.image); - expect(photoTile, findsOneWidget); - await tester.tap(photoTile); - await tester.pumpAndSettle(); - expect(called, 1); - }); - - testWidgets('onVideoTap works', (tester) async { - var called = 0; - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Builder(builder: (context) { - return Center( - child: AttachmentModalSheet( - onPhotoTap: () {}, - onVideoTap: () => called = 1, - onFileTap: () {}, - ), - ); - }), - ), - ), - ); - - expect(find.byType(AttachmentModalSheet), findsOneWidget); - final videoTile = find.widgetWithIcon(ListTile, Icons.video_library); - expect(videoTile, findsOneWidget); - await tester.tap(videoTile); - await tester.pumpAndSettle(); - expect(called, 1); - }); - - testWidgets('onFileTap works', (tester) async { - var called = 0; - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Builder(builder: (context) { - return Center( - child: AttachmentModalSheet( - onPhotoTap: () {}, - onVideoTap: () {}, - onFileTap: () => called = 1, - ), - ); - }), - ), - ), - ); - - expect(find.byType(AttachmentModalSheet), findsOneWidget); - final fileTile = find.widgetWithIcon(ListTile, Icons.insert_drive_file); - expect(fileTile, findsOneWidget); - await tester.tap(fileTile); - await tester.pumpAndSettle(); - expect(called, 1); - }); - - goldenTest( - 'golden test for AttachmentModalSheet', - fileName: 'attachment_modal_sheet_0', - constraints: const BoxConstraints.tightFor(width: 300, height: 300), - builder: () => MaterialAppWrapper( - home: Scaffold( - body: Builder(builder: (context) { - return Center( - child: AttachmentModalSheet( - onPhotoTap: () {}, - onVideoTap: () {}, - onFileTap: () {}, - ), - ); - }), - ), - ), - ); - }); -} diff --git a/packages/stream_chat_flutter/test/src/bottom_sheets/edit_message_sheet_test.dart b/packages/stream_chat_flutter/test/src/bottom_sheets/edit_message_sheet_test.dart deleted file mode 100644 index 571fe4c59c..0000000000 --- a/packages/stream_chat_flutter/test/src/bottom_sheets/edit_message_sheet_test.dart +++ /dev/null @@ -1,94 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; -import '../mocks.dart'; - -void main() { - group('EditMessageSheet tests', () { - const methodChannel = - MethodChannel('dev.fluttercommunity.plus/connectivity_status'); - setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(methodChannel, - (MethodCall methodCall) async { - if (methodCall.method == 'listen') { - try { - await TestDefaultBinaryMessengerBinding - .instance.defaultBinaryMessenger - .handlePlatformMessage( - methodChannel.name, - methodChannel.codec.encodeSuccessEnvelope(['wifi']), - (_) {}, - ); - } catch (e) { - print(e); - } - } - return null; - }); - }); - - testWidgets('appears on tap', (tester) async { - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: MockClient(), - child: child, - ), - home: Scaffold( - body: Builder( - builder: (context) { - return Center( - child: ElevatedButton( - child: const Text('Show Modal'), - onPressed: () => showModalBottomSheet( - context: context, - builder: (_) => EditMessageSheet( - channel: MockChannel(), - message: Message(id: 'msg123', text: 'Hello World!'), - ), - ), - ), - ); - }, - ), - ), - ), - ); - - final button = find.byType(ElevatedButton); - await tester.tap(button); - await tester.pumpAndSettle(); - expect(find.byType(EditMessageSheet), findsOneWidget); - expect(find.text('Edit Message'), findsOneWidget); - expect(find.byType(StreamMessageInput), findsOneWidget); - }); - - goldenTest( - 'golden test for EditMessageSheet', - fileName: 'edit_message_sheet_0', - constraints: const BoxConstraints.tightFor(width: 300, height: 300), - builder: () => MaterialAppWrapper( - builder: (context, child) => StreamChat( - client: MockClient(), - child: child, - ), - home: Scaffold( - bottomSheet: EditMessageSheet( - channel: MockChannel(), - message: Message(id: 'msg123', text: 'Hello World!'), - ), - ), - ), - ); - - tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(methodChannel, null); - }); - }); -} diff --git a/packages/stream_chat_flutter/test/src/bottom_sheets/error_alert_sheet_test.dart b/packages/stream_chat_flutter/test/src/bottom_sheets/error_alert_sheet_test.dart deleted file mode 100644 index 7dc89683b8..0000000000 --- a/packages/stream_chat_flutter/test/src/bottom_sheets/error_alert_sheet_test.dart +++ /dev/null @@ -1,100 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; -import '../mocks.dart'; - -void main() { - group('ErrorAlertSheet tests', () { - const methodChannel = - MethodChannel('dev.fluttercommunity.plus/connectivity_status'); - - setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(methodChannel, - (MethodCall methodCall) async { - if (methodCall.method == 'listen') { - try { - await TestDefaultBinaryMessengerBinding - .instance.defaultBinaryMessenger - .handlePlatformMessage( - methodChannel.name, - methodChannel.codec.encodeSuccessEnvelope(['wifi']), - (_) {}, - ); - } catch (e) { - print(e); - } - } - return null; - }); - }); - - testWidgets('appears on error', (tester) async { - void failFunction() => throw Exception('Something went wrong'); - - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: MockClient(), - child: child, - ), - home: Scaffold( - body: Builder( - builder: (context) { - return Center( - child: ElevatedButton( - child: const Text('Show Modal'), - onPressed: () { - try { - failFunction(); - } catch (e) { - showModalBottomSheet( - context: context, - builder: (_) => ErrorAlertSheet( - errorDescription: e.toString(), - ), - ); - } - }, - ), - ); - }, - ), - ), - ), - ); - - final button = find.byType(ElevatedButton); - await tester.tap(button); - await tester.pumpAndSettle(); - expect(find.byType(ErrorAlertSheet), findsOneWidget); - expect(find.text('Something went wrong'), findsOneWidget); - }); - - goldenTest( - 'golden test for ErrorAlertSheet', - fileName: 'error_alert_sheet_0', - constraints: const BoxConstraints.tightFor(width: 300, height: 300), - builder: () => MaterialAppWrapper( - builder: (context, child) => StreamChat( - client: MockClient(), - child: child, - ), - home: const Scaffold( - bottomSheet: ErrorAlertSheet( - errorDescription: 'Something went wrong.', - ), - ), - ), - ); - - tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(methodChannel, null); - }); - }); -} diff --git a/packages/stream_chat_flutter/test/src/bottom_sheets/goldens/ci/attachment_modal_sheet_0.png b/packages/stream_chat_flutter/test/src/bottom_sheets/goldens/ci/attachment_modal_sheet_0.png deleted file mode 100644 index d08bde7123..0000000000 Binary files a/packages/stream_chat_flutter/test/src/bottom_sheets/goldens/ci/attachment_modal_sheet_0.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/bottom_sheets/goldens/ci/edit_message_sheet_0.png b/packages/stream_chat_flutter/test/src/bottom_sheets/goldens/ci/edit_message_sheet_0.png deleted file mode 100644 index 035e74551f..0000000000 Binary files a/packages/stream_chat_flutter/test/src/bottom_sheets/goldens/ci/edit_message_sheet_0.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/bottom_sheets/goldens/ci/error_alert_sheet_0.png b/packages/stream_chat_flutter/test/src/bottom_sheets/goldens/ci/error_alert_sheet_0.png deleted file mode 100644 index eecd466710..0000000000 Binary files a/packages/stream_chat_flutter/test/src/bottom_sheets/goldens/ci/error_alert_sheet_0.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/channel/channel_header_test.dart b/packages/stream_chat_flutter/test/src/channel/channel_header_test.dart deleted file mode 100644 index 2e2f2e6d70..0000000000 --- a/packages/stream_chat_flutter/test/src/channel/channel_header_test.dart +++ /dev/null @@ -1,464 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; -import '../mocks.dart'; - -void main() { - testWidgets( - 'it should show basic channel information', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final user = OwnUser(id: 'user-id'); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(user); - when(() => clientState.currentUserStream) - .thenAnswer((_) => Stream.value(user)); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.isMuted).thenReturn(false); - when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(() => channel.nameStream).thenAnswer((_) => Stream.value('test')); - when(() => channel.name).thenReturn('test'); - when(() => channel.imageStream) - .thenAnswer((i) => Stream.value('https://bit.ly/321RmWb')); - when(() => channel.image).thenReturn('https://bit.ly/321RmWb'); - when(() => channelState.unreadCount).thenReturn(1); - when(() => client.wsConnectionStatusStream) - .thenAnswer((_) => Stream.value(ConnectionStatus.connected)); - when(() => channelState.unreadCountStream) - .thenAnswer((i) => Stream.value(1)); - when(() => clientState.totalUnreadCount).thenAnswer((i) => 1); - when(() => clientState.totalUnreadCountStream) - .thenAnswer((i) => Stream.value(1)); - when(() => channelState.membersStream).thenAnswer( - (i) => Stream.value([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ) - ]), - ); - when(() => channelState.members).thenReturn([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ), - ]); - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: const Scaffold( - body: StreamChannelHeader(), - ), - ), - ), - ), - ); - - expect(find.text('test'), findsOneWidget); - expect(find.byType(StreamChannelAvatar), findsOneWidget); - expect(find.byType(StreamBackButton), findsOneWidget); - expect(find.byType(StreamChannelInfo), findsOneWidget); - }, - ); - - testWidgets( - 'it should show the InfoTile message if disconnected', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final user = OwnUser(id: 'user-id'); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(user); - when(() => clientState.currentUserStream) - .thenAnswer((_) => Stream.value(user)); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.isMuted).thenReturn(false); - when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(() => channel.nameStream).thenAnswer((_) => Stream.value('test')); - when(() => channel.name).thenReturn('test'); - when(() => channel.imageStream) - .thenAnswer((i) => Stream.value('https://bit.ly/321RmWb')); - when(() => channel.image).thenReturn('https://bit.ly/321RmWb'); - when(() => channelState.unreadCount).thenReturn(1); - when(() => channelState.unreadCountStream) - .thenAnswer((i) => Stream.value(1)); - when(() => channelState.membersStream).thenAnswer( - (i) => Stream.value([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ) - ]), - ); - when(() => channelState.members).thenReturn([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ), - ]); - when(() => client.wsConnectionStatusStream) - .thenAnswer((_) => Stream.value(ConnectionStatus.disconnected)); - when(() => client.wsConnectionStatus) - .thenReturn(ConnectionStatus.disconnected); - when(() => clientState.totalUnreadCount).thenAnswer((i) => 1); - when(() => clientState.totalUnreadCountStream) - .thenAnswer((i) => Stream.value(1)); - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: const Scaffold( - body: StreamChannelHeader( - showConnectionStateTile: true, - ), - ), - ), - ), - ), - ); - - expect( - tester - .widget(find.byType(StreamInfoTile)) - .showMessage, - true); - expect(tester.widget(find.byType(StreamInfoTile)).message, - 'Disconnected'); - }, - ); - - testWidgets( - 'it should show the InfoTile message if connecting', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final user = OwnUser(id: 'user-id'); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(user); - when(() => clientState.currentUserStream) - .thenAnswer((_) => Stream.value(user)); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.isMuted).thenReturn(false); - when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(() => channel.nameStream).thenAnswer((_) => Stream.value('test')); - when(() => channel.name).thenReturn('test'); - when(() => channel.imageStream) - .thenAnswer((i) => Stream.value('https://bit.ly/321RmWb')); - when(() => channel.image).thenReturn('https://bit.ly/321RmWb'); - when(() => channelState.unreadCount).thenReturn(1); - when(() => channelState.unreadCountStream) - .thenAnswer((i) => Stream.value(1)); - when(() => channelState.membersStream).thenAnswer( - (i) => Stream.value([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ) - ]), - ); - when(() => channelState.members).thenReturn([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ), - ]); - when(() => client.wsConnectionStatusStream) - .thenAnswer((_) => Stream.value(ConnectionStatus.connecting)); - when(() => clientState.totalUnreadCount).thenAnswer((i) => 1); - when(() => clientState.totalUnreadCountStream) - .thenAnswer((i) => Stream.value(1)); - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - showLoading: false, - child: const Scaffold( - body: StreamChannelHeader( - showConnectionStateTile: true, - ), - ), - ), - ), - ), - ); - - await tester.pump(); - - expect( - tester - .widget(find.byType(StreamInfoTile)) - .showMessage, - true); - expect(tester.widget(find.byType(StreamInfoTile)).message, - 'Reconnecting...'); - }, - ); - - testWidgets( - 'it should apply passed properties', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final user = OwnUser(id: 'user-id'); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(user); - when(() => clientState.currentUserStream) - .thenAnswer((_) => Stream.value(user)); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.isMuted).thenReturn(false); - when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ - 'name': 'test', - })); - when(() => channel.extraData).thenReturn({ - 'name': 'test', - }); - when(() => channelState.unreadCount).thenReturn(1); - when(() => channelState.unreadCountStream) - .thenAnswer((i) => Stream.value(1)); - when(() => channelState.membersStream).thenAnswer( - (i) => Stream.value([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ) - ]), - ); - when(() => channelState.members).thenReturn([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ), - ]); - when(() => client.wsConnectionStatusStream) - .thenAnswer((_) => Stream.value(ConnectionStatus.connecting)); - when(() => clientState.totalUnreadCountStream) - .thenAnswer((i) => Stream.value(1)); - - await tester.pumpWidget( - MaterialAppWrapper( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: const Scaffold( - body: StreamChannelHeader( - leading: Text('leading'), - subtitle: Text('subtitle'), - actions: [ - Text('action'), - ], - title: Text('title'), - ), - ), - ), - ), - ), - ); - - expect(find.text('test'), findsNothing); - expect(find.byType(StreamBackButton), findsNothing); - expect(find.byType(StreamChannelAvatar), findsNothing); - expect(find.byType(StreamChannelInfo), findsNothing); - expect(find.text('leading'), findsOneWidget); - expect(find.text('title'), findsOneWidget); - expect(find.text('subtitle'), findsOneWidget); - expect(find.text('action'), findsOneWidget); - }, - ); - - testWidgets( - 'showBackButton: false should hide the StreamBackButton and ' - 'showTypingIndicator: false should hide the typing indicator and ' - 'showConnectionStateTile: false should be passed to the infotile', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final user = OwnUser(id: 'user-id'); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(user); - when(() => clientState.currentUserStream) - .thenAnswer((_) => Stream.value(user)); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.isMuted).thenReturn(false); - when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(() => channel.nameStream).thenAnswer((_) => Stream.value('test')); - when(() => channel.name).thenReturn('test'); - when(() => channel.imageStream) - .thenAnswer((i) => Stream.value('https://bit.ly/321RmWb')); - when(() => channel.image).thenReturn('https://bit.ly/321RmWb'); - when(() => channelState.unreadCount).thenReturn(1); - when(() => channelState.unreadCountStream) - .thenAnswer((i) => Stream.value(1)); - when(() => channelState.membersStream).thenAnswer( - (i) => Stream.value([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ) - ]), - ); - when(() => channelState.members).thenReturn([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ), - ]); - when(() => client.wsConnectionStatusStream) - .thenAnswer((_) => Stream.value(ConnectionStatus.disconnected)); - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: const Scaffold( - body: StreamChannelHeader( - showTypingIndicator: false, - showBackButton: false, - ), - ), - ), - ), - ), - ); - - expect(find.byType(StreamBackButton), findsNothing); - expect( - tester - .widget(find.byType(StreamChannelInfo)) - .showTypingIndicator, - false, - ); - expect( - tester - .widget(find.byType(StreamInfoTile)) - .showMessage, - false); - }, - ); - - testWidgets( - 'should apply passed callbacks', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final user = OwnUser(id: 'user-id'); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(user); - when(() => clientState.currentUserStream) - .thenAnswer((_) => Stream.value(user)); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.isMuted).thenReturn(false); - when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(() => channel.nameStream).thenAnswer((_) => Stream.value('test')); - when(() => channel.name).thenReturn('test'); - when(() => channel.imageStream) - .thenAnswer((i) => Stream.value('https://bit.ly/321RmWb')); - when(() => channel.image).thenReturn('https://bit.ly/321RmWb'); - when(() => channelState.unreadCount).thenReturn(1); - when(() => channelState.unreadCountStream) - .thenAnswer((i) => Stream.value(1)); - when(() => channelState.membersStream).thenAnswer( - (i) => Stream.value([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ) - ]), - ); - when(() => channelState.members).thenReturn([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ), - ]); - when(() => client.wsConnectionStatusStream) - .thenAnswer((_) => Stream.value(ConnectionStatus.connecting)); - when(() => clientState.totalUnreadCount).thenAnswer((i) => 1); - when(() => clientState.totalUnreadCountStream) - .thenAnswer((i) => Stream.value(1)); - - var backPressed = false; - var imageTapped = false; - var titleTapped = false; - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: Scaffold( - body: StreamChannelHeader( - onBackPressed: () => backPressed = true, - onImageTap: () => imageTapped = true, - onTitleTap: () => titleTapped = true, - ), - ), - ), - ), - ), - ); - - await tester.tap(find.byType(StreamBackButton)); - await tester.tap(find.byType(StreamChannelAvatar)); - await tester.tap(find.byType(StreamChannelName)); - - expect(backPressed, true); - expect(imageTapped, true); - expect(titleTapped, true); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/channel/channel_image_test.dart b/packages/stream_chat_flutter/test/src/channel/channel_image_test.dart deleted file mode 100644 index b22a918289..0000000000 --- a/packages/stream_chat_flutter/test/src/channel/channel_image_test.dart +++ /dev/null @@ -1,229 +0,0 @@ -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - testWidgets( - 'it should show the image in channel.extraData', - (tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.nameStream).thenAnswer((_) => Stream.value('test')); - when(() => channel.name).thenReturn('test'); - when(() => channel.imageStream) - .thenAnswer((i) => Stream.value('https://bit.ly/321RmWb')); - when(() => channel.image).thenReturn('https://bit.ly/321RmWb'); - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: Scaffold( - body: StreamChannelAvatar(channel: channel), - ), - ), - ), - ), - ); - - final image = - tester.widget(find.byType(CachedNetworkImage)); - expect(image.imageUrl, 'https://bit.ly/321RmWb'); - }, - ); - - testWidgets( - 'it should show the other member image', - (tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.nameStream).thenAnswer((_) => Stream.value('test')); - when(() => channel.name).thenReturn('test'); - when(() => channel.imageStream).thenAnswer((i) => Stream.value(null)); - when(() => channel.image).thenReturn(null); - when(() => channelState.membersStream).thenAnswer( - (i) => Stream.value([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ), - Member( - userId: 'user-id2', - user: User( - id: 'user-id2', - image: 'testimage', - ), - ) - ]), - ); - when(() => channelState.members).thenReturn([ - Member( - userId: 'user-id2', - user: User( - id: 'user-id2', - image: 'testimage', - ), - ), - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ) - ]); - when(() => clientState.usersStream).thenAnswer( - (i) => Stream.value({ - 'user-id2': User( - id: 'user-id2', - image: 'testimage', - ), - }), - ); - when(() => channel.extraData).thenReturn({ - 'name': 'test', - }); - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: Scaffold( - body: StreamChannelAvatar(channel: channel), - ), - ), - ), - ), - ); - - final image = - tester.widget(find.byType(CachedNetworkImage)); - expect(image.imageUrl, 'testimage'); - }, - ); - - testWidgets( - 'it should use a groupimage if more than 2 members', - (tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final currentUser = OwnUser(id: 'user-id'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(currentUser); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.nameStream).thenAnswer((_) => Stream.value('test')); - when(() => channel.name).thenReturn('test'); - when(() => channel.imageStream).thenAnswer((i) => Stream.value(null)); - final members = [ - Member( - userId: 'user-id', - user: User( - id: 'user-id', - image: 'testimage1', - ), - ), - Member( - userId: 'user-id2', - user: User( - id: 'user-id2', - image: 'testimage2', - ), - ), - Member( - userId: 'user-id3', - user: User( - id: 'user-id3', - image: 'testimage3', - ), - ), - ]; - when(() => channelState.members).thenReturn(members); - when(() => channelState.membersStream) - .thenAnswer((_) => Stream.value(members)); - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: Scaffold( - body: StreamChannelAvatar(channel: channel), - ), - ), - ), - ), - ); - - final image = - tester.widget(find.byType(StreamGroupAvatar)); - final otherMembers = members.where((it) => it.userId != currentUser.id); - expect( - image.members.map((it) => it.user?.id), - otherMembers.map((it) => it.user?.id), - ); - }, - ); - - testWidgets( - 'using select: true should show a selection border', - (tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.nameStream).thenAnswer((_) => Stream.value('test')); - when(() => channel.name).thenReturn('test'); - when(() => channel.imageStream) - .thenAnswer((i) => Stream.value('https://bit.ly/321RmWb')); - when(() => channel.image).thenReturn('https://bit.ly/321RmWb'); - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: Scaffold( - body: StreamChannelAvatar( - channel: channel, - selected: true, - ), - ), - ), - ), - ), - ); - - expect(find.byKey(const Key('selectedImage')), findsOneWidget); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/channel/channel_list_header_test.dart b/packages/stream_chat_flutter/test/src/channel/channel_list_header_test.dart deleted file mode 100644 index 6193d08d97..0000000000 --- a/packages/stream_chat_flutter/test/src/channel/channel_list_header_test.dart +++ /dev/null @@ -1,206 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - testWidgets( - 'control test', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => client.wsConnectionStatusStream) - .thenAnswer((_) => Stream.value(ConnectionStatus.connected)); - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: const Scaffold( - body: StreamChannelListHeader(), - ), - ), - ), - ); - await tester.pumpAndSettle(); - - final userAvatar = - tester.widget(find.byType(StreamUserAvatar)); - expect(userAvatar.user, clientState.currentUser); - expect(find.byType(StreamNeumorphicButton), findsOneWidget); - expect(find.text('Stream Chat'), findsOneWidget); - }, - ); - - testWidgets( - 'it should show the InfoTile message if disconnected', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => client.wsConnectionStatusStream) - .thenAnswer((_) => Stream.value(ConnectionStatus.disconnected)); - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: const Scaffold( - body: StreamChannelListHeader( - showConnectionStateTile: true, - ), - ), - ), - ), - ); - await tester.pumpAndSettle(); - - expect(find.text('Disconnected'), findsOneWidget); - }, - ); - - testWidgets( - 'it should show the InfoTile message if connecting', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => client.wsConnectionStatusStream) - .thenAnswer((_) => Stream.value(ConnectionStatus.connecting)); - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: const Scaffold( - body: StreamChannelListHeader( - showConnectionStateTile: true, - ), - ), - ), - ), - ); - await tester.pump(); - - expect(find.text('Reconnecting...'), findsOneWidget); - }, - ); - - testWidgets( - 'it should apply passed properties', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => client.wsConnectionStatusStream) - .thenAnswer((_) => Stream.value(ConnectionStatus.connecting)); - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: Scaffold( - body: StreamChannelListHeader( - titleBuilder: (context, status, client) => const Text('TITLE'), - subtitle: const Text('SUBTITLE'), - leading: const Text('LEADING'), - actions: const [ - Text('ACTION'), - ], - client: client, - ), - ), - ), - ), - ); - await tester.pump(); - - expect(find.text('TITLE'), findsOneWidget); - expect(find.text('SUBTITLE'), findsOneWidget); - expect(find.text('LEADING'), findsOneWidget); - expect(find.text('ACTION'), findsOneWidget); - }, - ); - - testWidgets( - 'it should apply prenavigationcallback', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => client.wsConnectionStatusStream) - .thenAnswer((_) => Stream.value(ConnectionStatus.connecting)); - - var tapped = false; - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: Scaffold( - body: StreamChannelListHeader( - preNavigationCallback: () { - tapped = true; - }, - ), - ), - ), - ), - ); - await tester.pump(); - - await tester.tap(find.byType(StreamUserAvatar)); - expect(tapped, true); - }, - ); - - testWidgets( - 'it should apply passed callbacks', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => client.wsConnectionStatusStream) - .thenAnswer((_) => Stream.value(ConnectionStatus.connecting)); - - var tapped = 0; - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: Scaffold( - body: StreamChannelListHeader( - onUserAvatarTap: (u) { - tapped++; - }, - onNewChatButtonTap: () { - tapped++; - }, - ), - ), - ), - ), - ); - await tester.pump(); - - await tester.tap(find.byType(StreamUserAvatar)); - await tester.tap(find.byType(StreamNeumorphicButton)); - expect(tapped, 2); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/channel/channel_name_test.dart b/packages/stream_chat_flutter/test/src/channel/channel_name_test.dart deleted file mode 100644 index 01d622ae40..0000000000 --- a/packages/stream_chat_flutter/test/src/channel/channel_name_test.dart +++ /dev/null @@ -1,78 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - testWidgets( - 'it should show channel name', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.isMuted).thenReturn(false); - when(() => channel.isMutedStream).thenAnswer((_) => Stream.value(false)); - when(() => channel.nameStream).thenAnswer((_) => Stream.value('test')); - when(() => channel.name).thenReturn('test'); - when(() => channelState.unreadCount).thenReturn(1); - when(() => channelState.unreadCountStream) - .thenAnswer((i) => Stream.value(1)); - when(() => channelState.membersStream).thenAnswer( - (_) => Stream.value([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ) - ]), - ); - when(() => channelState.members).thenReturn([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ), - ]); - when(() => channelState.messages).thenReturn([ - Message( - text: 'hello', - user: User(id: 'other-user'), - ) - ]); - when(() => channelState.messagesStream).thenAnswer( - (i) => Stream.value([ - Message( - text: 'hello', - user: User(id: 'other-user'), - ) - ]), - ); - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: Scaffold( - body: StreamChannelName( - channel: channel, - ), - ), - ), - ), - ), - ); - - expect(find.text('test'), findsOneWidget); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/context_menu_items/download_menu_item_test.dart b/packages/stream_chat_flutter/test/src/context_menu_items/download_menu_item_test.dart deleted file mode 100644 index 23b3a4c533..0000000000 --- a/packages/stream_chat_flutter/test/src/context_menu_items/download_menu_item_test.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/src/context_menu_items/download_menu_item.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; -import '../mocks.dart'; - -void main() { - group('DownloadMenuItem tests', () { - testWidgets('renders ListTile widget', (tester) async { - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: MockClient(), - child: child, - ), - home: Scaffold( - body: Center( - child: DownloadMenuItem( - attachment: MockAttachment(), - ), - ), - ), - ), - ); - - expect(find.byType(ListTile), findsOneWidget); - }); - - goldenTest( - 'golden test for DownloadMenuItem', - fileName: 'download_menu_item_0', - constraints: const BoxConstraints.tightFor(width: 300, height: 100), - builder: () => MaterialAppWrapper( - builder: (context, child) => StreamChatTheme( - data: StreamChatThemeData.light(), - child: child!, - ), - home: Scaffold( - body: Center( - child: DownloadMenuItem( - attachment: MockAttachment(), - ), - ), - ), - ), - ); - }); -} diff --git a/packages/stream_chat_flutter/test/src/context_menu_items/goldens/ci/download_menu_item_0.png b/packages/stream_chat_flutter/test/src/context_menu_items/goldens/ci/download_menu_item_0.png deleted file mode 100644 index d4751f48c5..0000000000 Binary files a/packages/stream_chat_flutter/test/src/context_menu_items/goldens/ci/download_menu_item_0.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/context_menu_items/goldens/ci/stream_chat_context_menu_item_0.png b/packages/stream_chat_flutter/test/src/context_menu_items/goldens/ci/stream_chat_context_menu_item_0.png deleted file mode 100644 index 1cfb96377a..0000000000 Binary files a/packages/stream_chat_flutter/test/src/context_menu_items/goldens/ci/stream_chat_context_menu_item_0.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/context_menu_items/stream_chat_context_menu_item_test.dart b/packages/stream_chat_flutter/test/src/context_menu_items/stream_chat_context_menu_item_test.dart deleted file mode 100644 index 798e649cfb..0000000000 --- a/packages/stream_chat_flutter/test/src/context_menu_items/stream_chat_context_menu_item_test.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/src/context_menu_items/stream_chat_context_menu_item.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; -import '../mocks.dart'; - -void main() { - group('StreamChatContextMenuItem tests', () { - testWidgets('renders ListTile widget', (tester) async { - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: MockClient(), - child: child, - ), - home: const Scaffold( - body: Center( - child: StreamChatContextMenuItem(), - ), - ), - ), - ); - - expect(find.byType(ListTile), findsOneWidget); - }); - - goldenTest( - 'golden test for StreamChatContextMenuItem', - fileName: 'stream_chat_context_menu_item_0', - constraints: const BoxConstraints.tightFor(width: 300, height: 80), - builder: () => MaterialAppWrapper( - builder: (context, child) => StreamChatTheme( - data: StreamChatThemeData.light(), - child: child!, - ), - home: Scaffold( - body: Center( - child: StreamChatContextMenuItem( - leading: const Icon(Icons.download), - title: const Text('Download'), - onClick: () {}, - ), - ), - ), - ), - ); - }); -} diff --git a/packages/stream_chat_flutter/test/src/dialogs/channel_info_dialog_test.dart b/packages/stream_chat_flutter/test/src/dialogs/channel_info_dialog_test.dart deleted file mode 100644 index 666de07ae8..0000000000 --- a/packages/stream_chat_flutter/test/src/dialogs/channel_info_dialog_test.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/src/dialogs/channel_info_dialog.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - late MockClient client; - late MockClientState clientState; - late MockOwnUser user; - late MockChannel channel; - late MockChannelState channelState; - - setUpAll(() { - client = MockClient(); - clientState = MockClientState(); - user = MockOwnUser(); - channel = MockChannel(); - channelState = MockChannelState(); - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(user); - when(() => user.id).thenReturn('1'); - when(() => channel.state).thenReturn(channelState); - when(() => channelState.members).thenReturn([ - Member( - user: User( - id: '1', - ), - ), - Member( - user: User( - id: '2', - ), - ), - ]); - when(() => channel.name).thenReturn('test-channel'); - when(() => channel.id).thenReturn('123456789'); - when(() => channel.isDistinct).thenReturn(true); - when(() => channel.memberCount).thenReturn(2); - when(() => channelState.membersStream).thenAnswer( - (_) => Stream.value([ - Member( - user: User( - id: '1', - ), - ), - Member( - user: User( - id: '2', - ), - ), - ]), - ); - }); - - testWidgets('ChannelInfoDialog shows info and members', (tester) async { - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - streamChatThemeData: StreamChatThemeData.light(), - child: Scaffold( - body: Center( - child: ChannelInfoDialog( - channel: channel, - ), - ), - ), - ), - ), - ); - - expect(find.byType(SimpleDialog), findsOneWidget); - expect(find.byType(StreamChannelInfo), findsOneWidget); - expect(find.byType(StreamUserAvatar), findsOneWidget); - }); -} diff --git a/packages/stream_chat_flutter/test/src/dialogs/confirmation_dialog_test.dart b/packages/stream_chat_flutter/test/src/dialogs/confirmation_dialog_test.dart deleted file mode 100644 index 27e74c5892..0000000000 --- a/packages/stream_chat_flutter/test/src/dialogs/confirmation_dialog_test.dart +++ /dev/null @@ -1,72 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/src/dialogs/confirmation_dialog.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; - -void main() { - group('ConfirmationDialog tests', () { - testWidgets('renders with title, prompt, and action', (tester) async { - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Builder( - builder: (context) { - return Center( - child: StreamChatTheme( - data: StreamChatThemeData.light(), - child: ConfirmationDialog( - titleText: context.translations - .toggleMuteUnmuteUserText(isMuted: false), - promptText: context.translations - .toggleMuteUnmuteUserQuestion(isMuted: false), - affirmativeText: context.translations - .toggleMuteUnmuteAction(isMuted: false), - onConfirmation: () {}, - ), - ), - ); - }, - ), - ), - ), - ); - - expect(find.byType(AlertDialog), findsOneWidget); - expect(find.text('Mute User'), findsOneWidget); - expect(find.text('Are you sure you want to mute this user?'), - findsOneWidget); - expect(find.text('MUTE'), findsOneWidget); - }); - - goldenTest( - 'golden test for ConfirmationDialog', - fileName: 'confirmation_dialog_0', - constraints: const BoxConstraints.tightFor(width: 400, height: 300), - builder: () => MaterialAppWrapper( - home: Scaffold( - body: Builder( - builder: (context) { - return Center( - child: StreamChatTheme( - data: StreamChatThemeData.light(), - child: ConfirmationDialog( - titleText: context.translations - .toggleMuteUnmuteUserText(isMuted: false), - promptText: context.translations - .toggleMuteUnmuteUserQuestion(isMuted: false), - affirmativeText: context.translations - .toggleMuteUnmuteAction(isMuted: false), - onConfirmation: () {}, - ), - ), - ); - }, - ), - ), - ), - ); - }); -} diff --git a/packages/stream_chat_flutter/test/src/dialogs/delete_message_dialog_test.dart b/packages/stream_chat_flutter/test/src/dialogs/delete_message_dialog_test.dart deleted file mode 100644 index f1f1547708..0000000000 --- a/packages/stream_chat_flutter/test/src/dialogs/delete_message_dialog_test.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/src/dialogs/delete_message_dialog.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; - -void main() { - group('DeleteMessageDialog tests', () { - testWidgets('renders with correct title and actions', (tester) async { - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Builder( - builder: (context) { - return Center( - child: StreamChatTheme( - data: StreamChatThemeData.light(), - child: const DeleteMessageDialog(), - ), - ); - }, - ), - ), - ), - ); - - expect(find.byType(AlertDialog), findsOneWidget); - expect(find.text('Delete Message'), findsOneWidget); - expect(find.text('DELETE'), findsOneWidget); - }); - - goldenTest( - 'golden test for DeleteMessageDialog', - fileName: 'delete_message_dialog_0', - constraints: const BoxConstraints.tightFor(width: 400, height: 300), - builder: () => MaterialAppWrapper( - home: Scaffold( - body: Builder( - builder: (context) { - return Center( - child: StreamChatTheme( - data: StreamChatThemeData.light(), - child: const DeleteMessageDialog(), - ), - ); - }, - ), - ), - ), - ); - }); -} diff --git a/packages/stream_chat_flutter/test/src/dialogs/goldens/ci/confirmation_dialog_0.png b/packages/stream_chat_flutter/test/src/dialogs/goldens/ci/confirmation_dialog_0.png deleted file mode 100644 index 28fd090881..0000000000 Binary files a/packages/stream_chat_flutter/test/src/dialogs/goldens/ci/confirmation_dialog_0.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/dialogs/goldens/ci/delete_message_dialog_0.png b/packages/stream_chat_flutter/test/src/dialogs/goldens/ci/delete_message_dialog_0.png deleted file mode 100644 index 9314acdc86..0000000000 Binary files a/packages/stream_chat_flutter/test/src/dialogs/goldens/ci/delete_message_dialog_0.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/dialogs/goldens/ci/message_dialog_0.png b/packages/stream_chat_flutter/test/src/dialogs/goldens/ci/message_dialog_0.png deleted file mode 100644 index e6bf309bc3..0000000000 Binary files a/packages/stream_chat_flutter/test/src/dialogs/goldens/ci/message_dialog_0.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/dialogs/goldens/ci/message_dialog_1.png b/packages/stream_chat_flutter/test/src/dialogs/goldens/ci/message_dialog_1.png deleted file mode 100644 index 58f5634347..0000000000 Binary files a/packages/stream_chat_flutter/test/src/dialogs/goldens/ci/message_dialog_1.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/dialogs/goldens/ci/message_dialog_2.png b/packages/stream_chat_flutter/test/src/dialogs/goldens/ci/message_dialog_2.png deleted file mode 100644 index e6bf309bc3..0000000000 Binary files a/packages/stream_chat_flutter/test/src/dialogs/goldens/ci/message_dialog_2.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/dialogs/message_dialog_test.dart b/packages/stream_chat_flutter/test/src/dialogs/message_dialog_test.dart deleted file mode 100644 index 5a44836080..0000000000 --- a/packages/stream_chat_flutter/test/src/dialogs/message_dialog_test.dart +++ /dev/null @@ -1,126 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/src/dialogs/message_dialog.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; - -void main() { - group('MessageDialog tests', () { - testWidgets('shows default info', (tester) async { - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Builder( - builder: (context) { - return Center( - child: StreamChatTheme( - data: StreamChatThemeData.light(), - child: const MessageDialog(), - ), - ); - }, - ), - ), - ), - ); - - expect(find.byType(AlertDialog), findsOneWidget); - expect(find.text('Something went wrong'), findsOneWidget); - expect(find.text('OK'), findsOneWidget); - }); - - testWidgets('shows custom info', (tester) async { - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Builder( - builder: (context) { - return Center( - child: StreamChatTheme( - data: StreamChatThemeData.light(), - child: const MessageDialog( - titleText: 'Message', - messageText: 'Message body', - ), - ), - ); - }, - ), - ), - ), - ); - - expect(find.byType(AlertDialog), findsOneWidget); - expect(find.text('Message'), findsOneWidget); - expect(find.text('Message body'), findsOneWidget); - expect(find.text('OK'), findsOneWidget); - }); - - goldenTest( - 'golden test for default MessageDialog', - fileName: 'message_dialog_0', - constraints: const BoxConstraints.tightFor(width: 400, height: 300), - builder: () => MaterialAppWrapper( - home: Scaffold( - body: Builder( - builder: (context) { - return Center( - child: StreamChatTheme( - data: StreamChatThemeData.light(), - child: const MessageDialog(), - ), - ); - }, - ), - ), - ), - ); - - goldenTest( - 'golden test for custom MessageDialog', - fileName: 'message_dialog_1', - constraints: const BoxConstraints.tightFor(width: 400, height: 300), - builder: () => MaterialAppWrapper( - home: Scaffold( - body: Builder( - builder: (context) { - return Center( - child: StreamChatTheme( - data: StreamChatThemeData.light(), - child: const MessageDialog( - titleText: 'Message', - messageText: 'Message body', - ), - ), - ); - }, - ), - ), - ), - ); - - goldenTest( - 'golden test for custom MessageDialog with no body', - fileName: 'message_dialog_2', - constraints: const BoxConstraints.tightFor(width: 400, height: 300), - builder: () => MaterialAppWrapper( - home: Scaffold( - body: Builder( - builder: (context) { - return Center( - child: StreamChatTheme( - data: StreamChatThemeData.light(), - child: const MessageDialog( - titleText: 'Message', - ), - ), - ); - }, - ), - ), - ), - ); - }); -} diff --git a/packages/stream_chat_flutter/test/src/fakes.dart b/packages/stream_chat_flutter/test/src/fakes.dart deleted file mode 100644 index 40e7f1fd2d..0000000000 --- a/packages/stream_chat_flutter/test/src/fakes.dart +++ /dev/null @@ -1,103 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -const String kTemporaryPath = 'temporaryPath'; -const String kApplicationSupportPath = 'applicationSupportPath'; -const String kDownloadsPath = 'downloadsPath'; -const String kLibraryPath = 'libraryPath'; -const String kApplicationDocumentsPath = 'applicationDocumentsPath'; -const String kExternalCachePath = 'externalCachePath'; -const String kExternalStoragePath = 'externalStoragePath'; - -class FakePathProviderPlatform extends Fake - with MockPlatformInterfaceMixin - implements PathProviderPlatform { - @override - Future getTemporaryPath() async { - return kTemporaryPath; - } - - @override - Future getApplicationSupportPath() async { - return kApplicationSupportPath; - } - - @override - Future getLibraryPath() async { - return kLibraryPath; - } - - @override - Future getApplicationDocumentsPath() async { - return kApplicationDocumentsPath; - } - - @override - Future getExternalStoragePath() async { - return kExternalStoragePath; - } - - @override - Future?> getExternalCachePaths() async { - return [kExternalCachePath]; - } - - @override - Future?> getExternalStoragePaths({ - StorageDirectory? type, - }) async { - return [kExternalStoragePath]; - } - - @override - Future getDownloadsPath() async { - return kDownloadsPath; - } -} - -class AllNullFakePathProviderPlatform extends Fake - with MockPlatformInterfaceMixin - implements PathProviderPlatform { - @override - Future getTemporaryPath() async { - return null; - } - - @override - Future getApplicationSupportPath() async { - return null; - } - - @override - Future getLibraryPath() async { - return null; - } - - @override - Future getApplicationDocumentsPath() async { - return null; - } - - @override - Future getExternalStoragePath() async { - return null; - } - - @override - Future?> getExternalCachePaths() async { - return null; - } - - @override - Future?> getExternalStoragePaths({ - StorageDirectory? type, - }) async { - return null; - } - - @override - Future getDownloadsPath() async { - return null; - } -} diff --git a/packages/stream_chat_flutter/test/src/full_screen_media/full_screen_media_test.dart b/packages/stream_chat_flutter/test/src/full_screen_media/full_screen_media_test.dart deleted file mode 100644 index 4fafdbf3f5..0000000000 --- a/packages/stream_chat_flutter/test/src/full_screen_media/full_screen_media_test.dart +++ /dev/null @@ -1,105 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:photo_view/photo_view.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - testWidgets( - 'it should show channel typing', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.isMuted).thenReturn(false); - when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(() => channel.extraDataStream).thenAnswer( - (i) => Stream.value({ - 'name': 'test', - }), - ); - when(() => channel.extraData).thenReturn({ - 'name': 'test', - }); - when(() => channelState.membersStream).thenAnswer( - (i) => Stream.value([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ) - ]), - ); - when(() => channelState.members).thenReturn([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ), - ]); - when(() => channelState.messages).thenReturn([ - Message( - text: 'hello', - user: User(id: 'other-user'), - ) - ]); - when(() => channelState.messagesStream).thenAnswer( - (i) => Stream.value([ - Message( - text: 'hello', - user: User(id: 'other-user'), - ) - ]), - ); - when(() => channelState.typingEvents).thenAnswer((i) => { - User(id: 'other-user', extraData: const {'name': 'demo'}): - Event(type: EventType.typingStart), - }); - when(() => channelState.typingEventsStream).thenAnswer( - (i) => Stream.value({ - User(id: 'other-user', extraData: const {'name': 'demo'}): - Event(type: EventType.typingStart), - }), - ); - - final attachment = Attachment( - type: 'image', - title: 'demo image', - imageUrl: '', - ); - final message = Message( - createdAt: DateTime.now(), - attachments: [ - attachment, - ], - ); - await tester.pumpWidget(MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: StreamFullScreenMedia( - mediaAttachmentPackages: [ - StreamAttachmentPackage( - attachment: attachment, - message: message, - ), - ], - ), - ), - ), - )); - - expect(find.byType(PhotoView), findsOneWidget); - expect(find.byType(StreamSvgIcon), findsNWidgets(4)); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/gallery/gallery_footer_test.dart b/packages/stream_chat_flutter/test/src/gallery/gallery_footer_test.dart deleted file mode 100644 index bd74265fb0..0000000000 --- a/packages/stream_chat_flutter/test/src/gallery/gallery_footer_test.dart +++ /dev/null @@ -1,115 +0,0 @@ -import 'package:alchemist/alchemist.dart'; // Changed from golden_toolkit -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; -import '../mocks.dart'; - -void main() { - late MockClient client; - late MockClientState clientState; - late MockChannel channel; - late MockChannelState channelState; - const methodChannel = - MethodChannel('dev.fluttercommunity.plus/connectivity_status'); - - setUpAll(() { - client = MockClient(); - clientState = MockClientState(); - channel = MockChannel(); - channelState = MockChannelState(); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.isMuted).thenReturn(false); - when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(() => channel.extraDataStream).thenAnswer( - (i) => Stream.value({ - 'name': 'test', - }), - ); - when(() => channel.extraData).thenReturn({ - 'name': 'test', - }); - }); - - setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(methodChannel, (MethodCall methodCall) async { - if (methodCall.method == 'listen') { - try { - await TestDefaultBinaryMessengerBinding - .instance.defaultBinaryMessenger - .handlePlatformMessage( - methodChannel.name, - methodChannel.codec.encodeSuccessEnvelope(['wifi']), - (_) {}, - ); - } catch (e) { - print(e); - } - } - return null; - }); - }); - - testWidgets( - 'it should show channel typing', - (WidgetTester tester) async { - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: PopScope( - onPopInvokedWithResult: (bool didPop, res) async => false, - child: const Scaffold( - body: StreamGalleryFooter( - mediaAttachmentPackages: [], - ), - ), - ), - ), - ), - ), - ); - - expect(find.byType(StreamSvgIcon), findsNWidgets(2)); - }, - ); - - goldenTest( - 'golden test for GalleryFooter', - fileName: 'gallery_footer_0', - constraints: const BoxConstraints.tightFor(width: 400, height: 300), - builder: () => MaterialAppWrapper( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: PopScope( - onPopInvokedWithResult: (bool didPop, res) async => false, - child: const Scaffold( - bottomNavigationBar: StreamGalleryFooter( - mediaAttachmentPackages: [], - ), - ), - ), - ), - ), - ), - ); - - tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(methodChannel, null); - }); -} diff --git a/packages/stream_chat_flutter/test/src/gallery/gallery_header_test.dart b/packages/stream_chat_flutter/test/src/gallery/gallery_header_test.dart deleted file mode 100644 index 20fa9a8815..0000000000 --- a/packages/stream_chat_flutter/test/src/gallery/gallery_header_test.dart +++ /dev/null @@ -1,121 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; -import '../mocks.dart'; - -void main() { - late MockClient client; - late MockClientState clientState; - late MockChannel channel; - late MockChannelState channelState; - const methodChannel = - MethodChannel('dev.fluttercommunity.plus/connectivity_status'); - - setUpAll(() { - client = MockClient(); - clientState = MockClientState(); - channel = MockChannel(); - channelState = MockChannelState(); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.isMuted).thenReturn(false); - when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(() => channel.extraDataStream).thenAnswer( - (i) => Stream.value({ - 'name': 'test', - }), - ); - when(() => channel.extraData).thenReturn({ - 'name': 'test', - }); - }); - - setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(methodChannel, (MethodCall methodCall) async { - if (methodCall.method == 'listen') { - try { - await TestDefaultBinaryMessengerBinding - .instance.defaultBinaryMessenger - .handlePlatformMessage( - methodChannel.name, - methodChannel.codec.encodeSuccessEnvelope(['wifi']), - (_) {}, - ); - } catch (e) { - print(e); - } - } - return null; - }); - }); - - testWidgets( - 'it should show channel typing', - (WidgetTester tester) async { - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: PopScope( - onPopInvokedWithResult: (bool didPop, res) async => false, - child: Scaffold( - appBar: StreamGalleryHeader( - attachment: MockAttachment(), - message: Message(), - ), - ), - ), - ), - ), - ), - ); - - expect(find.byType(StreamSvgIcon), findsNWidgets(2)); - }, - ); - - goldenTest( - 'golden test for GalleryHeader', - fileName: 'gallery_header_0', - constraints: const BoxConstraints.tightFor(width: 300, height: 300), - builder: () { - return MaterialAppWrapper( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: PopScope( - onPopInvokedWithResult: (bool didPop, res) async => false, - child: Scaffold( - appBar: StreamGalleryHeader( - userName: 'User', - sentAt: '12:02 AM', - message: Message(), - attachment: MockAttachment(), - ), - ), - ), - ), - ), - ); - }, - ); - - tearDown(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(methodChannel, null); - }); -} diff --git a/packages/stream_chat_flutter/test/src/gallery/goldens/ci/gallery_footer_0.png b/packages/stream_chat_flutter/test/src/gallery/goldens/ci/gallery_footer_0.png deleted file mode 100644 index 32f5a634f1..0000000000 Binary files a/packages/stream_chat_flutter/test/src/gallery/goldens/ci/gallery_footer_0.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/gallery/goldens/ci/gallery_header_0.png b/packages/stream_chat_flutter/test/src/gallery/goldens/ci/gallery_header_0.png deleted file mode 100644 index e4d2e9a34f..0000000000 Binary files a/packages/stream_chat_flutter/test/src/gallery/goldens/ci/gallery_header_0.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/icons/goldens/ci/stream_svg_icon_dark.png b/packages/stream_chat_flutter/test/src/icons/goldens/ci/stream_svg_icon_dark.png deleted file mode 100644 index e498b63bf8..0000000000 Binary files a/packages/stream_chat_flutter/test/src/icons/goldens/ci/stream_svg_icon_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/icons/goldens/ci/stream_svg_icon_light.png b/packages/stream_chat_flutter/test/src/icons/goldens/ci/stream_svg_icon_light.png deleted file mode 100644 index 3e82952ab4..0000000000 Binary files a/packages/stream_chat_flutter/test/src/icons/goldens/ci/stream_svg_icon_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/icons/stream_svg_icon_test.dart b/packages/stream_chat_flutter/test/src/icons/stream_svg_icon_test.dart deleted file mode 100644 index 5c4fc86d9c..0000000000 --- a/packages/stream_chat_flutter/test/src/icons/stream_svg_icon_test.dart +++ /dev/null @@ -1,202 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/icons/stream_svg_icon.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -void main() { - for (final brightness in Brightness.values) { - goldenTest( - '[${brightness.name}] -> StreamSvgIcon should look fine', - fileName: 'stream_svg_icon_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 800, height: 1200), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - Column( - children: [ - // Monochrome svg icons - Expanded( - child: Wrap( - spacing: 16, - runSpacing: 16, - alignment: WrapAlignment.center, - runAlignment: WrapAlignment.center, - children: [ - _buildIcon(StreamSvgIcons.checkAll), - _buildIcon(StreamSvgIcons.lightning), - _buildIcon(StreamSvgIcons.error), - _buildIcon(StreamSvgIcons.userAdd), - _buildIcon(StreamSvgIcons.penWrite), - _buildIcon(StreamSvgIcons.share), - _buildIcon(StreamSvgIcons.record), - _buildIcon(StreamSvgIcons.message), - _buildIcon(StreamSvgIcons.moon), - _buildIcon(StreamSvgIcons.left), - _buildIcon(StreamSvgIcons.emptyCircleRight), - _buildIcon(StreamSvgIcons.save), - _buildIcon(StreamSvgIcons.cloudDownload), - _buildIcon(StreamSvgIcons.settings), - _buildIcon(StreamSvgIcons.mentions), - _buildIcon(StreamSvgIcons.circleUp), - _buildIcon(StreamSvgIcons.copy), - _buildIcon(StreamSvgIcons.download), - _buildIcon(StreamSvgIcons.send), - _buildIcon(StreamSvgIcons.messageUnread), - _buildIcon(StreamSvgIcons.search), - _buildIcon(StreamSvgIcons.userSettings), - _buildIcon(StreamSvgIcons.shareArrow), - _buildIcon(StreamSvgIcons.polls), - _buildIcon(StreamSvgIcons.smile), - _buildIcon(StreamSvgIcons.right), - _buildIcon(StreamSvgIcons.videoCall), - _buildIcon(StreamSvgIcons.close), - _buildIcon(StreamSvgIcons.closeSmall), - _buildIcon(StreamSvgIcons.volumeUp), - _buildIcon(StreamSvgIcons.mute), - _buildIcon(StreamSvgIcons.threadReply), - _buildIcon(StreamSvgIcons.sendMessage), - _buildIcon(StreamSvgIcons.edit), - _buildIcon(StreamSvgIcons.award), - _buildIcon(StreamSvgIcons.userDelete), - _buildIcon(StreamSvgIcons.attach), - _buildIcon(StreamSvgIcons.notification), - _buildIcon(StreamSvgIcons.userRemove), - _buildIcon(StreamSvgIcons.arrowRight), - _buildIcon(StreamSvgIcons.pin), - _buildIcon(StreamSvgIcons.time), - _buildIcon(StreamSvgIcons.reply), - _buildIcon(StreamSvgIcons.delete), - _buildIcon(StreamSvgIcons.check), - _buildIcon(StreamSvgIcons.flag), - _buildIcon(StreamSvgIcons.menuPoint), - _buildIcon(StreamSvgIcons.up), - _buildIcon(StreamSvgIcons.eye), - _buildIcon(StreamSvgIcons.retry), - _buildIcon(StreamSvgIcons.group), - _buildIcon(StreamSvgIcons.contacts), - _buildIcon(StreamSvgIcons.pictures), - _buildIcon(StreamSvgIcons.checkSend), - _buildIcon(StreamSvgIcons.camera), - _buildIcon(StreamSvgIcons.wutReaction), - _buildIcon(StreamSvgIcons.lolReaction), - _buildIcon(StreamSvgIcons.loveReaction), - _buildIcon(StreamSvgIcons.thumbsUpReaction), - _buildIcon(StreamSvgIcons.thumbsDownReaction), - _buildIcon(StreamSvgIcons.grid), - _buildIcon(StreamSvgIcons.user), - _buildIcon(StreamSvgIcons.files), - _buildIcon(StreamSvgIcons.reload), - _buildIcon(StreamSvgIcons.down), - _buildIcon(StreamSvgIcons.lock), - _buildIcon(StreamSvgIcons.mic), - _buildIcon(StreamSvgIcons.pause), - _buildIcon(StreamSvgIcons.play), - _buildIcon(StreamSvgIcons.stop), - _buildIcon(StreamSvgIcons.link), - ], - ), - ), - // Colored svg icons which preserves their color. - Expanded( - child: Wrap( - spacing: 16, - runSpacing: 16, - alignment: WrapAlignment.center, - runAlignment: WrapAlignment.center, - children: [ - _buildIcon(StreamSvgIcons.filetypePresentationPps), - _buildIcon(StreamSvgIcons.filetypeCompressionRar), - _buildIcon(StreamSvgIcons.filetypeSpreadsheetXlsx), - _buildIcon(StreamSvgIcons.filetypePresentationStandard), - _buildIcon(StreamSvgIcons.filetypePresentationSpecial), - _buildIcon(StreamSvgIcons.filetypeSpreadsheetSpecial), - _buildIcon(StreamSvgIcons.filetypeAudioSpecial), - _buildIcon(StreamSvgIcons.filetypeCodeTar), - _buildIcon(StreamSvgIcons.filetypePresentationOdp), - _buildIcon(StreamSvgIcons.filetypeAudioMp3), - _buildIcon(StreamSvgIcons.filetypeCompressionDeb), - _buildIcon(StreamSvgIcons.filetypeSpreadsheetXlsm), - _buildIcon(StreamSvgIcons.imgur), - _buildIcon(StreamSvgIcons.filetypeTextOdt), - _buildIcon(StreamSvgIcons.filetypePresentationPpt), - _buildIcon(StreamSvgIcons.filetypeCodeStandard), - _buildIcon(StreamSvgIcons.filetypeAudioFlac), - _buildIcon(StreamSvgIcons.filetypeTextSpecial), - _buildIcon(StreamSvgIcons.filetypeSpreadsheetOds), - _buildIcon(StreamSvgIcons.filetypeCodeXml), - _buildIcon(StreamSvgIcons.filetypeCompressionPkg), - _buildIcon(StreamSvgIcons.filetypeSpreadsheetXls), - _buildIcon(StreamSvgIcons.filetypeTextWdp), - _buildIcon(StreamSvgIcons.filetypeCompressionZip), - _buildIcon(StreamSvgIcons.filetypeSpreadsheetStandard), - _buildIcon(StreamSvgIcons.filetypeAudioAac), - _buildIcon(StreamSvgIcons.filetypeOtherPdf), - _buildIcon(StreamSvgIcons.filetypeCodeSav), - _buildIcon(StreamSvgIcons.filetypeCompressionRpm), - _buildIcon(StreamSvgIcons.filetypeOtherSpecial), - _buildIcon(StreamSvgIcons.filetypeAudioM4a), - _buildIcon(StreamSvgIcons.filetypeAudioM4b), - _buildIcon(StreamSvgIcons.filetypeCompressionArj), - _buildIcon(StreamSvgIcons.filetypeTextStandard), - _buildIcon(StreamSvgIcons.filetypeCodeMd), - _buildIcon(StreamSvgIcons.giphy), - _buildIcon(StreamSvgIcons.filetypeTextTxt), - _buildIcon(StreamSvgIcons.filetypeTextRtf), - _buildIcon(StreamSvgIcons.filetypeCompression7z), - _buildIcon(StreamSvgIcons.filetypeAudioStandard), - _buildIcon(StreamSvgIcons.filetypeOtherStandard), - _buildIcon(StreamSvgIcons.filetypeCodeCsv), - _buildIcon(StreamSvgIcons.filetypeCodeHtml), - _buildIcon(StreamSvgIcons.filetypeCodeDat), - _buildIcon(StreamSvgIcons.filetypePresentationPptx), - _buildIcon(StreamSvgIcons.filetypeOtherWkq), - _buildIcon(StreamSvgIcons.filetypeCompressionZ), - _buildIcon(StreamSvgIcons.filetypeAudioAiff), - _buildIcon(StreamSvgIcons.filetypeCodeSpecial), - _buildIcon(StreamSvgIcons.filetypeCompressionStandard), - _buildIcon(StreamSvgIcons.filetypeTextDoc), - _buildIcon(StreamSvgIcons.filetypeAudioOgg), - _buildIcon(StreamSvgIcons.filetypeCompressionSpecial), - _buildIcon(StreamSvgIcons.filetypePresentationKey), - _buildIcon(StreamSvgIcons.filetypeAudioWav), - _buildIcon(StreamSvgIcons.filetypeTextTex), - _buildIcon(StreamSvgIcons.filetypeCodeDb), - _buildIcon(StreamSvgIcons.filetypeTextDocx), - _buildIcon(StreamSvgIcons.filetypeAudioAlac), - ], - ), - ), - ], - ), - ), - ); - } -} - -Widget _wrapWithMaterialApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - home: Theme( - data: ThemeData(brightness: brightness), - child: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: Builder(builder: (context) { - final theme = StreamChatTheme.of(context); - return Scaffold( - backgroundColor: theme.colorTheme.appBg, - body: Center(child: widget), - ); - }), - ), - ), - ); -} - -Widget _buildIcon(StreamSvgIconData icon) { - return IconButton( - iconSize: 48, - onPressed: () {}, - icon: StreamSvgIcon(icon: icon), - ); -} diff --git a/packages/stream_chat_flutter/test/src/indicators/goldens/ci/sending_indicator_0.png b/packages/stream_chat_flutter/test/src/indicators/goldens/ci/sending_indicator_0.png deleted file mode 100644 index 838561febd..0000000000 Binary files a/packages/stream_chat_flutter/test/src/indicators/goldens/ci/sending_indicator_0.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/indicators/goldens/ci/sending_indicator_1.png b/packages/stream_chat_flutter/test/src/indicators/goldens/ci/sending_indicator_1.png deleted file mode 100644 index be1ab4d6f5..0000000000 Binary files a/packages/stream_chat_flutter/test/src/indicators/goldens/ci/sending_indicator_1.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/indicators/goldens/ci/sending_indicator_2.png b/packages/stream_chat_flutter/test/src/indicators/goldens/ci/sending_indicator_2.png deleted file mode 100644 index 0c31a3c88b..0000000000 Binary files a/packages/stream_chat_flutter/test/src/indicators/goldens/ci/sending_indicator_2.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/indicators/goldens/ci/upload_progress_indicator_0.png b/packages/stream_chat_flutter/test/src/indicators/goldens/ci/upload_progress_indicator_0.png deleted file mode 100644 index 388bedb5d6..0000000000 Binary files a/packages/stream_chat_flutter/test/src/indicators/goldens/ci/upload_progress_indicator_0.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/indicators/goldens/ci/upload_progress_indicator_1.png b/packages/stream_chat_flutter/test/src/indicators/goldens/ci/upload_progress_indicator_1.png deleted file mode 100644 index b408f16453..0000000000 Binary files a/packages/stream_chat_flutter/test/src/indicators/goldens/ci/upload_progress_indicator_1.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/indicators/goldens/ci/upload_progress_indicator_2.png b/packages/stream_chat_flutter/test/src/indicators/goldens/ci/upload_progress_indicator_2.png deleted file mode 100644 index e203548709..0000000000 Binary files a/packages/stream_chat_flutter/test/src/indicators/goldens/ci/upload_progress_indicator_2.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/indicators/sending_indicator_test.dart b/packages/stream_chat_flutter/test/src/indicators/sending_indicator_test.dart deleted file mode 100644 index 334ab9d466..0000000000 --- a/packages/stream_chat_flutter/test/src/indicators/sending_indicator_test.dart +++ /dev/null @@ -1,88 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import '../material_app_wrapper.dart'; - -void main() { - testWidgets( - 'StreamSendingIndicator shows sizedBox if message state is initial', - (tester) async { - await tester.pumpWidget( - MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData.light(), - child: Scaffold( - body: Center( - child: StreamSendingIndicator( - message: Message(), - ), - ), - ), - ), - ), - ); - - expect(find.byType(SizedBox), findsOneWidget); - }, - ); - - goldenTest( - 'golden test for StreamSendingIndicator with StreamSvgIcon.checkAll', - fileName: 'sending_indicator_0', - constraints: const BoxConstraints.tightFor(width: 50, height: 50), - builder: () => MaterialAppWrapper( - home: StreamChatTheme( - data: StreamChatThemeData.light(), - child: Scaffold( - body: Center( - child: StreamSendingIndicator( - isMessageRead: true, - message: Message(), - ), - ), - ), - ), - ), - ); - - goldenTest( - 'golden test for StreamSendingIndicator with StreamSvgIcon.check', - fileName: 'sending_indicator_1', - constraints: const BoxConstraints.tightFor(width: 50, height: 50), - builder: () => MaterialAppWrapper( - home: StreamChatTheme( - data: StreamChatThemeData.light(), - child: Scaffold( - body: Center( - child: StreamSendingIndicator( - message: Message( - state: MessageState.sent, - ), - ), - ), - ), - ), - ), - ); - - goldenTest( - 'golden test for StreamSendingIndicator with StreamSvgIcons.time', - fileName: 'sending_indicator_2', - constraints: const BoxConstraints.tightFor(width: 50, height: 50), - builder: () => MaterialAppWrapper( - home: StreamChatTheme( - data: StreamChatThemeData.light(), - child: Scaffold( - body: Center( - child: StreamSendingIndicator( - message: Message( - state: MessageState.sending, - ), - ), - ), - ), - ), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/indicators/typing_indicator_test.dart b/packages/stream_chat_flutter/test/src/indicators/typing_indicator_test.dart deleted file mode 100644 index 0c67db1102..0000000000 --- a/packages/stream_chat_flutter/test/src/indicators/typing_indicator_test.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - testWidgets( - 'it should show channel typing', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.isMuted).thenReturn(false); - when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(() => channel.extraDataStream).thenAnswer( - (i) => Stream.value({ - 'name': 'test', - }), - ); - when(() => channel.extraData).thenReturn({ - 'name': 'test', - }); - when(() => channelState.membersStream).thenAnswer( - (i) => Stream.value([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ) - ]), - ); - when(() => channelState.members).thenReturn([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ), - ]); - when(() => channelState.messages).thenReturn([ - Message( - text: 'hello', - user: User(id: 'other-user'), - ) - ]); - when(() => channelState.messagesStream).thenAnswer( - (i) => Stream.value([ - Message( - text: 'hello', - user: User(id: 'other-user'), - ) - ]), - ); - - when(() => channelState.typingEvents).thenAnswer((i) => { - User(id: 'other-user', extraData: const {'name': 'demo'}): - Event(type: EventType.typingStart), - }); - when(() => channelState.typingEventsStream).thenAnswer( - (i) => Stream.value({ - User(id: 'other-user', extraData: const {'name': 'demo'}): - Event(type: EventType.typingStart), - }), - ); - - const typingKey = Key('typing'); - - await tester.pumpWidget(MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: const Scaffold( - body: StreamTypingIndicator( - key: typingKey, - ), - ), - ), - ), - )); - - expect(find.byKey(typingKey), findsOneWidget); - expect(find.byType(Flexible), findsOneWidget); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/indicators/unread_indicator_test.dart b/packages/stream_chat_flutter/test/src/indicators/unread_indicator_test.dart deleted file mode 100644 index 3dd62f842b..0000000000 --- a/packages/stream_chat_flutter/test/src/indicators/unread_indicator_test.dart +++ /dev/null @@ -1,131 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - testWidgets( - 'it should show total unread count', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.extraDataStream).thenAnswer( - (i) => Stream.value({ - 'name': 'test', - }), - ); - when(() => channel.extraData).thenReturn({ - 'name': 'test', - }); - - when(() => clientState.totalUnreadCount).thenReturn(10); - when(() => clientState.totalUnreadCountStream) - .thenAnswer((i) => Stream.value(10)); - - await tester.pumpWidget(MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: Scaffold( - body: StreamUnreadIndicator(), - ), - ), - ), - )); - - expect(find.text('10'), findsOneWidget); - }, - ); - - testWidgets( - 'it should show nothing if no unread messages', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - when(() => channel.cid).thenReturn('cid'); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => clientState.channels).thenReturn({ - channel.cid!: channel, - }); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channelState.unreadCount).thenReturn(0); - when(() => channelState.unreadCountStream) - .thenAnswer((i) => Stream.value(0)); - - await tester.pumpWidget(MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: Scaffold( - body: StreamUnreadIndicator.channels( - cid: channel.cid, - ), - ), - ), - ), - )); - - expect(find.text('0'), findsNothing); - }, - ); - - testWidgets( - 'it should show 99+ if more than 99 unreads', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - when(() => channel.cid).thenReturn('cid'); - final channelState = MockChannelState(); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => clientState.channels).thenReturn({ - channel.cid!: channel, - }); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channelState.unreadCount).thenReturn(100); - when(() => channelState.unreadCountStream) - .thenAnswer((i) => Stream.value(100)); - - await tester.pumpWidget(MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: Scaffold( - body: StreamUnreadIndicator.channels( - cid: channel.cid, - ), - ), - ), - ), - )); - - expect(find.text('99+'), findsOneWidget); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/indicators/upload_progress_indicator_test.dart b/packages/stream_chat_flutter/test/src/indicators/upload_progress_indicator_test.dart deleted file mode 100644 index 09a9237d81..0000000000 --- a/packages/stream_chat_flutter/test/src/indicators/upload_progress_indicator_test.dart +++ /dev/null @@ -1,160 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; - -void main() { - testWidgets('StreamUploadProgressIndicator at 0% with no background', - (tester) async { - await tester.pumpWidget( - MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData.light(), - child: const Scaffold( - body: Center( - child: StreamUploadProgressIndicator( - total: 100, - uploaded: 0, - showBackground: false, - ), - ), - ), - ), - ), - ); - - expect(find.text('0%'), findsOneWidget); - }); - - testWidgets('StreamUploadProgressIndicator at 50% with no background', - (tester) async { - await tester.pumpWidget( - MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData.light(), - child: const Scaffold( - body: Center( - child: StreamUploadProgressIndicator( - total: 100, - uploaded: 50, - showBackground: false, - ), - ), - ), - ), - ), - ); - - expect(find.text('50%'), findsOneWidget); - }); - - testWidgets('StreamUploadProgressIndicator at 100% with no background', - (tester) async { - await tester.pumpWidget( - MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData.light(), - child: const Scaffold( - body: Center( - child: StreamUploadProgressIndicator( - total: 100, - uploaded: 100, - showBackground: false, - ), - ), - ), - ), - ), - ); - - expect(find.text('100%'), findsOneWidget); - }); - - testWidgets('StreamUploadProgressIndicator at 50% with background', - (tester) async { - await tester.pumpWidget( - MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData.light(), - child: const Scaffold( - body: Center( - child: StreamUploadProgressIndicator( - total: 100, - uploaded: 50, - ), - ), - ), - ), - ), - ); - - final backgroundColor = - ((find.byType(DecoratedBox).evaluate().first.widget as DecoratedBox) - .decoration as BoxDecoration) - .color; - - expect(const Color(0x99000000), backgroundColor); - }); - - goldenTest( - 'golden test for StreamUploadProgressIndicator at 0% with background', - fileName: 'upload_progress_indicator_0', - constraints: const BoxConstraints.tightFor(width: 300, height: 300), - pumpBeforeTest: pumpOnce, - builder: () => MaterialAppWrapper( - home: StreamChatTheme( - data: StreamChatThemeData.light(), - child: const Scaffold( - body: Center( - child: StreamUploadProgressIndicator( - total: 100, - uploaded: 0, - ), - ), - ), - ), - ), - ); - - goldenTest( - 'golden test for StreamUploadProgressIndicator at 50% with background', - fileName: 'upload_progress_indicator_1', - constraints: const BoxConstraints.tightFor(width: 300, height: 300), - pumpBeforeTest: pumpOnce, - builder: () => MaterialAppWrapper( - home: StreamChatTheme( - data: StreamChatThemeData.light(), - child: const Scaffold( - body: Center( - child: StreamUploadProgressIndicator( - total: 100, - uploaded: 50, - ), - ), - ), - ), - ), - ); - - goldenTest( - 'golden test for StreamUploadProgressIndicator at 100% with background', - fileName: 'upload_progress_indicator_2', - constraints: const BoxConstraints.tightFor(width: 300, height: 300), - pumpBeforeTest: pumpOnce, - builder: () => MaterialAppWrapper( - home: StreamChatTheme( - data: StreamChatThemeData.light(), - child: const Scaffold( - body: Center( - child: StreamUploadProgressIndicator( - total: 100, - uploaded: 100, - ), - ), - ), - ), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/keyboard_shortcuts/keyboard_shortcut_runner_test.dart b/packages/stream_chat_flutter/test/src/keyboard_shortcuts/keyboard_shortcut_runner_test.dart deleted file mode 100644 index 269e8320d7..0000000000 --- a/packages/stream_chat_flutter/test/src/keyboard_shortcuts/keyboard_shortcut_runner_test.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -void main() { - testWidgets('KeyboardShortcutRunner onEnterKeypress works', (tester) async { - var count = 0; - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: KeyboardShortcutRunner( - onEnterKeypress: () { - count++; - }, - onEscapeKeypress: () {}, - child: const TextField(), - ), - ), - ), - ), - ); - - final textField = find.byType(TextField); - await tester.tap(textField); - await tester.enterText(textField, 'Test'); - await tester.sendKeyDownEvent(LogicalKeyboardKey.enter); - await tester.pumpAndSettle(); - expect(count, 1); - }); - - testWidgets('KeyboardShortcutRunner onEscapeKeypress works', (tester) async { - final controller = TextEditingController(); - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: KeyboardShortcutRunner( - onEnterKeypress: () {}, - onEscapeKeypress: controller.clear, - child: TextField( - controller: controller, - ), - ), - ), - ), - ), - ); - - final textField = find.byType(TextField); - await tester.tap(textField); - await tester.enterText(textField, 'Test'); - await tester.sendKeyDownEvent(LogicalKeyboardKey.escape); - await tester.pumpAndSettle(); - expect(controller.text, ''); - }); -} diff --git a/packages/stream_chat_flutter/test/src/localization/default_translations_test.dart b/packages/stream_chat_flutter/test/src/localization/default_translations_test.dart deleted file mode 100644 index 18eb53cd82..0000000000 --- a/packages/stream_chat_flutter/test/src/localization/default_translations_test.dart +++ /dev/null @@ -1,177 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -void main() { - test('Default translations should exist', () { - const translations = DefaultTranslations.instance; - expect(translations.launchUrlError, isNotNull); - expect(translations.loadingUsersError, isNotNull); - expect(translations.noUsersLabel, isNotNull); - expect(translations.retryLabel, isNotNull); - expect(translations.userLastOnlineText, isNotNull); - expect(translations.userOnlineText, isNotNull); - expect(translations.userOnlineText, isNotNull); - // no users - expect(translations.userTypingText([]), isNotNull); - // single user - expect(translations.userTypingText([User(id: 'test-id')]), isNotNull); - // multiple users - expect( - translations.userTypingText([ - User(id: 'test-id-1'), - User(id: 'test-id-2'), - ]), - isNotNull, - ); - expect(translations.threadReplyLabel, isNotNull); - expect(translations.onlyVisibleToYouText, isNotNull); - expect(translations.threadReplyCountText(3), isNotNull); - expect( - translations.attachmentsUploadProgressText(remaining: 3, total: 10), - isNotNull, - ); - expect( - translations.pinnedByUserText( - pinnedBy: User(id: 'pinned-by-user-id'), - currentUser: OwnUser(id: 'current-user-id'), - ), - isNotNull, - ); - expect(translations.emptyMessagesText, isNotNull); - expect(translations.genericErrorText, isNotNull); - expect(translations.loadingMessagesError, isNotNull); - expect(translations.resultCountText(3), isNotNull); - expect(translations.messageDeletedText, isNotNull); - expect(translations.messageDeletedLabel, isNotNull); - expect(translations.messageReactionsLabel, isNotNull); - expect(translations.emptyChatMessagesText, isNotNull); - expect(translations.threadSeparatorText(3), isNotNull); - expect(translations.connectedLabel, isNotNull); - expect(translations.disconnectedLabel, isNotNull); - expect(translations.reconnectingLabel, isNotNull); - expect(translations.alsoSendAsDirectMessageLabel, isNotNull); - expect(translations.addACommentOrSendLabel, isNotNull); - expect(translations.searchGifLabel, isNotNull); - expect(translations.writeAMessageLabel, isNotNull); - expect(translations.instantCommandsLabel, isNotNull); - expect(translations.fileTooLargeAfterCompressionError(33), isNotNull); - expect(translations.fileTooLargeError(33), isNotNull); - expect(translations.addAFileLabel, isNotNull); - expect(translations.photoFromCameraLabel, isNotNull); - expect(translations.uploadAFileLabel, isNotNull); - expect(translations.uploadAPhotoLabel, isNotNull); - expect(translations.uploadAVideoLabel, isNotNull); - expect(translations.videoFromCameraLabel, isNotNull); - expect(translations.okLabel, isNotNull); - expect(translations.somethingWentWrongError, isNotNull); - expect(translations.addMoreFilesLabel, isNotNull); - expect(translations.enablePhotoAndVideoAccessMessage, isNotNull); - expect(translations.allowGalleryAccessMessage, isNotNull); - expect(translations.flagMessageLabel, isNotNull); - expect(translations.flagMessageQuestion, isNotNull); - expect(translations.flagLabel, isNotNull); - expect(translations.cancelLabel, isNotNull); - expect(translations.flagMessageSuccessfulLabel, isNotNull); - expect(translations.flagMessageSuccessfulText, isNotNull); - expect(translations.deleteLabel, isNotNull); - expect(translations.deleteMessageLabel, isNotNull); - expect(translations.deleteMessageQuestion, isNotNull); - expect(translations.operationCouldNotBeCompletedText, isNotNull); - expect(translations.replyLabel, isNotNull); - // pinned - expect(translations.togglePinUnpinText(pinned: true), isNotNull); - // un-pinned - expect(translations.togglePinUnpinText(pinned: false), isNotNull); - // delete-failed - expect( - translations.toggleDeleteRetryDeleteMessageText(isDeleteFailed: true), - isNotNull, - ); - // first-delete - expect( - translations.toggleDeleteRetryDeleteMessageText(isDeleteFailed: false), - isNotNull, - ); - expect(translations.copyMessageLabel, isNotNull); - expect(translations.editMessageLabel, isNotNull); - // resend-failed - expect( - translations.toggleResendOrResendEditedMessage(isUpdateFailed: true), - isNotNull, - ); - // first resend - expect( - translations.toggleResendOrResendEditedMessage(isUpdateFailed: false), - isNotNull, - ); - expect(translations.photosLabel, isNotNull); - // today - expect( - translations.sentAtText( - date: DateTime.now(), - time: DateTime.now(), - ), - isNotNull, - ); - // yesterday - expect( - translations.sentAtText( - date: DateTime.now().subtract(const Duration(days: 1)), - time: DateTime.now(), - ), - isNotNull, - ); - // any other day - expect( - translations.sentAtText( - date: DateTime.now().subtract(const Duration(days: 3)), - time: DateTime.now(), - ), - isNotNull, - ); - expect(translations.todayLabel, isNotNull); - expect(translations.yesterdayLabel, isNotNull); - expect(translations.channelIsMutedText, isNotNull); - expect(translations.noTitleText, isNotNull); - expect(translations.letsStartChattingLabel, isNotNull); - expect(translations.sendingFirstMessageLabel, isNotNull); - expect(translations.startAChatLabel, isNotNull); - expect(translations.loadingChannelsError, isNotNull); - expect(translations.deleteConversationLabel, isNotNull); - expect(translations.deleteConversationQuestion, isNotNull); - expect(translations.streamChatLabel, isNotNull); - expect(translations.searchingForNetworkText, isNotNull); - expect(translations.offlineLabel, isNotNull); - expect(translations.tryAgainLabel, isNotNull); - // 1 member - expect(translations.membersCountText(1), isNotNull); - // 3 members - expect(translations.membersCountText(3), isNotNull); - // 1 member - expect(translations.watchersCountText(1), isNotNull); - // 3 members - expect(translations.watchersCountText(3), isNotNull); - expect(translations.viewInfoLabel, isNotNull); - expect(translations.leaveGroupLabel, isNotNull); - expect(translations.leaveLabel, isNotNull); - expect(translations.leaveConversationLabel, isNotNull); - expect(translations.leaveConversationQuestion, isNotNull); - expect(translations.showInChatLabel, isNotNull); - expect(translations.saveImageLabel, isNotNull); - expect(translations.saveVideoLabel, isNotNull); - expect(translations.uploadErrorLabel, isNotNull); - expect(translations.giphyLabel, isNotNull); - expect(translations.shuffleLabel, isNotNull); - expect(translations.sendLabel, isNotNull); - expect(translations.withText, isNotNull); - expect(translations.inText, isNotNull); - expect(translations.youText, isNotNull); - expect(translations.galleryPaginationText, isNotNull); - expect(translations.fileText, isNotNull); - expect(translations.replyToMessageLabel, isNotNull); - expect(translations.unreadCountIndicatorLabel(unreadCount: 2), isNotNull); - expect(translations.unreadMessagesSeparatorText(), isNotNull); - expect(translations.markUnreadError, isNotNull); - expect(translations.markAsUnreadLabel, isNotNull); - }); -} diff --git a/packages/stream_chat_flutter/test/src/material_app_wrapper.dart b/packages/stream_chat_flutter/test/src/material_app_wrapper.dart deleted file mode 100644 index 904b9a080d..0000000000 --- a/packages/stream_chat_flutter/test/src/material_app_wrapper.dart +++ /dev/null @@ -1,28 +0,0 @@ -// ignore_for_file: public_member_api_docs, use_super_parameters - -import 'package:flutter/material.dart'; - -class MaterialAppWrapper extends MaterialApp { - MaterialAppWrapper({ - Key? key, - TargetPlatform platform = TargetPlatform.android, - Iterable>? localizations, - NavigatorObserver? navigatorObserver, - Iterable? localeOverrides, - ThemeData? theme, - TransitionBuilder? builder, - Widget? home, - }) : super( - key: key, - builder: builder, - localizationsDelegates: localizations, - supportedLocales: localeOverrides ?? const [Locale('en')], - theme: theme?.copyWith(platform: platform) ?? - ThemeData(platform: platform, useMaterial3: false), - debugShowCheckedModeBanner: false, - home: home, - navigatorObservers: [ - if (navigatorObserver != null) navigatorObserver, - ], - ); -} diff --git a/packages/stream_chat_flutter/test/src/message_actions_modal/message_actions_modal_test.dart b/packages/stream_chat_flutter/test/src/message_actions_modal/message_actions_modal_test.dart deleted file mode 100644 index f6739c3aea..0000000000 --- a/packages/stream_chat_flutter/test/src/message_actions_modal/message_actions_modal_test.dart +++ /dev/null @@ -1,907 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/src/message_actions_modal/message_actions_modal.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - setUpAll(() { - registerFallbackValue( - MaterialPageRoute(builder: (context) => const SizedBox())); - registerFallbackValue(Message()); - }); - - testWidgets( - 'it should show the all actions', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - await tester.pumpWidget( - MaterialApp( - theme: themeData, - home: StreamChat( - streamChatThemeData: streamTheme, - client: client, - child: SizedBox( - child: StreamChannel( - channel: channel, - child: MessageActionsModal( - message: Message( - text: 'test', - user: User( - id: 'user-id', - ), - state: MessageState.sent, - ), - messageWidget: const Text( - 'test', - key: Key('MessageWidget'), - ), - messageTheme: streamTheme.ownMessageTheme, - ), - ), - ), - ), - ), - ); - await tester.pumpAndSettle(); - - expect(find.byKey(const Key('MessageWidget')), findsOneWidget); - expect(find.text('Thread Reply'), findsOneWidget); - expect(find.text('Reply'), findsOneWidget); - expect(find.text('Edit Message'), findsOneWidget); - expect(find.text('Delete Message'), findsOneWidget); - expect(find.text('Copy Message'), findsOneWidget); - expect(find.text('Mark as Unread'), findsOneWidget); - }, - ); - - testWidgets( - 'it should show the reaction picker', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel( - ownCapabilities: ['send-message', 'send-reaction'], - ); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - await tester.pumpWidget( - MaterialApp( - theme: themeData, - home: StreamChat( - streamChatThemeData: streamTheme, - client: client, - child: SizedBox( - child: StreamChannel( - channel: channel, - child: MessageActionsModal( - message: Message( - text: 'test', - user: User( - id: 'user-id', - ), - state: MessageState.sent, - ), - messageWidget: const Text( - 'test', - key: Key('MessageWidget'), - ), - messageTheme: streamTheme.ownMessageTheme, - ), - ), - ), - ), - ), - ); - await tester.pumpAndSettle(); - - expect(find.byType(StreamReactionPicker), findsOneWidget); - }, - ); - - testWidgets( - 'it should not show the reaction picker', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - await tester.pumpWidget( - MaterialApp( - theme: themeData, - home: StreamChat( - streamChatThemeData: streamTheme, - client: client, - child: SizedBox( - child: StreamChannel( - channel: channel, - child: MessageActionsModal( - showReactionPicker: false, - message: Message( - text: 'test', - user: User( - id: 'user-id', - ), - state: MessageState.sent, - ), - messageWidget: const Text( - 'test', - key: Key('MessageWidget'), - ), - messageTheme: streamTheme.ownMessageTheme, - ), - ), - ), - ), - ), - ); - await tester.pumpAndSettle(); - - expect(find.byType(StreamReactionPicker), findsNothing); - }, - ); - - testWidgets( - 'it should show some actions', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - await tester.pumpWidget( - MaterialApp( - theme: themeData, - home: StreamChat( - streamChatThemeData: streamTheme, - client: client, - child: SizedBox( - child: StreamChannel( - channel: channel, - child: MessageActionsModal( - showCopyMessage: false, - showReplyMessage: false, - showThreadReplyMessage: false, - message: Message( - text: 'test', - user: User( - id: 'user-id', - ), - ), - messageTheme: streamTheme.ownMessageTheme, - messageWidget: const Text( - 'test', - key: Key('MessageWidget'), - ), - ), - ), - ), - ), - ), - ); - await tester.pumpAndSettle(); - - expect(find.byKey(const Key('MessageWidget')), findsOneWidget); - expect(find.text('Reply'), findsNothing); - expect(find.text('Thread reply'), findsNothing); - expect(find.text('Edit message'), findsNothing); - expect(find.text('Delete message'), findsNothing); - expect(find.text('Copy message'), findsNothing); - }, - ); - - testWidgets( - 'it should show custom actions', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - var tapped = false; - await tester.pumpWidget(MaterialApp( - theme: themeData, - home: StreamChat( - streamChatThemeData: streamTheme, - client: client, - child: SizedBox( - child: StreamChannel( - channel: channel, - child: StreamChannel( - channel: channel, - child: MessageActionsModal( - messageWidget: const Text('test'), - message: Message( - text: 'test', - user: User( - id: 'user-id', - ), - ), - messageTheme: streamTheme.ownMessageTheme, - customActions: [ - StreamMessageAction( - leading: const Icon(Icons.check), - title: const Text('title'), - onTap: (m) { - tapped = true; - }, - ), - ], - ), - ), - ), - ), - ), - )); - - await tester.pumpAndSettle(); - - expect(find.byIcon(Icons.check), findsOneWidget); - expect(find.text('title'), findsOneWidget); - - await tester.tap(find.text('title')); - - expect(tapped, true); - }, - ); - - testWidgets( - 'tapping on reply should call the callback', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - var tapped = false; - - await tester.pumpWidget( - MaterialApp( - theme: themeData, - home: StreamChat( - streamChatThemeData: streamTheme, - client: client, - child: SizedBox( - child: StreamChannel( - channel: channel, - child: MessageActionsModal( - messageWidget: const Text('test'), - onReplyTap: (m) { - tapped = true; - }, - message: Message( - text: 'test', - user: User( - id: 'user-id', - ), - state: MessageState.sent, - ), - messageTheme: streamTheme.ownMessageTheme, - ), - ), - ), - ), - ), - ); - await tester.pumpAndSettle(); - - await tester.tap(find.text('Reply')); - - expect(tapped, true); - }, - ); - - testWidgets( - 'tapping on thread reply should call the callback', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - var tapped = false; - - await tester.pumpWidget( - MaterialApp( - theme: themeData, - home: StreamChat( - streamChatThemeData: streamTheme, - client: client, - child: SizedBox( - child: StreamChannel( - channel: channel, - child: MessageActionsModal( - messageWidget: const Text('test'), - onThreadReplyTap: (m) { - tapped = true; - }, - message: Message( - text: 'test', - user: User( - id: 'user-id', - ), - state: MessageState.sent, - ), - messageTheme: streamTheme.ownMessageTheme, - ), - ), - ), - ), - ), - ); - await tester.pumpAndSettle(); - - await tester.tap(find.text('Thread Reply')); - - expect(tapped, true); - }, - ); - - testWidgets( - 'tapping on edit should show the edit bottom sheet', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => channel.state).thenReturn(channelState); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: client, - streamChatThemeData: streamTheme, - child: child, - ), - theme: themeData, - home: StreamChannel( - showLoading: false, - channel: channel, - child: SizedBox( - child: MessageActionsModal( - messageWidget: const Text('test'), - message: Message( - text: 'test', - user: User( - id: 'user-id', - ), - ), - messageTheme: streamTheme.ownMessageTheme, - ), - ), - ), - ), - ); - await tester.pumpAndSettle(); - - await tester.tap(find.text('Edit Message')); - - await tester.pumpAndSettle(); - - expect(find.byType(StreamMessageInput), findsOneWidget); - }, - ); - - testWidgets( - 'tapping on edit should show use the custom builder', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: client, - streamChatThemeData: streamTheme, - child: child, - ), - theme: themeData, - home: StreamChannel( - showLoading: false, - channel: channel, - child: SizedBox( - child: MessageActionsModal( - messageWidget: const Text('test'), - editMessageInputBuilder: (context, m) => const Text('test'), - message: Message( - text: 'test', - user: User( - id: 'user-id', - ), - ), - messageTheme: streamTheme.ownMessageTheme, - ), - ), - ), - ), - ); - await tester.pumpAndSettle(); - - await tester.tap(find.text('Edit Message')); - - await tester.pumpAndSettle(); - - expect(find.text('test'), findsOneWidget); - }, - ); - - testWidgets( - 'tapping on copy should use the callback', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - var tapped = false; - - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: client, - streamChatThemeData: streamTheme, - child: child, - ), - theme: themeData, - home: StreamChannel( - showLoading: false, - channel: channel, - child: SizedBox( - child: MessageActionsModal( - messageWidget: const Text('test'), - onCopyTap: (m) => tapped = true, - message: Message( - text: 'test', - user: User( - id: 'user-id', - ), - ), - messageTheme: streamTheme.ownMessageTheme, - ), - ), - ), - ), - ); - await tester.pumpAndSettle(); - - await tester.tap(find.text('Copy Message')); - - expect(tapped, true); - }, - ); - - testWidgets( - 'tapping on resend should call retry message', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - - final message = Message( - state: MessageState.sendingFailed, - text: 'test', - user: User( - id: 'user-id', - ), - ); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => channel.retryMessage(message)) - .thenAnswer((_) async => SendMessageResponse()); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: client, - streamChatThemeData: streamTheme, - child: child, - ), - theme: themeData, - home: StreamChannel( - showLoading: false, - channel: channel, - child: SizedBox( - child: MessageActionsModal( - messageWidget: const Text('test'), - message: message, - messageTheme: streamTheme.ownMessageTheme, - ), - ), - ), - ), - ); - await tester.pumpAndSettle(); - - await tester.tap(find.text('Resend')); - - verify(() => channel.retryMessage(message)).called(1); - }, - ); - - testWidgets( - 'tapping on flag message should show the dialog', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: client, - streamChatThemeData: streamTheme, - child: child, - ), - theme: themeData, - home: StreamChannel( - showLoading: false, - channel: channel, - child: SizedBox( - child: MessageActionsModal( - messageWidget: const Text('test'), - message: Message( - id: 'testid', - text: 'test', - user: User( - id: 'user-id', - ), - ), - messageTheme: streamTheme.ownMessageTheme, - ), - ), - ), - ), - ); - await tester.pumpAndSettle(); - - await tester.tap(find.text('Flag Message')); - await tester.pumpAndSettle(); - - expect(find.text('Flag Message'), findsNWidgets(2)); - - await tester.tap(find.text('FLAG')); - await tester.pumpAndSettle(); - - verify(() => client.flagMessage('testid')).called(1); - }, - ); - - testWidgets( - 'if flagging a message throws an error the error dialog should appear', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => client.flagMessage(any())) - .thenThrow(StreamChatNetworkError(ChatErrorCode.internalSystemError)); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: client, - streamChatThemeData: streamTheme, - child: child, - ), - theme: themeData, - home: StreamChannel( - showLoading: false, - channel: channel, - child: SizedBox( - child: MessageActionsModal( - messageWidget: const Text('test'), - message: Message( - id: 'testid', - text: 'test', - user: User( - id: 'user-id', - ), - ), - messageTheme: streamTheme.ownMessageTheme, - ), - ), - ), - ), - ); - await tester.pumpAndSettle(); - - await tester.tap(find.text('Flag Message')); - await tester.pumpAndSettle(); - - expect(find.text('Flag Message'), findsNWidgets(2)); - - await tester.tap(find.text('FLAG')); - await tester.pumpAndSettle(); - - expect(find.text('Something went wrong'), findsOneWidget); - }, - ); - - testWidgets( - 'if flagging an already flagged message no error should appear', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => client.flagMessage(any())) - .thenThrow(StreamChatNetworkError(ChatErrorCode.inputError)); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: client, - streamChatThemeData: streamTheme, - child: child, - ), - theme: themeData, - home: StreamChannel( - showLoading: false, - channel: channel, - child: SizedBox( - child: MessageActionsModal( - messageWidget: const Text('test'), - message: Message( - id: 'testid', - text: 'test', - user: User( - id: 'user-id', - ), - ), - messageTheme: streamTheme.ownMessageTheme, - ), - ), - ), - ), - ); - await tester.pumpAndSettle(); - - await tester.tap(find.text('Flag Message')); - await tester.pumpAndSettle(); - - expect(find.text('Flag Message'), findsNWidgets(2)); - - await tester.tap(find.text('FLAG')); - await tester.pumpAndSettle(); - - expect(find.text('Message flagged'), findsOneWidget); - }, - ); - - testWidgets( - 'tapping on delete message should call client.delete', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: client, - streamChatThemeData: streamTheme, - child: child, - ), - theme: themeData, - home: StreamChannel( - showLoading: false, - channel: channel, - child: SizedBox( - child: MessageActionsModal( - messageWidget: const Text('test'), - message: Message( - id: 'testid', - text: 'test', - user: User( - id: 'user-id', - ), - ), - messageTheme: streamTheme.ownMessageTheme, - ), - ), - ), - ), - ); - await tester.pumpAndSettle(); - - await tester.tap(find.text('Delete Message')); - await tester.pumpAndSettle(); - - expect(find.text('Delete Message'), findsOneWidget); - - await tester.tap(find.text('DELETE')); - await tester.pumpAndSettle(); - - verify(() => channel.deleteMessage(any())).called(1); - }, - ); - - testWidgets( - 'tapping on delete message should call client.delete', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => channel.deleteMessage(any())) - .thenThrow(StreamChatNetworkError(ChatErrorCode.internalSystemError)); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: client, - streamChatThemeData: streamTheme, - child: child, - ), - theme: themeData, - home: StreamChannel( - showLoading: false, - channel: channel, - child: SizedBox( - child: MessageActionsModal( - messageWidget: const Text('test'), - message: Message( - id: 'testid', - text: 'test', - user: User( - id: 'user-id', - ), - ), - messageTheme: streamTheme.ownMessageTheme, - ), - ), - ), - ), - ); - await tester.pumpAndSettle(); - - await tester.tap(find.text('Delete Message')); - await tester.pumpAndSettle(); - - expect(find.text('Delete Message'), findsOneWidget); - - await tester.tap(find.text('DELETE')); - await tester.pumpAndSettle(); - - expect(find.text('Something went wrong'), findsOneWidget); - }, - ); - - testWidgets( - 'tapping on unread message should call client.unread', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: client, - streamChatThemeData: streamTheme, - child: child, - ), - theme: themeData, - home: Scaffold( - body: StreamChannel( - showLoading: false, - channel: channel, - child: SizedBox( - child: MessageActionsModal( - messageWidget: const Text('test'), - message: Message( - id: 'testid', - text: 'test', - user: User( - id: 'user-id', - ), - ), - messageTheme: streamTheme.ownMessageTheme, - ), - ), - ), - ), - ), - ); - await tester.pumpAndSettle(); - - await tester.tap(find.text('Mark as Unread')); - await tester.pumpAndSettle(); - - verify(() => channel.markUnread(any())).called(1); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/message_input/attachment_button_test.dart b/packages/stream_chat_flutter/test/src/message_input/attachment_button_test.dart deleted file mode 100644 index e6290edeb5..0000000000 --- a/packages/stream_chat_flutter/test/src/message_input/attachment_button_test.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/src/message_input/attachment_button.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; - -void main() { - testWidgets('SendButton onPressed works', (tester) async { - var count = 0; - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: AttachmentButton( - color: StreamChatThemeData.light() - .messageInputTheme - .actionButtonIdleColor, - onPressed: () { - count++; - }, - ), - ), - ), - ), - ); - - final button = find.byType(IconButton); - expect(button, findsOneWidget); - expect(find.byType(StreamSvgIcon), findsOneWidget); - await tester.tap(button); - expect(count, 1); - }); - - goldenTest( - 'golden test for AttachmentButton', - fileName: 'attachment_button_0', - constraints: const BoxConstraints.tightFor(width: 50, height: 50), - builder: () => MaterialAppWrapper( - home: Scaffold( - body: Center( - child: AttachmentButton( - color: StreamChatThemeData.light() - .messageInputTheme - .actionButtonIdleColor, - onPressed: () {}, - ), - ), - ), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/message_input/audio_recorder/assets/audio.m4a b/packages/stream_chat_flutter/test/src/message_input/audio_recorder/assets/audio.m4a deleted file mode 100644 index 8134b16b9e..0000000000 Binary files a/packages/stream_chat_flutter/test/src/message_input/audio_recorder/assets/audio.m4a and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/message_input/audio_recorder/audio_recorder_controller_test.dart b/packages/stream_chat_flutter/test/src/message_input/audio_recorder/audio_recorder_controller_test.dart deleted file mode 100644 index 86ad93a548..0000000000 --- a/packages/stream_chat_flutter/test/src/message_input/audio_recorder/audio_recorder_controller_test.dart +++ /dev/null @@ -1,229 +0,0 @@ -// ignore_for_file: cascade_invocations - -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; -import 'package:record/record.dart'; -import 'package:rxdart/rxdart.dart'; -import 'package:stream_chat_flutter/src/message_input/audio_recorder/audio_recorder_controller.dart'; -import 'package:stream_chat_flutter/src/message_input/audio_recorder/audio_recorder_state.dart'; - -import '../../fakes.dart'; - -class MockAudioRecorder extends Mock implements AudioRecorder {} - -class MockAmplitude extends Mock implements Amplitude {} - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - late MockAudioRecorder mockRecorder; - const config = RecordConfig(numChannels: 1); - late StreamAudioRecorderController controller; - late PublishSubject amplitudeController; - - setUpAll(() { - registerFallbackValue(Duration.zero); - }); - - setUp(() { - PathProviderPlatform.instance = FakePathProviderPlatform(); - - mockRecorder = MockAudioRecorder(); - when(() => mockRecorder.dispose()).thenAnswer((_) async {}); - - amplitudeController = PublishSubject(); - when(() => mockRecorder.onAmplitudeChanged(any())) - .thenAnswer((_) => amplitudeController.stream); - - controller = StreamAudioRecorderController.raw( - config: config, - recorder: mockRecorder, - ); - }); - - tearDown(() { - amplitudeController.close(); - controller.dispose(); - }); - - test('initial state should be idle', () { - expect(controller.value, isA()); - }); - - group('startRecord', () { - setUp(() { - when(() => mockRecorder.start(config, path: any(named: 'path'))) - .thenAnswer((_) async {}); - }); - - test( - 'starts recording when permission is granted', - () async { - when(() => mockRecorder.hasPermission()).thenAnswer((_) async => true); - - await controller.startRecord(); - - expect(controller.value, isA()); - verify(() => mockRecorder.start(config, path: any(named: 'path'))); - }, - ); - - test('does not start recording when permission is denied', () async { - when(() => mockRecorder.hasPermission()).thenAnswer((_) async => false); - - await controller.startRecord(); - - expect(controller.value, isA()); - verifyNever(() => mockRecorder.start(config, path: any(named: 'path'))); - }); - }); - - group('stopRecord', () { - const pathPrefix = './test/src/message_input/audio_recorder/assets'; - const testPath = '$pathPrefix/audio.m4a'; - - setUp(() async { - when(() => mockRecorder.hasPermission()).thenAnswer((_) async => true); - when(() => mockRecorder.stop()).thenAnswer((_) async => testPath); - when(() => mockRecorder.start(config, path: any(named: 'path'))) - .thenAnswer((_) async {}); - }); - - test('stops recording and updates state to stopped', () async { - await controller.startRecord(); - await controller.stopRecord(); - - expect(controller.value, isA()); - verify(() => mockRecorder.stop()).called(1); - }); - - test('includes duration and waveform in stopped state', () async { - // Simulate some recording time and amplitude changes - final controller = StreamAudioRecorderController.raw( - recorder: mockRecorder, - config: config, - initialState: const RecordStateRecordingHold( - duration: Duration(seconds: 5), - waveform: [0.5, 0.6, 0.7], - ), - ); - - await controller.startRecord(); - await controller.stopRecord(); - - final stoppedState = controller.value as RecordStateStopped; - expect(stoppedState.audioRecording.extraData['duration'], isNotNull); - expect(stoppedState.audioRecording.extraData['waveform_data'], isNotNull); - }); - }); - - group('cancelRecord', () { - setUp(() { - when(() => mockRecorder.hasPermission()).thenAnswer((_) async => true); - when(() => mockRecorder.cancel()).thenAnswer((_) async {}); - when(() => mockRecorder.start(config, path: any(named: 'path'))) - .thenAnswer((_) async {}); - }); - - test('cancels recording and returns to idle state', () async { - await controller.startRecord(); - await controller.cancelRecord(); - - expect(controller.value, isA()); - verify(() => mockRecorder.cancel()).called(1); - }); - }); - - group('lockRecord', () { - test('transitions from hold to locked state', () async { - final controller = StreamAudioRecorderController.raw( - recorder: mockRecorder, - config: config, - initialState: const RecordStateRecordingHold(), - ); - - controller.lockRecord(); - - expect(controller.value, isA()); - }); - - test('preserves duration and waveform when locking', () async { - const duration = Duration(seconds: 3); - final waveform = [0.1, 0.2, 0.3]; - - final controller = StreamAudioRecorderController.raw( - recorder: mockRecorder, - config: config, - initialState: RecordStateRecordingHold( - duration: duration, - waveform: waveform, - ), - ); - - controller.lockRecord(); - - final lockedState = controller.value as RecordStateRecordingLocked; - expect(lockedState.duration, duration); - expect(lockedState.waveform, waveform); - }); - }); - - group('dragRecord', () { - test('updates drag offset in hold state', () async { - final controller = StreamAudioRecorderController.raw( - recorder: mockRecorder, - config: config, - initialState: const RecordStateRecordingHold(), - ); - - const dragOffset = Offset(10, 20); - controller.dragRecord(dragOffset); - - final holdState = controller.value as RecordStateRecordingHold; - expect(holdState.dragOffset, dragOffset); - }); - }); - - group('showInfo', () { - test('shows info message in idle state', () { - const message = 'Test Message'; - controller.showInfo(message); - - final idleState = controller.value as RecordStateIdle; - expect(idleState.message, message); - }); - - test('clears info message after duration', () async { - const message = 'Test Message'; - controller.showInfo(message, duration: const Duration(milliseconds: 100)); - - await Future.delayed(const Duration(milliseconds: 150)); - - final idleState = controller.value as RecordStateIdle; - expect(idleState.message, isNull); - }); - }); - - group('amplitude changes', () { - setUp(() { - when(() => mockRecorder.hasPermission()).thenAnswer((_) async => true); - when(() => mockRecorder.start(config, path: any(named: 'path'))) - .thenAnswer((_) async {}); - }); - - test('updates waveform data when amplitude changes', () async { - await controller.startRecord(); - - final mockAmplitude = MockAmplitude(); - when(() => mockAmplitude.current).thenReturn(-30); - amplitudeController.add(mockAmplitude); - - // Allow the stream to process - await Future.delayed(Duration.zero); - - final recordingState = controller.value as RecordStateRecording; - expect(recordingState.waveform, isNotEmpty); - }); - }); -} diff --git a/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_idle_dark.png b/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_idle_dark.png deleted file mode 100644 index a928f3e658..0000000000 Binary files a/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_idle_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_idle_light.png b/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_idle_light.png deleted file mode 100644 index 68f02ee6f6..0000000000 Binary files a/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_idle_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_hold_dark.png b/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_hold_dark.png deleted file mode 100644 index 5c7a7afaa2..0000000000 Binary files a/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_hold_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_hold_light.png b/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_hold_light.png deleted file mode 100644 index 5dcce88080..0000000000 Binary files a/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_hold_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_locked_dark.png b/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_locked_dark.png deleted file mode 100644 index ff99d92898..0000000000 Binary files a/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_locked_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_locked_light.png b/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_locked_light.png deleted file mode 100644 index 6de3d7b6dd..0000000000 Binary files a/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_locked_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_stopped_dark.png b/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_stopped_dark.png deleted file mode 100644 index 6710cf41f4..0000000000 Binary files a/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_stopped_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_stopped_light.png b/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_stopped_light.png deleted file mode 100644 index 40a2c8be8f..0000000000 Binary files a/packages/stream_chat_flutter/test/src/message_input/audio_recorder/goldens/ci/stream_audio_recorder_button_recording_stopped_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/message_input/audio_recorder/stream_audio_recorder_test.dart b/packages/stream_chat_flutter/test/src/message_input/audio_recorder/stream_audio_recorder_test.dart deleted file mode 100644 index 5aced84d01..0000000000 --- a/packages/stream_chat_flutter/test/src/message_input/audio_recorder/stream_audio_recorder_test.dart +++ /dev/null @@ -1,418 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_portal/flutter_portal.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/src/misc/audio_waveform.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import '../../utils/finders.dart'; - -void main() { - group('StreamAudioRecorderButton', () { - final fakeAudioRecording = Attachment( - type: AttachmentType.voiceRecording, - file: AttachmentFile( - size: 1000000, - path: 'voice_recordings/test.m4a', - ), - extraData: { - 'duration': 3000, - 'waveform_data': List.filled(50, 0.5), - }, - ); - - testWidgets( - 'renders mic icon in idle state', - (tester) async { - await tester.pumpWidget( - _wrapWithStreamChatApp( - const StreamAudioRecorderButton( - recordState: RecordStateIdle(), - ), - ), - ); - - expect(find.bySvgIcon(StreamSvgIcons.mic), findsOneWidget); - }, - ); - - testWidgets( - 'calls onRecordStart when long pressed', - (tester) async { - var recordStartCalled = false; - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamAudioRecorderButton( - recordState: const RecordStateIdle(), - onRecordStart: () { - recordStartCalled = true; - }, - ), - ), - ); - - final center = tester.getCenter(find.byType(StreamAudioRecorderButton)); - await tester.startGesture(center); - await tester.pump(kLongPressTimeout + const Duration(milliseconds: 50)); - - expect(recordStartCalled, true); - }, - ); - - testWidgets( - 'shows recording UI when in RecordStateRecordingHold', - (tester) async { - final recordingState = RecordStateRecordingHold( - duration: const Duration(seconds: 5), - waveform: List.filled(50, 0.5), - ); - - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamAudioRecorderButton( - recordState: recordingState, - onRecordStart: () {}, - ), - ), - ); - - expect(find.byType(PlaybackTimerIndicator), findsOneWidget); - expect(find.byType(SlideToCancelIndicator), findsOneWidget); - }, - ); - - testWidgets( - 'calls onRecordFinish when long press is released', - (tester) async { - var onRecordFinishCalled = false; - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamAudioRecorderButton( - recordState: const RecordStateRecordingHold(), - onRecordFinish: () { - onRecordFinishCalled = true; - }, - ), - ), - ); - - final center = tester.getCenter(find.byType(RecordButton)); - final gesture = await tester.startGesture(center); - await tester.pump(kLongPressTimeout + const Duration(milliseconds: 50)); - await gesture.up(); - - expect(onRecordFinishCalled, true); - }, - ); - - testWidgets( - 'calls onRecordCancel when dragged left beyond threshold', - (tester) async { - const cancelThreshold = 60.0; - - var onRecordCancelCalled = false; - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamAudioRecorderButton( - recordState: const RecordStateRecordingHold(), - cancelRecordThreshold: cancelThreshold, - onRecordCancel: () { - onRecordCancelCalled = true; - }, - ), - ), - ); - - final center = tester.getCenter(find.byType(RecordButton)); - final gesture = await tester.startGesture(center); - await tester.pump(kLongPressTimeout + const Duration(milliseconds: 50)); - // Move beyond threshold - await gesture.moveBy(const Offset(-cancelThreshold, 0)); - - expect(onRecordCancelCalled, true); - }, - ); - - testWidgets( - 'calls onRecordLock when dragged up beyond threshold', - (tester) async { - const lockThreshold = 60.0; - - var lockCalled = false; - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamAudioRecorderButton( - recordState: const RecordStateRecordingHold(), - lockRecordThreshold: lockThreshold, - onRecordLock: () { - lockCalled = true; - }, - ), - ), - ); - - final center = tester.getCenter(find.byType(RecordButton)); - final gesture = await tester.startGesture(center); - await tester.pump(kLongPressTimeout + const Duration(milliseconds: 50)); - // Move beyond threshold - await gesture.moveBy(const Offset(0, -lockThreshold)); - - expect(lockCalled, true); - }, - ); - - testWidgets( - 'shows locked recording UI when in RecordStateRecordingLocked', - (tester) async { - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamAudioRecorderButton( - recordState: RecordStateRecordingLocked( - duration: const Duration(seconds: 5), - waveform: List.filled(50, 0.5), - ), - ), - ), - ); - - expect(find.byType(StreamAudioWaveform), findsOneWidget); - expect(find.bySvgIcon(StreamSvgIcons.delete), findsOneWidget); - expect(find.bySvgIcon(StreamSvgIcons.stop), findsOneWidget); - expect(find.bySvgIcon(StreamSvgIcons.checkSend), findsOneWidget); - }, - ); - - testWidgets( - 'calls onRecordCancel when delete is tap in RecordStateRecordingLocked', - (tester) async { - var onRecordCancelCalled = false; - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamAudioRecorderButton( - recordState: RecordStateRecordingLocked( - duration: const Duration(seconds: 5), - waveform: List.filled(50, 0.5), - ), - onRecordCancel: () { - onRecordCancelCalled = true; - }, - ), - ), - ); - - await tester.tap(find.bySvgIcon(StreamSvgIcons.delete)); - - expect(onRecordCancelCalled, true); - }, - ); - - testWidgets( - 'calls onRecordStop when stop is tap in RecordStateRecordingLocked', - (tester) async { - var onRecordStopCalled = false; - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamAudioRecorderButton( - recordState: RecordStateRecordingLocked( - duration: const Duration(seconds: 5), - waveform: List.filled(50, 0.5), - ), - onRecordStop: () { - onRecordStopCalled = true; - }, - ), - ), - ); - - await tester.tap(find.bySvgIcon(StreamSvgIcons.stop)); - - expect(onRecordStopCalled, true); - }, - ); - - testWidgets( - 'calls onRecordFinish when checkSend is tap RecordStateRecordingLocked', - (tester) async { - var onRecordFinishCalled = false; - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamAudioRecorderButton( - recordState: RecordStateRecordingLocked( - duration: const Duration(seconds: 5), - waveform: List.filled(50, 0.5), - ), - onRecordFinish: () { - onRecordFinishCalled = true; - }, - ), - ), - ); - - await tester.tap(find.bySvgIcon(StreamSvgIcons.checkSend)); - - expect(onRecordFinishCalled, true); - }, - ); - - testWidgets( - 'shows stopped recording UI with playback controls', - (tester) async { - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamAudioRecorderButton( - recordState: RecordStateStopped( - audioRecording: fakeAudioRecording, - ), - ), - ), - ); - - expect(find.byType(PlaybackControlButton), findsOneWidget); - expect(find.byType(PlaybackTimerText), findsOneWidget); - expect(find.byType(StreamAudioWaveformSlider), findsOneWidget); - expect(find.bySvgIcon(StreamSvgIcons.delete), findsOneWidget); - expect(find.bySvgIcon(StreamSvgIcons.checkSend), findsOneWidget); - }, - ); - - testWidgets( - 'calls onRecordFinish when checkSend is tap in RecordStateStopped', - (tester) async { - var onRecordFinishCalled = false; - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamAudioRecorderButton( - recordState: RecordStateStopped( - audioRecording: fakeAudioRecording, - ), - onRecordFinish: () { - onRecordFinishCalled = true; - }, - ), - ), - ); - - await tester.tap(find.bySvgIcon(StreamSvgIcons.checkSend)); - - expect(onRecordFinishCalled, true); - }, - ); - - testWidgets( - 'calls onRecordFinish when delete is tap in RecordStateStopped', - (tester) async { - var onRecordCancelCalled = false; - await tester.pumpWidget( - _wrapWithStreamChatApp( - StreamAudioRecorderButton( - recordState: RecordStateStopped( - audioRecording: fakeAudioRecording, - ), - onRecordCancel: () { - onRecordCancelCalled = true; - }, - ), - ), - ); - - await tester.tap(find.bySvgIcon(StreamSvgIcons.delete)); - - expect(onRecordCancelCalled, true); - }, - ); - - for (final brightness in Brightness.values) { - goldenTest( - '[${brightness.name}] -> should look fine in idol state', - fileName: 'stream_audio_recorder_button_idle_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 400, height: 160), - builder: () => _wrapWithStreamChatApp( - brightness: brightness, - const StreamAudioRecorderButton( - recordState: RecordStateIdle(), - ), - ), - ); - - goldenTest( - '[${brightness.name}] -> should look fine in recording hold state', - fileName: - 'stream_audio_recorder_button_recording_hold_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 400, height: 160), - builder: () => _wrapWithStreamChatApp( - brightness: brightness, - StreamAudioRecorderButton( - recordState: RecordStateRecordingHold( - duration: const Duration(seconds: 5), - waveform: List.filled(50, 0.5), - ), - ), - ), - ); - - goldenTest( - '[${brightness.name}] -> should look fine in recording locked state', - fileName: - 'stream_audio_recorder_button_recording_locked_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 400, height: 160), - builder: () => _wrapWithStreamChatApp( - brightness: brightness, - StreamAudioRecorderButton( - recordState: RecordStateRecordingLocked( - duration: const Duration(seconds: 5), - waveform: List.filled(50, 0.5), - ), - onRecordCancel: () {}, - onRecordStop: () {}, - onRecordFinish: () {}, - ), - ), - ); - - goldenTest( - '[${brightness.name}] -> should look fine in recording stopped state', - fileName: - 'stream_audio_recorder_button_recording_stopped_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 400, height: 160), - builder: () => _wrapWithStreamChatApp( - brightness: brightness, - StreamAudioRecorderButton( - recordState: RecordStateStopped( - audioRecording: fakeAudioRecording, - ), - onRecordCancel: () {}, - onRecordFinish: () {}, - ), - ), - ); - } - }); -} - -Widget _wrapWithStreamChatApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - debugShowCheckedModeBanner: false, - home: Portal( - child: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: Builder(builder: (context) { - final theme = StreamChatTheme.of(context); - return Scaffold( - backgroundColor: theme.colorTheme.appBg, - bottomNavigationBar: Material( - elevation: 10, - color: theme.colorTheme.barsBg, - child: Padding( - padding: const EdgeInsets.all(8), - child: widget, - ), - ), - ); - }), - ), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/message_input/clear_input_item_test.dart b/packages/stream_chat_flutter/test/src/message_input/clear_input_item_test.dart deleted file mode 100644 index 68d1f430fd..0000000000 --- a/packages/stream_chat_flutter/test/src/message_input/clear_input_item_test.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/src/message_input/clear_input_item_button.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; - -void main() { - testWidgets('ClearInputItemButton onPressed works', (tester) async { - var count = 0; - await tester.pumpWidget( - MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData.light(), - child: Scaffold( - body: Center( - child: ClearInputItemButton( - onTap: () { - count++; - }, - ), - ), - ), - ), - ), - ); - - final button = find.byType(RawMaterialButton); - expect(button, findsOneWidget); - expect(find.byType(StreamSvgIcon), findsOneWidget); - await tester.tap(button); - expect(count, 1); - }); - - goldenTest( - 'golden test for ClearInputItemButton', - fileName: 'clear_input_item_0', - constraints: const BoxConstraints.tightFor(width: 50, height: 50), - builder: () => MaterialAppWrapper( - home: StreamChatTheme( - data: StreamChatThemeData.light(), - child: Scaffold( - body: Center( - child: ClearInputItemButton( - onTap: () {}, - ), - ), - ), - ), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/message_input/command_button_test.dart b/packages/stream_chat_flutter/test/src/message_input/command_button_test.dart deleted file mode 100644 index fc7dad0b54..0000000000 --- a/packages/stream_chat_flutter/test/src/message_input/command_button_test.dart +++ /dev/null @@ -1,90 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/src/message_input/command_button.dart'; - -import '../material_app_wrapper.dart'; - -void main() { - testWidgets('CommandButton onPressed works', (tester) async { - var count = 0; - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: CommandButton( - color: Colors.red, - onPressed: () { - count++; - }, - ), - ), - ), - ), - ); - - final button = find.byType(IconButton); - expect(button, findsOneWidget); - await tester.tap(button); - expect(count, 1); - }); - - testWidgets('CommandButton should accept icon', (tester) async { - const icon = Icon(Icons.add); - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: CommandButton( - icon: icon, - onPressed: () {}, - ), - ), - ), - ), - ); - - final iconFound = find.byWidget(icon); - expect(iconFound, findsOneWidget); - }); - - testWidgets('CommandButton should not accept both color and icon', - (tester) async { - expect( - () => MaterialApp( - home: Scaffold( - body: Center( - child: CommandButton( - color: Colors.red, - icon: const Icon(Icons.add), - onPressed: () {}, - ), - ), - ), - ), - throwsA( - isA().having( - (e) => e.message, - 'message', - 'Either icon or color should be provided', - ), - ), - ); - }); - - goldenTest( - 'golden test for CommandButton', - fileName: 'command_button_0', - constraints: const BoxConstraints.tightFor(width: 50, height: 50), - builder: () => MaterialAppWrapper( - home: Scaffold( - body: Center( - child: CommandButton( - color: Colors.blueAccent, - onPressed: () {}, - ), - ), - ), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/message_input/countdown_button_test.dart b/packages/stream_chat_flutter/test/src/message_input/countdown_button_test.dart deleted file mode 100644 index 025f7db803..0000000000 --- a/packages/stream_chat_flutter/test/src/message_input/countdown_button_test.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; - -void main() { - testWidgets('CountdownButton works', (tester) async { - await tester.pumpWidget( - MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData.light(), - child: const Scaffold( - body: Center( - child: StreamCountdownButton(count: 5), - ), - ), - ), - ), - ); - - expect(find.text('5'), findsOneWidget); - }); - - goldenTest( - 'golden test for CountdownButton', - fileName: 'countdown_button_0', - constraints: const BoxConstraints.tightFor(width: 50, height: 50), - builder: () => MaterialAppWrapper( - home: StreamChatTheme( - data: StreamChatThemeData.light(), - child: const Scaffold( - body: Center( - child: StreamCountdownButton(count: 5), - ), - ), - ), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/message_input/dm_checkbox_test.dart b/packages/stream_chat_flutter/test/src/message_input/dm_checkbox_test.dart deleted file mode 100644 index f462b4a7df..0000000000 --- a/packages/stream_chat_flutter/test/src/message_input/dm_checkbox_test.dart +++ /dev/null @@ -1,133 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/src/message_input/dm_checkbox.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; - -void main() { - testWidgets('DmCheckbox onTap works', (tester) async { - var count = 0; - await tester.pumpWidget( - MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData.light(), - child: Scaffold( - body: Center( - child: DmCheckbox( - foregroundDecoration: BoxDecoration( - border: Border.all( - color: StreamChatThemeData.light() - .colorTheme - .textHighEmphasis - // ignore: deprecated_member_use - .withOpacity(0.5), - width: 2, - ), - borderRadius: BorderRadius.circular(3), - ), - color: StreamChatThemeData.light().colorTheme.accentPrimary, - onTap: () { - count++; - }, - crossFadeState: CrossFadeState.showFirst, - ), - ), - ), - ), - ), - ); - - expect(find.byType(AnimatedCrossFade), findsOneWidget); - final checkbox = find.byType(InkWell); - await tester.tap(checkbox); - await tester.pumpAndSettle(); - expect(count, 1); - }); - - goldenTest( - 'golden test for checked DmCheckbox with border', - fileName: 'dm_checkbox_0', - constraints: const BoxConstraints.tightFor(width: 200, height: 50), - builder: () => MaterialAppWrapper( - home: StreamChatTheme( - data: StreamChatThemeData.light(), - child: Scaffold( - body: Center( - child: DmCheckbox( - foregroundDecoration: BoxDecoration( - border: Border.all( - color: StreamChatThemeData.light() - .colorTheme - .textHighEmphasis - // ignore: deprecated_member_use - .withOpacity(0.5), - width: 2, - ), - borderRadius: BorderRadius.circular(3), - ), - color: StreamChatThemeData.light().colorTheme.accentPrimary, - onTap: () {}, - crossFadeState: CrossFadeState.showFirst, - ), - ), - ), - ), - ), - ); - - goldenTest( - 'golden test for checked DmCheckbox without border', - fileName: 'dm_checkbox_1', - constraints: const BoxConstraints.tightFor(width: 200, height: 50), - builder: () => MaterialAppWrapper( - home: StreamChatTheme( - data: StreamChatThemeData.light(), - child: Scaffold( - body: Center( - child: DmCheckbox( - foregroundDecoration: BoxDecoration( - borderRadius: BorderRadius.circular(3), - ), - color: StreamChatThemeData.light().colorTheme.accentPrimary, - onTap: () {}, - crossFadeState: CrossFadeState.showFirst, - ), - ), - ), - ), - ), - ); - - goldenTest( - 'golden test for unchecked DmCheckbox with border', - fileName: 'dm_checkbox_2', - constraints: const BoxConstraints.tightFor(width: 200, height: 50), - builder: () => MaterialAppWrapper( - home: StreamChatTheme( - data: StreamChatThemeData.light(), - child: Scaffold( - body: Center( - child: DmCheckbox( - foregroundDecoration: BoxDecoration( - border: Border.all( - color: StreamChatThemeData.light() - .colorTheme - .textHighEmphasis - // ignore: deprecated_member_use - .withOpacity(0.5), - width: 2, - ), - borderRadius: BorderRadius.circular(3), - ), - color: StreamChatThemeData.light().colorTheme.barsBg, - onTap: () {}, - crossFadeState: CrossFadeState.showSecond, - ), - ), - ), - ), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/message_input/goldens/ci/attachment_button_0.png b/packages/stream_chat_flutter/test/src/message_input/goldens/ci/attachment_button_0.png deleted file mode 100644 index cac3a620ac..0000000000 Binary files a/packages/stream_chat_flutter/test/src/message_input/goldens/ci/attachment_button_0.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/message_input/goldens/ci/clear_input_item_0.png b/packages/stream_chat_flutter/test/src/message_input/goldens/ci/clear_input_item_0.png deleted file mode 100644 index 989e925b4e..0000000000 Binary files a/packages/stream_chat_flutter/test/src/message_input/goldens/ci/clear_input_item_0.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/message_input/goldens/ci/command_button_0.png b/packages/stream_chat_flutter/test/src/message_input/goldens/ci/command_button_0.png deleted file mode 100644 index 588d580602..0000000000 Binary files a/packages/stream_chat_flutter/test/src/message_input/goldens/ci/command_button_0.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/message_input/goldens/ci/countdown_button_0.png b/packages/stream_chat_flutter/test/src/message_input/goldens/ci/countdown_button_0.png deleted file mode 100644 index 519ff3f2c0..0000000000 Binary files a/packages/stream_chat_flutter/test/src/message_input/goldens/ci/countdown_button_0.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/message_input/goldens/ci/dm_checkbox_0.png b/packages/stream_chat_flutter/test/src/message_input/goldens/ci/dm_checkbox_0.png deleted file mode 100644 index 3980881900..0000000000 Binary files a/packages/stream_chat_flutter/test/src/message_input/goldens/ci/dm_checkbox_0.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/message_input/goldens/ci/dm_checkbox_1.png b/packages/stream_chat_flutter/test/src/message_input/goldens/ci/dm_checkbox_1.png deleted file mode 100644 index c00aa7f6ad..0000000000 Binary files a/packages/stream_chat_flutter/test/src/message_input/goldens/ci/dm_checkbox_1.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/message_input/goldens/ci/dm_checkbox_2.png b/packages/stream_chat_flutter/test/src/message_input/goldens/ci/dm_checkbox_2.png deleted file mode 100644 index 340ddb3eb1..0000000000 Binary files a/packages/stream_chat_flutter/test/src/message_input/goldens/ci/dm_checkbox_2.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/message_input/message_input_attachment_list_test.dart b/packages/stream_chat_flutter/test/src/message_input/message_input_attachment_list_test.dart deleted file mode 100644 index 15a655d9c1..0000000000 --- a/packages/stream_chat_flutter/test/src/message_input/message_input_attachment_list_test.dart +++ /dev/null @@ -1,242 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -Widget wrapWithStreamChat( - Widget child, { - StreamChatClient? client, -}) { - return MaterialApp( - home: StreamChat( - client: client ?? MockClient(), - child: child, - ), - ); -} - -void main() { - group('StreamMessageInputAttachmentList tests', () { - testWidgets( - 'StreamMessageInputAttachmentList should render attachments', - (WidgetTester tester) async { - final attachments = [ - Attachment(type: 'file', id: 'file1'), - Attachment(type: 'file', id: 'file2'), - Attachment(type: 'image', id: 'image1'), - ]; - - await tester.pumpWidget( - wrapWithStreamChat( - StreamMessageInputAttachmentList( - attachments: attachments, - ), - ), - ); - - // Expect 2 file attachments and 1 media attachment - expect(find.byType(MessageInputFileAttachments), findsOneWidget); - expect(find.byType(StreamFileAttachment), findsNWidgets(2)); - expect(find.byType(MessageInputMediaAttachments), findsOneWidget); - expect(find.byType(StreamMediaAttachmentThumbnail), findsOneWidget); - }, - ); - - testWidgets( - 'StreamMessageInputAttachmentList should call onRemovePressed callback', - (WidgetTester tester) async { - Attachment? removedAttachment; - - final attachments = [ - Attachment(type: 'file', id: 'file1'), - Attachment(type: 'file', id: 'file2'), - ]; - - await tester.pumpWidget( - wrapWithStreamChat( - StreamMessageInputAttachmentList( - attachments: attachments, - onRemovePressed: (attachment) { - removedAttachment = attachment; - }, - ), - ), - ); - - final removeButtons = find.byType(RemoveAttachmentButton); - - // Tap the first remove button - await tester.tap(removeButtons.first); - await tester.pump(); - - // Expect the onRemovePressed callback to be called with the second - // attachment as they are reversed in the UI. - expect(removedAttachment, attachments[1]); - }, - ); - - testWidgets( - '''StreamMessageInputAttachmentList should display empty box if no attachments''', - (WidgetTester tester) async { - final attachments = []; - - await tester.pumpWidget( - wrapWithStreamChat( - StreamMessageInputAttachmentList( - attachments: attachments, - ), - ), - ); - - // Expect an empty box - expect(find.byType(SizedBox), findsOneWidget); - }, - ); - }); - - group('MessageInputFileAttachments tests', () { - testWidgets( - 'MessageInputFileAttachments should render file attachments', - (WidgetTester tester) async { - final attachments = [ - Attachment(type: 'file', id: 'file1'), - Attachment(type: 'file', id: 'file2'), - ]; - - await tester.pumpWidget( - wrapWithStreamChat( - MessageInputFileAttachments( - attachments: attachments, - ), - ), - ); - - // Expect 2 file attachments - expect(find.byType(StreamFileAttachment), findsNWidgets(2)); - }, - ); - - testWidgets( - 'MessageInputFileAttachments should call onRemovePressed callback', - (WidgetTester tester) async { - Attachment? removedAttachment; - - final attachments = [ - Attachment(type: 'file', id: 'file1'), - ]; - - await tester.pumpWidget( - wrapWithStreamChat( - MessageInputFileAttachments( - attachments: attachments, - onRemovePressed: (attachment) { - removedAttachment = attachment; - }, - ), - ), - ); - - final removeButton = find.byType(RemoveAttachmentButton); - - // Tap the remove button - await tester.tap(removeButton); - await tester.pump(); - - // Expect the onRemovePressed callback to be called with the attachment - expect(removedAttachment, attachments.first); - }, - ); - }); - - group('MessageInputMediaAttachments tests', () { - testWidgets( - 'MessageInputMediaAttachments should render media attachments', - (WidgetTester tester) async { - final attachments = [ - Attachment(type: 'media', id: 'media1'), - Attachment(type: 'media', id: 'media2'), - ]; - - await tester.pumpWidget( - wrapWithStreamChat( - MessageInputMediaAttachments( - attachments: attachments, - ), - ), - ); - - // Expect 2 media attachments - expect(find.byType(Stack), findsNWidgets(2)); - }, - ); - - testWidgets( - 'MessageInputMediaAttachments should display empty box if no attachments', - (WidgetTester tester) async { - final attachments = []; - - await tester.pumpWidget( - wrapWithStreamChat( - MessageInputMediaAttachments( - attachments: attachments, - ), - ), - ); - - // Expect an empty box - expect(find.byType(SizedBox), findsOneWidget); - }, - ); - }); - - group('StreamMediaAttachmentBuilder tests', () { - testWidgets( - 'StreamMediaAttachmentBuilder should render media attachment', - (WidgetTester tester) async { - final attachment = Attachment(type: 'media', id: 'media1'); - - await tester.pumpWidget( - wrapWithStreamChat( - StreamMediaAttachmentBuilder( - attachment: attachment, - onRemovePressed: (attachment) {}, - ), - ), - ); - - // Expect one media attachment widget - expect(find.byType(StreamMediaAttachmentBuilder), findsOneWidget); - }, - ); - - testWidgets( - 'StreamMediaAttachmentBuilder should call onRemovePressed callback', - (WidgetTester tester) async { - Attachment? removedAttachment; - - final attachment = Attachment(type: 'file', id: 'file1'); - - await tester.pumpWidget( - wrapWithStreamChat( - StreamMediaAttachmentBuilder( - attachment: attachment, - onRemovePressed: (attachment) { - removedAttachment = attachment; - }, - ), - ), - ); - - final removeButton = find.byType(RemoveAttachmentButton); - - // Tap the remove button - await tester.tap(removeButton); - await tester.pump(); - - // Expect the onRemovePressed callback to be called with the attachment - expect(removedAttachment, attachment); - }, - ); - }); -} diff --git a/packages/stream_chat_flutter/test/src/message_input/message_input_test.dart b/packages/stream_chat_flutter/test/src/message_input/message_input_test.dart deleted file mode 100644 index 1fe25ac33f..0000000000 --- a/packages/stream_chat_flutter/test/src/message_input/message_input_test.dart +++ /dev/null @@ -1,149 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - testWidgets( - 'checks message input features', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.isMuted).thenReturn(false); - when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(() => channel.extraDataStream).thenAnswer( - (i) => Stream.value({ - 'name': 'test', - }), - ); - when(() => channel.extraData).thenReturn({ - 'name': 'test', - }); - when(() => channelState.membersStream).thenAnswer( - (i) => Stream.value([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ) - ]), - ); - when(() => channelState.members).thenReturn([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ), - ]); - when(() => channelState.messages).thenReturn([ - Message( - text: 'hello', - user: User(id: 'other-user'), - ) - ]); - when(() => channelState.messagesStream).thenAnswer( - (i) => Stream.value([ - Message( - text: 'hello', - user: User(id: 'other-user'), - ) - ]), - ); - - await tester.pumpWidget(MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: const Scaffold( - body: StreamMessageInput(), - ), - ), - ), - )); - - expect(find.byType(TextField), findsOneWidget); - expect(find.byKey(const Key('messageInputText')), findsOneWidget); - }, - ); - - testWidgets( - 'checks message input slow mode', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.cooldown).thenReturn(10); - when(() => channel.cooldownStartedAt).thenReturn(DateTime.now()); - when(() => channel.client).thenReturn(client); - when(() => channel.isMuted).thenReturn(false); - when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(() => channel.extraDataStream).thenAnswer( - (i) => Stream.value({ - 'name': 'test', - }), - ); - when(() => channel.extraData).thenReturn({ - 'name': 'test', - }); - when(() => channelState.membersStream).thenAnswer( - (i) => Stream.value([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ) - ]), - ); - when(() => channelState.members).thenReturn([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ), - ]); - when(() => channelState.messages).thenReturn([ - Message( - text: 'hello', - user: User(id: 'other-user'), - ) - ]); - when(() => channelState.messagesStream).thenAnswer( - (i) => Stream.value([ - Message( - text: 'hello', - user: User(id: 'other-user'), - ) - ]), - ); - - await tester.pumpWidget(MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: const Scaffold( - body: StreamMessageInput(), - ), - ), - ), - )); - - expect(find.text('Slow mode ON'), findsOneWidget); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/message_list_view/bottom_row_test.dart b/packages/stream_chat_flutter/test/src/message_list_view/bottom_row_test.dart deleted file mode 100644 index 291d43e2fb..0000000000 --- a/packages/stream_chat_flutter/test/src/message_list_view/bottom_row_test.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - late Channel channel; - late ChannelClientState channelClientState; - - setUp(() { - channel = MockChannel(); - when(() => channel.on(any(), any(), any(), any())) - .thenAnswer((_) => const Stream.empty()); - channelClientState = MockChannelState(); - when(() => channel.state).thenReturn(channelClientState); - when(() => channelClientState.messages).thenReturn([ - Message( - id: 'parentId', - ) - ]); - }); - - setUpAll(() { - registerFallbackValue(Message()); - }); - - testWidgets('BottomRow', (tester) async { - final theme = StreamChatThemeData.light(); - final onThreadTap = MockValueChanged(); - - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: StreamChannel( - channel: channel, - child: BottomRow( - message: Message( - parentId: 'parentId', - ), - isDeleted: false, - showThreadReplyIndicator: false, - showUsername: false, - showInChannel: true, - showTimeStamp: false, - showEditedLabel: false, - reverse: false, - showSendingIndicator: false, - hasUrlAttachments: false, - isGiphy: false, - isOnlyEmoji: false, - messageTheme: theme.otherMessageTheme, - streamChatTheme: theme, - hasNonUrlAttachments: false, - streamChat: StreamChatState(), - onThreadTap: onThreadTap, - ), - ), - ), - ), - ), - ); - - await tester.tap(find.byType(GestureDetector)); - await tester.pumpAndSettle(); - - verify(() => onThreadTap.call(any())); - }); -} diff --git a/packages/stream_chat_flutter/test/src/message_list_view/floating_date_divider_test.dart b/packages/stream_chat_flutter/test/src/message_list_view/floating_date_divider_test.dart deleted file mode 100644 index 9b0ad86d30..0000000000 --- a/packages/stream_chat_flutter/test/src/message_list_view/floating_date_divider_test.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/scrollable_positioned_list/scrollable_positioned_list.dart'; -import 'package:stream_chat_flutter/src/message_list_view/floating_date_divider.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - testWidgets('FloatingDateDivider', (tester) async { - final client = MockClient(); - final clientState = MockClientState(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final createdAt = DateTime.now(); - - final itemPositionsListener = ValueNotifier( - [ - const ItemPosition( - index: 0, - itemLeadingEdge: 0, - itemTrailingEdge: 0.1, // Trailing edge is slightly visible. - ), - ], - ); - - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Stack( - children: [ - FloatingDateDivider( - reverse: false, - itemCount: 3, - itemPositionListener: itemPositionsListener, - messages: [Message(createdAt: createdAt)], - dateDividerBuilder: (dateTime) => Text('$dateTime'), - ), - ], - ), - ), - ), - ); - - expect(find.text('$createdAt'), findsOneWidget); - }); -} diff --git a/packages/stream_chat_flutter/test/src/message_list_view/message_list_view_test.dart b/packages/stream_chat_flutter/test/src/message_list_view/message_list_view_test.dart deleted file mode 100644 index ee09d24f11..0000000000 --- a/packages/stream_chat_flutter/test/src/message_list_view/message_list_view_test.dart +++ /dev/null @@ -1,244 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../../test_utils/data_generator.dart'; -import '../mocks.dart'; - -void main() { - late StreamChatClient client; - late Channel channel; - late ChannelClientState channelClientState; - late ClientState clientState; - - setUp(() { - client = MockClient(); - clientState = MockClientState(); - when(() => client.state).thenAnswer((_) => clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'testid')); - when(() => clientState.currentUserStream) - .thenAnswer((_) => Stream.value(OwnUser(id: 'testid'))); - channel = MockChannel(); - when(() => channel.on(any(), any(), any(), any())) - .thenAnswer((_) => const Stream.empty()); - channelClientState = MockChannelState(); - when(() => channel.client).thenReturn(client); - when(() => channel.state).thenReturn(channelClientState); - when(() => channelClientState.threadsStream) - .thenAnswer((_) => const Stream.empty()); - when(() => channelClientState.messagesStream) - .thenAnswer((_) => const Stream.empty()); - when(() => channelClientState.messages).thenReturn([]); - when(() => channelClientState.isUpToDate).thenReturn(true); - when(() => channelClientState.isUpToDateStream) - .thenAnswer((_) => Stream.value(true)); - when(() => channelClientState.unreadCountStream) - .thenAnswer((_) => Stream.value(0)); - when(() => channelClientState.unreadCount).thenReturn(0); - when(() => channelClientState.readStream) - .thenAnswer((_) => const Stream.empty()); - when(() => channelClientState.read).thenReturn([]); - when(() => channelClientState.membersStream) - .thenAnswer((_) => const Stream.empty()); - when(() => channelClientState.members).thenReturn([]); - }); - - // https://github.com/GetStream/stream-chat-flutter/issues/674 - testWidgets('renders empty message list view', (tester) async { - const emptyWidgetKey = Key('empty_widget'); - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: StreamMessageListView( - emptyBuilder: (_) => Container(key: emptyWidgetKey), - ), - ), - ), - ), - ); - await tester.pumpAndSettle(); - - expect(find.byType(StreamMessageListView), findsOneWidget); - expect(find.byKey(emptyWidgetKey), findsOneWidget); - }); - - testWidgets('renders a non empty message list view with custom background', - (tester) async { - final message = Message( - id: 'message1', - text: 'Hello world!', - user: User( - id: 'user', - name: 'Test User', - ), - ); - - when(() => channelClientState.messagesStream).thenAnswer( - (_) => Stream.value([message]), - ); - when(() => channelClientState.messages).thenReturn([message]); - - const nonEmptyWidgetKey = Key('non_empty_widget'); - await tester.runAsync(() async { - await tester.pumpWidget( - MaterialApp( - home: DefaultAssetBundle( - bundle: rootBundle, - child: StreamChat( - client: client, - streamChatThemeData: StreamChatThemeData.light().copyWith( - messageListViewTheme: const StreamMessageListViewThemeData( - backgroundColor: Colors.grey, - backgroundImage: DecorationImage( - image: AssetImage( - 'lib/assets/images/placeholder.png', - package: 'stream_chat_flutter', - ), - fit: BoxFit.none, - ), - ), - ), - child: StreamChannel( - channel: channel, - child: const StreamMessageListView( - key: nonEmptyWidgetKey, - ), - ), - ), - ), - ), - ); - await tester.pumpAndSettle(); - }); - - bool findBackground(Widget widget) => - widget is DecoratedBox && - widget.decoration is BoxDecoration && - (widget.decoration as BoxDecoration).image != null; - - expect(find.byType(StreamMessageListView), findsOneWidget); - expect(find.byKey(nonEmptyWidgetKey), findsOneWidget); - expect( - find.byWidgetPredicate( - findBackground, - description: 'findBackground', - ), - findsOneWidget, - ); - }); - - testWidgets('renders a non empty message list view with unread messages', - (tester) async { - final user = OwnUser(id: 'testid'); - final message = Message( - id: 'message1', - text: 'Hello world!', - user: User( - id: 'testid', - name: 'Test User', - ), - ); - - when(() => channelClientState.read) - .thenReturn([Read(lastRead: DateTime.now(), user: user)]); - - when(() => channelClientState.messagesStream).thenAnswer( - (_) => Stream.value([message]), - ); - when(() => channelClientState.messages).thenReturn([message]); - - const nonEmptyWidgetKey = Key('non_empty_widget'); - await tester.runAsync(() async { - await tester.pumpWidget( - MaterialApp( - home: DefaultAssetBundle( - bundle: rootBundle, - child: StreamChat( - client: client, - streamChatThemeData: StreamChatThemeData.light().copyWith( - messageListViewTheme: const StreamMessageListViewThemeData( - backgroundColor: Colors.grey, - backgroundImage: DecorationImage( - image: AssetImage( - 'lib/assets/images/placeholder.png', - package: 'stream_chat_flutter', - ), - fit: BoxFit.none, - ), - ), - ), - child: StreamChannel( - channel: channel, - child: const StreamMessageListView( - key: nonEmptyWidgetKey, - ), - ), - ), - ), - ), - ); - await tester.pumpAndSettle(); - }); - - expect(find.byType(StreamMessageListView), findsOneWidget); - expect(find.byKey(nonEmptyWidgetKey), findsOneWidget); - }); - - testWidgets('scrolls to bottom when arrow button is pressed', (tester) async { - final own = OwnUser(id: 'ownid'); - final other = User(id: 'otherid'); - final messages = generateConversation(20, users: [own, other]); - - when(() => channelClientState.messagesStream).thenAnswer( - (_) => Stream.value(messages), - ); - when(() => channelClientState.messages).thenReturn(messages); - - const nonEmptyWidgetKey = Key('non_empty_widget'); - await tester.runAsync(() async { - await tester.pumpWidget( - MaterialApp( - home: DefaultAssetBundle( - bundle: rootBundle, - child: StreamChat( - client: client, - streamChatThemeData: StreamChatThemeData.light().copyWith( - messageListViewTheme: const StreamMessageListViewThemeData( - backgroundColor: Colors.grey, - backgroundImage: DecorationImage( - image: AssetImage( - 'lib/assets/images/placeholder.png', - package: 'stream_chat_flutter', - ), - fit: BoxFit.none, - ), - ), - ), - child: StreamChannel( - channel: channel, - child: const StreamMessageListView( - key: nonEmptyWidgetKey, - initialScrollIndex: 5, - ), - ), - ), - ), - ), - ); - await tester.pumpAndSettle(); - }); - - expect(find.byType(FloatingActionButton), findsOneWidget); - - await tester.tap(find.byType(FloatingActionButton)); - await tester.pumpAndSettle(); - - expect(find.byType(FloatingActionButton), findsNothing); - }); -} diff --git a/packages/stream_chat_flutter/test/src/message_list_view/thread_separator_test.dart b/packages/stream_chat_flutter/test/src/message_list_view/thread_separator_test.dart deleted file mode 100644 index c0cfcb50e3..0000000000 --- a/packages/stream_chat_flutter/test/src/message_list_view/thread_separator_test.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/src/message_list_view/thread_separator.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -void main() { - testWidgets('ThreadSeparator', (tester) async { - await tester.pumpWidget( - MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData.light(), - child: Scaffold( - body: Center( - child: ThreadSeparator( - parentMessage: Message(), - ), - ), - ), - ), - ), - ); - - expect(find.byType(DecoratedBox), findsOneWidget); - expect(find.byType(Text), findsOneWidget); - }); -} diff --git a/packages/stream_chat_flutter/test/src/message_reactions_modal/message_reactions_modal_test.dart b/packages/stream_chat_flutter/test/src/message_reactions_modal/message_reactions_modal_test.dart deleted file mode 100644 index 8cf78680b3..0000000000 --- a/packages/stream_chat_flutter/test/src/message_reactions_modal/message_reactions_modal_test.dart +++ /dev/null @@ -1,121 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - testWidgets( - 'control test', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final themeData = ThemeData(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - final message = Message( - id: 'test', - text: 'test message', - user: User( - id: 'test-user', - ), - ); - await tester.pumpWidget( - MaterialApp( - theme: themeData, - home: StreamChat( - client: client, - streamChatThemeData: streamTheme, - child: StreamChannel( - channel: channel, - child: StreamMessageReactionsModal( - messageWidget: const Text( - 'test', - key: Key('MessageWidget'), - ), - message: message, - messageTheme: streamTheme.ownMessageTheme, - ), - ), - ), - ), - ); - - await tester.pump(const Duration(milliseconds: 1000)); - - expect(find.byType(StreamReactionBubble), findsNothing); - - expect(find.byType(StreamUserAvatar), findsNothing); - }, - ); - - testWidgets( - 'it should apply passed parameters', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final themeData = ThemeData(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - final message = Message( - id: 'test', - text: 'test message', - user: User( - id: 'test-user', - ), - latestReactions: [ - Reaction( - messageId: 'test', - user: User(id: 'testid'), - type: 'test', - ), - ], - ); - - // ignore: prefer_function_declarations_over_variables - final onUserAvatarTap = (u) => print('ok'); - - await tester.pumpWidget( - MaterialApp( - theme: themeData, - home: StreamChat( - client: client, - streamChatThemeData: streamTheme, - child: StreamChannel( - channel: channel, - child: StreamMessageReactionsModal( - messageWidget: const Text( - 'test', - key: Key('MessageWidget'), - ), - message: message, - messageTheme: streamTheme.ownMessageTheme, - reverse: true, - showReactionPicker: false, - onUserAvatarTap: onUserAvatarTap, - ), - ), - ), - ), - ); - - await tester.pump(const Duration(milliseconds: 1000)); - - expect(find.byKey(const Key('MessageWidget')), findsOneWidget); - - expect(find.byType(StreamReactionBubble), findsOneWidget); - expect(find.byType(StreamUserAvatar), findsOneWidget); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/message_widget/deleted_message_test.dart b/packages/stream_chat_flutter/test/src/message_widget/deleted_message_test.dart deleted file mode 100644 index 1a33a4db21..0000000000 --- a/packages/stream_chat_flutter/test/src/message_widget/deleted_message_test.dart +++ /dev/null @@ -1,207 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; -import '../mocks.dart'; - -void main() { - testWidgets('control test', (tester) async { - final client = MockClient(); - final clientState = MockClientState(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: const Scaffold( - body: StreamDeletedMessage( - messageTheme: StreamMessageThemeData( - createdAtStyle: TextStyle( - color: Colors.black, - ), - messageTextStyle: TextStyle(), - ), - ), - ), - ), - ), - ); - - expect(find.text('Message deleted'), findsOneWidget); - }); - - goldenTest( - 'control golden light', - fileName: 'deleted_message_light', - constraints: const BoxConstraints.tightFor(width: 200, height: 200), - builder: () { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.extraDataStream).thenAnswer( - (i) => Stream.value({ - 'name': 'test', - }), - ); - when(() => channel.extraData).thenReturn({ - 'name': 'test', - }); - - when(() => clientState.totalUnreadCount).thenReturn(10); - when(() => clientState.totalUnreadCountStream) - .thenAnswer((i) => Stream.value(10)); - - final materialTheme = ThemeData.light( - useMaterial3: false, - ); - final theme = StreamChatThemeData.fromTheme(materialTheme); - return MaterialAppWrapper( - theme: materialTheme, - home: StreamChat( - streamChatThemeData: theme, - client: client, - connectivityStream: Stream.value([ConnectivityResult.mobile]), - child: StreamChannel( - showLoading: false, - channel: channel, - child: Scaffold( - body: Center( - child: StreamDeletedMessage( - messageTheme: theme.ownMessageTheme, - ), - ), - ), - ), - ), - ); - }, - ); - - goldenTest( - 'control golden dark', - fileName: 'deleted_message_dark', - constraints: const BoxConstraints.tightFor(width: 200, height: 200), - builder: () { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.extraDataStream).thenAnswer( - (i) => Stream.value({ - 'name': 'test', - }), - ); - when(() => channel.extraData).thenReturn({ - 'name': 'test', - }); - - when(() => clientState.totalUnreadCount).thenReturn(10); - when(() => clientState.totalUnreadCountStream) - .thenAnswer((i) => Stream.value(10)); - - final materialTheme = ThemeData.dark( - useMaterial3: false, - ); - final theme = StreamChatThemeData.fromTheme(materialTheme); - return MaterialAppWrapper( - theme: materialTheme, - home: StreamChat( - streamChatThemeData: theme, - client: client, - connectivityStream: Stream.value([ConnectivityResult.mobile]), - child: StreamChannel( - showLoading: false, - channel: channel, - child: Scaffold( - body: Center( - child: StreamDeletedMessage( - messageTheme: theme.ownMessageTheme, - ), - ), - ), - ), - ), - ); - }, - ); - - goldenTest( - 'golden customization test', - fileName: 'deleted_message_custom', - constraints: const BoxConstraints.tightFor(width: 200, height: 200), - builder: () { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.extraDataStream).thenAnswer( - (i) => Stream.value({ - 'name': 'test', - }), - ); - when(() => channel.extraData).thenReturn({ - 'name': 'test', - }); - - when(() => clientState.totalUnreadCount).thenReturn(10); - when(() => clientState.totalUnreadCountStream) - .thenAnswer((i) => Stream.value(10)); - - final materialTheme = ThemeData.light( - useMaterial3: false, - ); - final theme = StreamChatThemeData.fromTheme(materialTheme); - return MaterialAppWrapper( - theme: materialTheme, - home: StreamChat( - streamChatThemeData: theme, - client: client, - connectivityStream: Stream.value([ConnectivityResult.mobile]), - child: StreamChannel( - showLoading: false, - channel: channel, - child: Scaffold( - body: Center( - child: StreamDeletedMessage( - messageTheme: theme.ownMessageTheme, - reverse: true, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - ), - ), - ), - ), - ); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/message_widget/goldens/ci/deleted_message_custom.png b/packages/stream_chat_flutter/test/src/message_widget/goldens/ci/deleted_message_custom.png deleted file mode 100644 index 1a7c5174d4..0000000000 Binary files a/packages/stream_chat_flutter/test/src/message_widget/goldens/ci/deleted_message_custom.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/message_widget/goldens/ci/deleted_message_dark.png b/packages/stream_chat_flutter/test/src/message_widget/goldens/ci/deleted_message_dark.png deleted file mode 100644 index 9b7f30f4b5..0000000000 Binary files a/packages/stream_chat_flutter/test/src/message_widget/goldens/ci/deleted_message_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/message_widget/goldens/ci/deleted_message_light.png b/packages/stream_chat_flutter/test/src/message_widget/goldens/ci/deleted_message_light.png deleted file mode 100644 index 6c46e0739d..0000000000 Binary files a/packages/stream_chat_flutter/test/src/message_widget/goldens/ci/deleted_message_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/message_widget/goldens/ci/message_text.png b/packages/stream_chat_flutter/test/src/message_widget/goldens/ci/message_text.png deleted file mode 100644 index 656da4e1cc..0000000000 Binary files a/packages/stream_chat_flutter/test/src/message_widget/goldens/ci/message_text.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/message_widget/message_text_test.dart b/packages/stream_chat_flutter/test/src/message_widget/message_text_test.dart deleted file mode 100644 index e02b2b119f..0000000000 --- a/packages/stream_chat_flutter/test/src/message_widget/message_text_test.dart +++ /dev/null @@ -1,242 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_markdown/flutter_markdown.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; -import '../mocks.dart'; -import '../simple_frame.dart'; - -void expectTextStrings(Iterable widgets, List strings) { - var currentString = 0; - for (final widget in widgets) { - if (widget is RichText) { - final span = widget.text as TextSpan; - final text = _extractTextFromTextSpan(span); - expect(text, equals(strings[currentString])); - currentString += 1; - } - } -} - -String _extractTextFromTextSpan(TextSpan span) { - var text = span.text ?? ''; - if (span.children != null) { - for (final child in span.children! as Iterable) { - text += _extractTextFromTextSpan(child); - } - } - return text; -} - -void main() { - testWidgets( - 'it should show correct message text', - (WidgetTester tester) async { - final currentUser = OwnUser(id: 'user-id'); - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(currentUser); - when(() => clientState.currentUserStream) - .thenAnswer((_) => Stream.value(currentUser)); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.isMuted).thenReturn(false); - when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ - 'name': 'test', - })); - when(() => channel.extraData).thenReturn({ - 'name': 'test', - }); - - await tester.pumpWidget(MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: Scaffold( - body: StreamMessageText( - message: Message( - text: 'demo', - ), - messageTheme: streamTheme.otherMessageTheme), - ), - ), - ), - )); - - expect(find.byType(MarkdownBody), findsOneWidget); - }, - ); - - group('Message with i18n field', () { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - const messageTheme = StreamMessageThemeData(); - - final currentUser = OwnUser( - id: 'sahil', - language: 'hi', - ); - - setUp(() { - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(currentUser); - when(() => clientState.currentUserStream) - .thenAnswer((_) => Stream.value(currentUser)); - - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.isMuted).thenReturn(false); - when(() => channel.isMutedStream).thenAnswer((_) => Stream.value(false)); - }); - - testWidgets( - 'should show correct translated message text as per user language', - (WidgetTester tester) async { - final message = Message( - text: 'Hello', - i18n: const { - 'en_text': 'Hello', - 'hi_text': 'ą¤Øą¤®ą¤øą„ą¤¤ą„‡', - 'language': 'en', - }, - ); - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: Scaffold( - body: StreamMessageText( - message: message, - messageTheme: messageTheme, - ), - ), - ), - ), - ), - ); - - expect(find.byType(MarkdownBody), findsOneWidget); - - final widgets = tester.allWidgets; - expectTextStrings(widgets, ['ą¤Øą¤®ą¤øą„ą¤¤ą„‡']); - }, - ); - - testWidgets( - '''should show default text if i18n does not contain translations as per user language''', - (WidgetTester tester) async { - final message = Message( - text: 'Hello', - i18n: const { - 'en_text': 'Hello', - 'fr_text': 'Bonjour', - 'language': 'en', - }, - ); - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: Scaffold( - body: StreamMessageText( - message: message, - messageTheme: messageTheme, - ), - ), - ), - ), - ), - ); - - expect(find.byType(MarkdownBody), findsOneWidget); - - final widgets = tester.allWidgets; - expectTextStrings(widgets, ['Hello']); - }, - ); - }); - - goldenTest( - 'control test', - fileName: 'message_text', - constraints: const BoxConstraints.tightFor(width: 300, height: 200), - builder: () { - final currentUser = OwnUser(id: 'user-id'); - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.fromTheme(themeData); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(currentUser); - when(() => clientState.currentUserStream) - .thenAnswer((_) => Stream.value(currentUser)); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.isMuted).thenReturn(false); - when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(() => channel.extraDataStream).thenAnswer( - (i) => Stream.value({ - 'name': 'test', - }), - ); - when(() => channel.extraData).thenReturn({ - 'name': 'test', - }); - - const messageText = ''' -a message. -with multiple lines -and a list: -- a. okasd -- b lllll - -cool.'''; - - return MaterialAppWrapper( - home: SimpleFrame( - child: StreamChat( - client: client, - connectivityStream: Stream.value([ConnectivityResult.wifi]), - child: StreamChannel( - channel: channel, - child: Scaffold( - body: StreamMessageText( - message: Message( - text: messageText, - ), - messageTheme: streamTheme.otherMessageTheme, - ), - ), - ), - ), - ), - ); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/message_widget/username_test.dart b/packages/stream_chat_flutter/test/src/message_widget/username_test.dart deleted file mode 100644 index bde411cb38..0000000000 --- a/packages/stream_chat_flutter/test/src/message_widget/username_test.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/src/message_widget/username.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -void main() { - testWidgets('Username', (tester) async { - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: Username( - message: Message(), - messageTheme: StreamChatThemeData.light().ownMessageTheme, - ), - ), - ), - ), - ); - - expect(find.byType(Text), findsOneWidget); - }); -} diff --git a/packages/stream_chat_flutter/test/src/misc/audio_waveform_test.dart b/packages/stream_chat_flutter/test/src/misc/audio_waveform_test.dart deleted file mode 100644 index e9a8b75902..0000000000 --- a/packages/stream_chat_flutter/test/src/misc/audio_waveform_test.dart +++ /dev/null @@ -1,230 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/src/misc/audio_waveform.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - group('StreamAudioWaveformSlider', () { - // Prepare random waveform data - final waveformData = generateStaticWaveform(length: 30); - - testWidgets('Handles seek interactions', (WidgetTester tester) async { - final onChangeStart = MockValueChanged(); - final onChanged = MockValueChanged(); - final onChangeEnd = MockValueChanged(); - - await tester.pumpWidget( - _wrapWithMaterialApp( - ConstrainedBox( - constraints: const BoxConstraints.tightFor(width: 300, height: 50), - child: StreamAudioWaveformSlider( - limit: 35, - waveform: waveformData, - onChangeStart: onChangeStart, - onChanged: onChanged, - onChangeEnd: onChangeEnd, - ), - ), - ), - ); - - final topLeft = tester.getTopLeft(find.byType(StreamAudioWaveformSlider)); - - // Start gesture - final gesture = await tester.startGesture(topLeft); - verify(() => onChangeStart(0)).called(1); - - // Move gesture to the middle of the slider - await gesture.moveBy(const Offset(150, 0)); - verify(() => onChanged(0.5)).called(1); - - // Move gesture to the end of the slider - await gesture.moveBy(const Offset(300, 0)); - verify(() => onChanged(1)).called(1); - - // End gesture - await gesture.up(); - verify(() => onChangeEnd(1)).called(1); - }); - - for (final brightness in Brightness.values) { - final theme = brightness.name; - - goldenTest( - '[$theme] -> should look fine', - fileName: 'stream_audio_waveform_slider_$theme', - constraints: const BoxConstraints.tightFor(width: 300, height: 50), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - Padding( - padding: const EdgeInsets.all(8), - child: StreamAudioWaveformSlider( - limit: 30, - waveform: waveformData, - onChanged: (double value) {}, - ), - ), - ), - ); - - goldenTest( - '[$theme] -> should paint waveform bars inverted', - fileName: 'stream_audio_waveform_slider_inverted_$theme', - constraints: const BoxConstraints.tightFor(width: 300, height: 50), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - Padding( - padding: const EdgeInsets.all(8), - child: StreamAudioWaveformSlider( - limit: 30, - inverse: false, - waveform: waveformData, - onChanged: (double value) {}, - ), - ), - ), - ); - - goldenTest( - '[$theme] -> should look fine with progress', - fileName: 'stream_audio_waveform_slider_progress_$theme', - constraints: const BoxConstraints.tightFor(width: 300, height: 50), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - Padding( - padding: const EdgeInsets.all(8), - child: StreamAudioWaveformSlider( - limit: 30, - progress: 0.5, - waveform: waveformData, - onChanged: (double value) {}, - ), - ), - ), - ); - - goldenTest( - '[$theme] -> should look fine with custom properties', - fileName: 'stream_audio_waveform_slider_custom_$theme', - constraints: const BoxConstraints.tightFor(width: 300, height: 50), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - Padding( - padding: const EdgeInsets.all(8), - child: StreamAudioWaveformSlider( - limit: 30, - color: Colors.blue, - progress: 0.5, - progressColor: Colors.green, - waveform: waveformData, - onChanged: (double value) {}, - ), - ), - ), - ); - - goldenTest( - '[$theme] -> should build empty waveform if no data', - fileName: 'stream_audio_waveform_slider_empty_$theme', - constraints: const BoxConstraints.tightFor(width: 300, height: 50), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - Padding( - padding: const EdgeInsets.all(8), - child: StreamAudioWaveformSlider( - limit: 30, - waveform: const [], - onChanged: (double value) {}, - ), - ), - ), - ); - - goldenTest( - '[$theme] -> should build empty waveform if less data', - fileName: 'stream_audio_waveform_slider_less_data_$theme', - constraints: const BoxConstraints.tightFor(width: 300, height: 50), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - Padding( - padding: const EdgeInsets.all(8), - child: StreamAudioWaveformSlider( - limit: 50, - waveform: waveformData, - onChanged: (double value) {}, - ), - ), - ), - ); - } - }); -} - -List generateStaticWaveform({ - int length = 35, -}) { - // A predefined pattern that mimics audio signal variations - final basePattern = [ - 0.2, - 0.4, - 0.6, - 0.3, - 0.5, - 0.7, - 0.1, - 0.8, - 0.2, - 0.6, - 0.4, - 0.9, - 0.3, - 0.7, - 0.5, - 0.2, - 0.8, - 0.1, - 0.6, - 0.4, - 0.7, - 0.3, - 0.5, - 0.2, - 0.8, - 0.1, - 0.6, - 0.4, - 0.7, - 0.3, - 0.5, - 0.2, - 0.8, - 0.1, - 0.6, - ]; - - // Ensure the returned list matches the requested length - return basePattern.take(length).toList(); -} - -Widget _wrapWithMaterialApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - debugShowCheckedModeBanner: false, - home: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: Builder(builder: (context) { - final theme = StreamChatTheme.of(context); - return Scaffold( - backgroundColor: theme.colorTheme.appBg, - body: Center(child: widget), - ); - }), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/misc/back_button_test.dart b/packages/stream_chat_flutter/test/src/misc/back_button_test.dart deleted file mode 100644 index c4ae6efc2a..0000000000 --- a/packages/stream_chat_flutter/test/src/misc/back_button_test.dart +++ /dev/null @@ -1,146 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - testWidgets( - 'BackButton control test', - (WidgetTester tester) async { - final theme = ThemeData(); - await tester.pumpWidget( - MaterialApp( - home: const Material(child: Text('Home')), - routes: { - '/next': (BuildContext context) { - return Material( - child: Center( - child: StreamChatTheme( - data: StreamChatThemeData.fromTheme(theme), - child: const StreamBackButton(), - ), - ), - ); - }, - }, - ), - ); - - // ignore: unawaited_futures - tester.state(find.byType(Navigator)).pushNamed('/next'); - - await tester.pumpAndSettle(); - - await tester.tap(find.byType(StreamBackButton)); - - await tester.pumpAndSettle(); - - expect(find.text('Home'), findsOneWidget); - }, - ); - - testWidgets( - 'it should not throw errors if cannot pop', - (WidgetTester tester) async { - final theme = ThemeData(); - await tester.pumpWidget( - MaterialApp( - home: Material( - child: Center( - child: StreamChatTheme( - data: StreamChatThemeData.fromTheme(theme), - child: const StreamBackButton(), - ), - ), - ), - ), - ); - - await tester.pumpAndSettle(); - - await tester.tap(find.byType(StreamBackButton)); - - await tester.pumpAndSettle(); - - expect(find.byType(StreamBackButton), findsOneWidget); - }, - ); - - testWidgets( - 'BackButton onPressed overrides default pop behavior', - (WidgetTester tester) async { - final theme = ThemeData(); - var customCallbackWasCalled = false; - await tester.pumpWidget( - MaterialApp( - home: const Material(child: Text('Home')), - routes: { - '/next': (BuildContext context) { - return Material( - child: Center( - child: StreamChatTheme( - data: StreamChatThemeData.fromTheme(theme), - child: StreamBackButton( - onPressed: () => customCallbackWasCalled = true, - ), - ), - ), - ); - }, - }, - ), - ); - - // ignore: unawaited_futures - tester.state(find.byType(Navigator)).pushNamed('/next'); - - await tester.pumpAndSettle(); - - expect(find.text('Home'), findsNothing); // Start off on the second page. - expect( - customCallbackWasCalled, - false, - ); // customCallbackWasCalled should still be false. - await tester.tap(find.byType(StreamBackButton)); - - await tester.pumpAndSettle(); - - // We're still on the second page. - expect(find.text('Home'), findsNothing); - // But the custom callback is called. - expect(customCallbackWasCalled, true); - }, - ); - - testWidgets( - 'it should show unread count', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.totalUnreadCount).thenAnswer((_) => 0); - when(() => clientState.totalUnreadCountStream) - .thenAnswer((_) => Stream.value(0)); - - await tester.pumpWidget( - MaterialApp( - home: Material( - child: Center( - child: StreamChat( - client: client, - child: const StreamBackButton( - showUnreadCount: true, - ), - ), - ), - ), - ), - ); - - expect(find.byType(StreamUnreadIndicator), findsOneWidget); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/misc/date_divider_test.dart b/packages/stream_chat_flutter/test/src/misc/date_divider_test.dart deleted file mode 100644 index 0b88ffc741..0000000000 --- a/packages/stream_chat_flutter/test/src/misc/date_divider_test.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - testWidgets( - 'it should show basic channel information', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: Scaffold( - body: StreamDateDivider( - dateTime: DateTime.now(), - ), - ), - ), - ), - ); - - expect(find.text('Today'), findsOneWidget); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_2.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_2.png deleted file mode 100644 index a7daaa3051..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_2.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_3_dark.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_3_dark.png deleted file mode 100644 index 2a88d12d61..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_3_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_3_light.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_3_light.png deleted file mode 100644 index a2f9afc0bb..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_3_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_like_dark.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_like_dark.png deleted file mode 100644 index b9fdc003ce..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_like_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_like_light.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_like_light.png deleted file mode 100644 index 5d5d6e1ba8..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_like_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_custom_dark.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_custom_dark.png deleted file mode 100644 index 8480b35ee1..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_custom_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_custom_light.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_custom_light.png deleted file mode 100644 index 38e1ce5bf7..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_custom_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_dark.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_dark.png deleted file mode 100644 index d0e6aab285..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_empty_dark.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_empty_dark.png deleted file mode 100644 index 6d63fa50ba..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_empty_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_empty_light.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_empty_light.png deleted file mode 100644 index f914ffb2ad..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_empty_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_inverted_dark.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_inverted_dark.png deleted file mode 100644 index 2d05d18ac3..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_inverted_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_inverted_light.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_inverted_light.png deleted file mode 100644 index 1434322d64..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_inverted_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_less_data_dark.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_less_data_dark.png deleted file mode 100644 index 77a1e1299b..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_less_data_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_less_data_light.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_less_data_light.png deleted file mode 100644 index 12b942eebc..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_less_data_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_light.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_light.png deleted file mode 100644 index 26add2a992..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_progress_dark.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_progress_dark.png deleted file mode 100644 index 9d1854a8e0..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_progress_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_progress_light.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_progress_light.png deleted file mode 100644 index 4951487559..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_progress_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_properties_dark.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_properties_dark.png deleted file mode 100644 index c821b5e445..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_properties_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_properties_light.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_properties_light.png deleted file mode 100644 index 38e1ce5bf7..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_audio_waveform_slider_properties_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_timestamp_dark.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_timestamp_dark.png deleted file mode 100644 index 4e7d68932c..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_timestamp_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_timestamp_light.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_timestamp_light.png deleted file mode 100644 index e8a6642fed..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/stream_timestamp_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/system_message_dark.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/system_message_dark.png deleted file mode 100644 index c22da26c2b..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/system_message_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/goldens/ci/system_message_light.png b/packages/stream_chat_flutter/test/src/misc/goldens/ci/system_message_light.png deleted file mode 100644 index 2eae251442..0000000000 Binary files a/packages/stream_chat_flutter/test/src/misc/goldens/ci/system_message_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/misc/info_tile_test.dart b/packages/stream_chat_flutter/test/src/misc/info_tile_test.dart deleted file mode 100644 index 9cdaa0ee52..0000000000 --- a/packages/stream_chat_flutter/test/src/misc/info_tile_test.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_portal/flutter_portal.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -void main() { - testWidgets( - 'control test', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: const Scaffold( - body: Portal( - child: SizedBox( - child: StreamInfoTile( - showMessage: true, - message: 'message', - child: Text('test'), - ), - ), - ), - ), - ), - ), - ); - - expect(find.text('message'), findsOneWidget); - }, - ); - - testWidgets( - 'it should hide when passing showMessage: false', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: const Scaffold( - body: Portal( - child: SizedBox( - child: StreamInfoTile( - showMessage: false, - message: 'message', - child: Text('test'), - ), - ), - ), - ), - ), - ), - ); - - expect(find.text('message'), findsNothing); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/misc/reaction_bubble_test.dart b/packages/stream_chat_flutter/test/src/misc/reaction_bubble_test.dart deleted file mode 100644 index 2dd7ef0b19..0000000000 --- a/packages/stream_chat_flutter/test/src/misc/reaction_bubble_test.dart +++ /dev/null @@ -1,238 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; -import '../mocks.dart'; - -void main() { - goldenTest( - 'it should show a like - light theme', - fileName: 'reaction_bubble_like_light', - constraints: const BoxConstraints.tightFor(width: 100, height: 100), - builder: () { - final client = MockClient(); - final clientState = MockClientState(); - final themeData = ThemeData.light( - useMaterial3: false, - ); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - final theme = StreamChatThemeData.fromTheme(themeData); - return MaterialAppWrapper( - theme: themeData, - home: StreamChat( - client: client, - streamChatThemeData: theme, - connectivityStream: Stream.value([ConnectivityResult.mobile]), - child: Scaffold( - body: Center( - child: StreamReactionBubble( - reactions: [ - Reaction( - type: 'like', - user: User(id: 'test'), - ), - ], - borderColor: theme.ownMessageTheme.reactionsBorderColor!, - backgroundColor: - theme.ownMessageTheme.reactionsBackgroundColor!, - maskColor: theme.ownMessageTheme.reactionsMaskColor!, - ), - ), - ), - ), - ); - }, - ); - - goldenTest( - 'it should show a like - dark theme', - fileName: 'reaction_bubble_like_dark', - constraints: const BoxConstraints.tightFor(width: 100, height: 100), - builder: () { - final client = MockClient(); - final clientState = MockClientState(); - final themeData = ThemeData.dark(); - final theme = StreamChatThemeData.fromTheme(themeData); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - return MaterialAppWrapper( - theme: themeData, - home: StreamChat( - client: client, - streamChatThemeData: StreamChatThemeData.fromTheme(themeData), - connectivityStream: Stream.value([ConnectivityResult.mobile]), - child: Scaffold( - body: Center( - child: StreamReactionBubble( - reactions: [ - Reaction( - type: 'like', - user: User(id: 'test'), - ), - ], - borderColor: theme.ownMessageTheme.reactionsBorderColor!, - backgroundColor: - theme.ownMessageTheme.reactionsBackgroundColor!, - maskColor: theme.ownMessageTheme.reactionsMaskColor!, - ), - ), - ), - ), - ); - }, - ); - - goldenTest( - 'it should show three reactions - light theme', - fileName: 'reaction_bubble_3_light', - constraints: const BoxConstraints.tightFor(width: 140, height: 140), - builder: () { - final client = MockClient(); - final clientState = MockClientState(); - final themeData = ThemeData.light(); - final theme = StreamChatThemeData.fromTheme(themeData); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - return MaterialAppWrapper( - theme: themeData, - home: StreamChat( - client: client, - streamChatThemeData: StreamChatThemeData.fromTheme(themeData), - connectivityStream: Stream.value([ConnectivityResult.mobile]), - child: Scaffold( - body: Center( - child: StreamReactionBubble( - reactions: [ - Reaction( - type: 'like', - user: User(id: 'test'), - ), - Reaction( - type: 'like', - user: User(id: 'user-id'), - ), - Reaction( - type: 'like', - user: User(id: 'test'), - ), - ], - borderColor: theme.ownMessageTheme.reactionsBorderColor!, - backgroundColor: - theme.ownMessageTheme.reactionsBackgroundColor!, - maskColor: theme.ownMessageTheme.reactionsMaskColor!, - ), - ), - ), - ), - ); - }, - ); - - goldenTest( - 'it should show three reactions - dark theme', - fileName: 'reaction_bubble_3_dark', - constraints: const BoxConstraints.tightFor(width: 140, height: 140), - builder: () { - final client = MockClient(); - final clientState = MockClientState(); - final themeData = ThemeData.dark(); - final theme = StreamChatThemeData.fromTheme(themeData); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - return MaterialAppWrapper( - theme: themeData, - home: StreamChat( - client: client, - streamChatThemeData: StreamChatThemeData.fromTheme(themeData), - connectivityStream: Stream.value([ConnectivityResult.mobile]), - child: Scaffold( - body: Center( - child: StreamReactionBubble( - reactions: [ - Reaction( - type: 'like', - user: User(id: 'test'), - ), - Reaction( - type: 'like', - user: User(id: 'user-id'), - ), - Reaction( - type: 'like', - user: User(id: 'test'), - ), - ], - borderColor: theme.ownMessageTheme.reactionsBorderColor!, - backgroundColor: - theme.ownMessageTheme.reactionsBackgroundColor!, - maskColor: theme.ownMessageTheme.reactionsMaskColor!, - ), - ), - ), - ), - ); - }, - ); - - goldenTest( - 'it should show two reactions with customized ui', - fileName: 'reaction_bubble_2', - constraints: const BoxConstraints.tightFor(width: 200, height: 200), - builder: () { - final client = MockClient(); - final clientState = MockClientState(); - final themeData = ThemeData( - useMaterial3: false, - ); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - - return MaterialAppWrapper( - theme: themeData, - home: StreamChat( - client: client, - connectivityStream: Stream.value([ConnectivityResult.mobile]), - streamChatThemeData: StreamChatThemeData.fromTheme(themeData), - child: Scaffold( - body: Center( - child: StreamReactionBubble( - reactions: [ - Reaction( - type: 'like', - user: User(id: 'test'), - ), - Reaction( - type: 'love', - user: User(id: 'user-id'), - ), - Reaction( - type: 'unknown', - user: User(id: 'test'), - ), - ], - borderColor: Colors.red, - backgroundColor: Colors.blue, - maskColor: Colors.green, - reverse: true, - flipTail: true, - tailCirclesSpacing: 4, - ), - ), - ), - ), - ); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/misc/system_message_test.dart b/packages/stream_chat_flutter/test/src/misc/system_message_test.dart deleted file mode 100644 index 932e8cd747..0000000000 --- a/packages/stream_chat_flutter/test/src/misc/system_message_test.dart +++ /dev/null @@ -1,167 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; -import '../mocks.dart'; - -void main() { - testWidgets( - 'it should show total unread count', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.extraDataStream).thenAnswer( - (i) => Stream.value({ - 'name': 'test', - }), - ); - when(() => channel.extraData).thenReturn({ - 'name': 'test', - }); - - when(() => clientState.totalUnreadCount).thenReturn(10); - when(() => clientState.totalUnreadCountStream) - .thenAnswer((i) => Stream.value(10)); - - var tapped = false; - - await tester.pumpWidget(MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: Scaffold( - body: StreamSystemMessage( - onMessageTap: (m) => tapped = true, - message: Message( - text: 'demo message', - ), - ), - ), - ), - ), - )); - - await tester.tap(find.byType(StreamSystemMessage)); - - expect(find.text('demo message'), findsOneWidget); - expect(tapped, true); - }, - ); - - goldenTest( - 'control golden light', - fileName: 'system_message_light', - constraints: const BoxConstraints.tightFor(width: 200, height: 200), - builder: () { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.extraDataStream).thenAnswer( - (i) => Stream.value({ - 'name': 'test', - }), - ); - when(() => channel.extraData).thenReturn({ - 'name': 'test', - }); - - when(() => clientState.totalUnreadCount).thenReturn(10); - when(() => clientState.totalUnreadCountStream) - .thenAnswer((i) => Stream.value(10)); - - return MaterialAppWrapper( - theme: ThemeData.light(), - home: StreamChat( - client: client, - connectivityStream: Stream.value([ConnectivityResult.mobile]), - child: StreamChannel( - showLoading: false, - channel: channel, - child: Scaffold( - body: Center( - child: StreamSystemMessage( - message: Message( - text: 'demo message', - ), - ), - ), - ), - ), - ), - ); - }, - ); - - goldenTest( - 'control golden dark', - fileName: 'system_message_dark', - constraints: const BoxConstraints.tightFor(width: 200, height: 200), - builder: () { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.extraDataStream).thenAnswer( - (i) => Stream.value({ - 'name': 'test', - }), - ); - when(() => channel.extraData).thenReturn({ - 'name': 'test', - }); - - when(() => clientState.totalUnreadCount).thenReturn(10); - when(() => clientState.totalUnreadCountStream) - .thenAnswer((i) => Stream.value(10)); - - return MaterialAppWrapper( - theme: ThemeData.dark(), - home: StreamChat( - client: client, - connectivityStream: Stream.value([ConnectivityResult.mobile]), - child: StreamChannel( - showLoading: false, - channel: channel, - child: Scaffold( - body: Center( - child: StreamSystemMessage( - message: Message( - text: 'demo message', - ), - ), - ), - ), - ), - ), - ); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/misc/thread_header_test.dart b/packages/stream_chat_flutter/test/src/misc/thread_header_test.dart deleted file mode 100644 index 05f6e56b77..0000000000 --- a/packages/stream_chat_flutter/test/src/misc/thread_header_test.dart +++ /dev/null @@ -1,148 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../material_app_wrapper.dart'; -import '../mocks.dart'; - -void main() { - testWidgets( - 'control test', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.isMuted).thenReturn(false); - when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(() => channel.name).thenReturn('test'); - when(() => channel.nameStream).thenAnswer((i) => Stream.value('test')); - when(() => channelState.unreadCount).thenReturn(1); - when(() => channelState.unreadCountStream) - .thenAnswer((i) => Stream.value(1)); - when(() => channelState.membersStream).thenAnswer( - (i) => Stream.value([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ) - ]), - ); - when(() => channelState.members).thenReturn([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ), - ]); - when(() => client.wsConnectionStatusStream) - .thenAnswer((_) => Stream.value(ConnectionStatus.connecting)); - when(() => clientState.totalUnreadCount).thenAnswer((i) => 1); - when(() => clientState.totalUnreadCountStream) - .thenAnswer((i) => Stream.value(1)); - - await tester.pumpWidget(MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: Scaffold( - body: StreamThreadHeader( - parent: Message(), - ), - ), - ), - ), - )); - await tester.pumpAndSettle(); - - expect(find.text('with '), findsOneWidget); - expect(find.byType(StreamChannelName), findsOneWidget); - expect(find.byType(StreamBackButton), findsOneWidget); - expect(find.text('Thread Reply'), findsOneWidget); - }, - ); - - testWidgets( - 'it should apply passed props', - (WidgetTester tester) async { - final client = MockClient(); - final clientState = MockClientState(); - final channel = MockChannel(); - final channelState = MockChannelState(); - final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - - when(() => client.state).thenReturn(clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id')); - when(() => channel.lastMessageAt).thenReturn(lastMessageAt); - when(() => channel.state).thenReturn(channelState); - when(() => channel.client).thenReturn(client); - when(() => channel.isMuted).thenReturn(false); - when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(() => channel.extraDataStream).thenAnswer( - (i) => Stream.value({ - 'name': 'test', - }), - ); - when(() => channel.extraData).thenReturn({ - 'name': 'test', - }); - when(() => channelState.unreadCount).thenReturn(1); - when(() => channelState.unreadCountStream) - .thenAnswer((i) => Stream.value(1)); - when(() => channelState.membersStream).thenAnswer( - (i) => Stream.value([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ) - ]), - ); - when(() => channelState.members).thenReturn([ - Member( - userId: 'user-id', - user: User(id: 'user-id'), - ), - ]); - - var tapped = false; - await tester.pumpWidget(MaterialAppWrapper( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: Scaffold( - body: StreamThreadHeader( - parent: Message(), - subtitle: const Text('subtitle'), - leading: const Text('leading'), - title: const Text('title'), - onTitleTap: () { - tapped = true; - }, - actions: const [ - Text('action'), - ], - ), - ), - ), - ), - )); - await tester.pumpAndSettle(); - - expect(find.text('title'), findsOneWidget); - await tester.tap(find.text('title')); - expect(tapped, true); - expect(find.text('subtitle'), findsOneWidget); - expect(find.text('action'), findsOneWidget); - expect(find.text('leading'), findsOneWidget); - }, - ); -} diff --git a/packages/stream_chat_flutter/test/src/misc/timestamp_test.dart b/packages/stream_chat_flutter/test/src/misc/timestamp_test.dart deleted file mode 100644 index f08263ae77..0000000000 --- a/packages/stream_chat_flutter/test/src/misc/timestamp_test.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/misc/timestamp.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -void main() { - for (final brightness in Brightness.values) { - goldenTest( - '[${brightness.name}] -> StreamTimestamp looks fine', - fileName: 'stream_timestamp_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 400, height: 100), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - Builder( - builder: (context) { - final theme = StreamChatTheme.of(context); - return StreamTimestamp( - date: DateTime.parse('2021-07-20T16:00:00.000Z'), - style: theme.textTheme.footnote.copyWith( - color: theme.colorTheme.textHighEmphasis, - ), - ); - }, - ), - ), - ); - } -} - -Widget _wrapWithMaterialApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: Builder(builder: (context) { - final theme = StreamChatTheme.of(context); - return Scaffold( - backgroundColor: theme.colorTheme.appBg, - body: Center(child: widget), - ); - }), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/mocks.dart b/packages/stream_chat_flutter/test/src/mocks.dart deleted file mode 100644 index 0f30c7d002..0000000000 --- a/packages/stream_chat_flutter/test/src/mocks.dart +++ /dev/null @@ -1,81 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/src/video/vlc/vlc_manager_desktop.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -class MockClient extends Mock implements StreamChatClient { - MockClient() { - when(() => wsConnectionStatus).thenReturn(ConnectionStatus.connected); - when(() => wsConnectionStatusStream) - .thenAnswer((_) => Stream.value(ConnectionStatus.connected)); - } -} - -class MockClientState extends Mock implements ClientState {} - -class MockChannel extends Mock implements Channel { - MockChannel({ - this.ownCapabilities = const ['send-message'], - }); - - @override - final List ownCapabilities; - - @override - Future get initialized async => true; - - @override - Future watch({ - bool presence = false, - PaginationParams? messagesPagination, - PaginationParams? membersPagination, - PaginationParams? watchersPagination, - }) { - return Future.value(ChannelState()); - } - - @override - // ignore: prefer_expression_function_bodies - Future keyStroke([String? parentId]) async { - return; - } -} - -class MockChannelState extends Mock implements ChannelClientState { - MockChannelState() { - when(() => typingEvents).thenReturn({}); - when(() => typingEventsStream).thenAnswer((_) => Stream.value({})); - when(() => unreadCount).thenReturn(0); - when(() => read).thenReturn([]); - } -} - -class MockNavigatorObserver extends Mock implements NavigatorObserver {} - -class MockVoidCallback extends Mock { - void call(); -} - -class MockValueChanged extends Mock { - void call(T value); -} - -class MockAttachmentHandler extends Mock implements StreamAttachmentHandler {} - -class MockMember extends Mock implements Member {} - -class MockUser extends Mock implements User {} - -class MockOwnUser extends Mock implements OwnUser {} - -class MockAttachment extends Mock implements Attachment {} - -class MockVlcManagerDesktop extends Mock implements VlcManagerDesktop {} - -class MockStreamMemberListController extends Mock - implements StreamMemberListController { - @override - PagedValue value = const PagedValue.loading(); -} - -class MockMessage extends Mock implements Message {} diff --git a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_option_reorderable_list_view_dark.png b/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_option_reorderable_list_view_dark.png deleted file mode 100644 index cd0a0fcf60..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_option_reorderable_list_view_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_option_reorderable_list_view_error_dark.png b/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_option_reorderable_list_view_error_dark.png deleted file mode 100644 index 5e9b3495ab..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_option_reorderable_list_view_error_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_option_reorderable_list_view_error_light.png b/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_option_reorderable_list_view_error_light.png deleted file mode 100644 index f402fea911..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_option_reorderable_list_view_error_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_option_reorderable_list_view_light.png b/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_option_reorderable_list_view_light.png deleted file mode 100644 index ca3fd01230..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_option_reorderable_list_view_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_question_text_field_dark.png b/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_question_text_field_dark.png deleted file mode 100644 index a6dfc2bf42..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_question_text_field_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_question_text_field_error_dark.png b/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_question_text_field_error_dark.png deleted file mode 100644 index 49eba87c27..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_question_text_field_error_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_question_text_field_error_light.png b/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_question_text_field_error_light.png deleted file mode 100644 index 4b873c709d..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_question_text_field_error_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_question_text_field_light.png b/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_question_text_field_light.png deleted file mode 100644 index 86a99f7cc9..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/poll_question_text_field_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/stream_poll_creator_dialog_dark.png b/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/stream_poll_creator_dialog_dark.png deleted file mode 100644 index d0da2bb493..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/stream_poll_creator_dialog_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/stream_poll_creator_dialog_light.png b/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/stream_poll_creator_dialog_light.png deleted file mode 100644 index 6be35a5c05..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/stream_poll_creator_dialog_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/stream_poll_creator_full_screen_dialog_dark.png b/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/stream_poll_creator_full_screen_dialog_dark.png deleted file mode 100644 index feb39d3cb3..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/stream_poll_creator_full_screen_dialog_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/stream_poll_creator_full_screen_dialog_light.png b/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/stream_poll_creator_full_screen_dialog_light.png deleted file mode 100644 index e41113b3d2..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/creator/goldens/ci/stream_poll_creator_full_screen_dialog_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/creator/poll_option_reorderable_list_view_test.dart b/packages/stream_chat_flutter/test/src/poll/creator/poll_option_reorderable_list_view_test.dart deleted file mode 100644 index e35b897aa7..0000000000 --- a/packages/stream_chat_flutter/test/src/poll/creator/poll_option_reorderable_list_view_test.dart +++ /dev/null @@ -1,73 +0,0 @@ -// ignore_for_file: lines_longer_than_80_chars - -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/poll/creator/poll_option_reorderable_list_view.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -void main() { - for (final brightness in Brightness.values) { - goldenTest( - '[${brightness.name}] -> PollOptionReorderableListView should look fine', - fileName: 'poll_option_reorderable_list_view_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 600, height: 500), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - PollOptionReorderableListView( - title: 'Options', - itemHintText: 'Add an option', - initialOptions: [ - PollOptionItem(text: 'Option 1'), - PollOptionItem(text: 'Option 2'), - PollOptionItem(text: 'Option 3'), - PollOptionItem(text: 'Option 4'), - ], - ), - ), - ); - - goldenTest( - '[${brightness.name}] -> PollOptionReorderableListView with error should look fine', - fileName: 'poll_option_reorderable_list_view_error_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 600, height: 500), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - PollOptionReorderableListView( - title: 'Options', - itemHintText: 'Add an option', - initialOptions: [ - PollOptionItem(text: 'Option 1', error: 'Option already exists'), - PollOptionItem(text: 'Option 1', error: 'Option already exists'), - PollOptionItem(text: 'Option 3'), - PollOptionItem(text: 'Option 4'), - ], - ), - ), - ); - } -} - -Widget _wrapWithMaterialApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: Builder( - builder: (context) { - final theme = StreamChatTheme.of(context); - return Scaffold( - backgroundColor: theme.colorTheme.appBg, - body: Center( - child: Padding( - padding: const EdgeInsets.all(8), - child: widget, - ), - ), - ); - }, - ), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/poll/creator/poll_question_text_field_test.dart b/packages/stream_chat_flutter/test/src/poll/creator/poll_question_text_field_test.dart deleted file mode 100644 index 8a07c4e7a9..0000000000 --- a/packages/stream_chat_flutter/test/src/poll/creator/poll_question_text_field_test.dart +++ /dev/null @@ -1,64 +0,0 @@ -// ignore_for_file: lines_longer_than_80_chars - -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/poll/creator/poll_question_text_field.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -void main() { - for (final brightness in Brightness.values) { - goldenTest( - '[${brightness.name}] -> PollQuestionTextField should look fine', - fileName: 'poll_question_text_field_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 600, height: 150), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - PollQuestionTextField( - title: 'Question', - hintText: 'Ask a question', - initialQuestion: PollQuestion(), - ), - ), - ); - - goldenTest( - '[${brightness.name}] -> PollQuestionTextField with error should look fine', - fileName: 'poll_question_text_field_error_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 600, height: 150), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - PollQuestionTextField( - title: 'Question', - hintText: 'Ask a question', - initialQuestion: PollQuestion( - text: 'A very long question that should not be allowed', - error: 'Question should be at most 10 characters long', - ), - ), - ), - ); - } -} - -Widget _wrapWithMaterialApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: Builder(builder: (context) { - final theme = StreamChatTheme.of(context); - return Scaffold( - backgroundColor: theme.colorTheme.appBg, - body: Center( - child: Padding( - padding: const EdgeInsets.all(8), - child: widget, - ), - ), - ); - }), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/poll/creator/stream_poll_creator_dialog_test.dart b/packages/stream_chat_flutter/test/src/poll/creator/stream_poll_creator_dialog_test.dart deleted file mode 100644 index 71bc9dc0e4..0000000000 --- a/packages/stream_chat_flutter/test/src/poll/creator/stream_poll_creator_dialog_test.dart +++ /dev/null @@ -1,42 +0,0 @@ -// ignore_for_file: lines_longer_than_80_chars - -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/poll/creator/stream_poll_creator_dialog.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -void main() { - for (final brightness in Brightness.values) { - goldenTest( - '[${brightness.name}] -> StreamPollCreatorDialog should look fine', - fileName: 'stream_poll_creator_dialog_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 1280, height: 800), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - const StreamPollCreatorDialog(), - ), - ); - - goldenTest( - '[${brightness.name}] -> StreamPollCreatorFullScreenDialog should look fine', - fileName: 'stream_poll_creator_full_screen_dialog_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 412, height: 916), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - const StreamPollCreatorFullScreenDialog(), - ), - ); - } -} - -Widget _wrapWithMaterialApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: widget, - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/poll/creator/stream_poll_creator_widget_test.dart b/packages/stream_chat_flutter/test/src/poll/creator/stream_poll_creator_widget_test.dart deleted file mode 100644 index ba45ab36cb..0000000000 --- a/packages/stream_chat_flutter/test/src/poll/creator/stream_poll_creator_widget_test.dart +++ /dev/null @@ -1,117 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/src/poll/creator/poll_option_reorderable_list_view.dart'; -import 'package:stream_chat_flutter/src/poll/creator/poll_question_text_field.dart'; -import 'package:stream_chat_flutter/src/poll/creator/poll_switch_list_tile.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -void main() { - testWidgets('StreamPollCreatorWidget renders correctly', (tester) async { - final controller = StreamPollController( - config: const PollConfig( - nameRange: (min: 1, max: 150), - allowedVotesRange: (min: 1, max: 10), - ), - ); - - await tester.pumpWidget( - _wrapWithMaterialApp( - StreamPollCreatorWidget( - controller: controller, - ), - ), - ); - - // Verify that the widget is rendered correctly - expect(find.byType(PollQuestionTextField), findsOneWidget); - expect(find.byType(PollOptionReorderableListView), findsOneWidget); - expect(find.byType(PollSwitchListTile), findsNWidgets(4)); - }); - - testWidgets('StreamPollCreatorWidget updates poll state correctly', - (tester) async { - final controller = StreamPollController( - config: const PollConfig( - nameRange: (min: 1, max: 150), - allowedVotesRange: (min: 1, max: 10), - ), - ); - - await tester.pumpWidget( - _wrapWithMaterialApp( - StreamPollCreatorWidget( - controller: controller, - ), - ), - ); - - // Interact with the widget to update the poll state - await tester.enterText( - find.byType(PollQuestionTextField), - 'What is your favorite color?', - ); - await tester.pumpAndSettle(); - expect(controller.value.name, 'What is your favorite color?'); - - await tester.tap(find.switchListTileText('Multiple answers')); - await tester.pumpAndSettle(); - expect(controller.value.enforceUniqueVote, false); - - await tester.tap( - find.descendant( - of: find.byType(PollSwitchTextField), - matching: find.byType(Switch), - ), - ); - await tester.pumpAndSettle(); - expect(controller.value.maxVotesAllowed, null); - - await tester.enterText( - find.descendant( - of: find.byType(PollSwitchTextField), - matching: find.byType(TextField), - ), - '3', - ); - await tester.pumpAndSettle(); - expect(controller.value.maxVotesAllowed, 3); - - await tester.tap(find.switchListTileText('Anonymous poll')); - await tester.pumpAndSettle(); - expect(controller.value.votingVisibility, VotingVisibility.anonymous); - - await tester.tap(find.switchListTileText('Suggest an option')); - await tester.pumpAndSettle(); - expect(controller.value.allowUserSuggestedOptions, true); - - await tester.dragUntilVisible( - find.switchListTileText('Add a comment'), - find.byType(SingleChildScrollView), - const Offset(0, 500), - ); - - await tester.tap(find.switchListTileText('Add a comment')); - await tester.pumpAndSettle(); - expect(controller.value.allowAnswers, true); - }); -} - -extension on CommonFinders { - Finder switchListTileText(String title) { - return ancestor( - of: find.text(title), - matching: find.byType(SwitchListTile), - ); - } -} - -Widget _wrapWithMaterialApp(Widget widget) { - return MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData(), - child: Scaffold( - body: widget, - ), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/poll/goldens/ci/poll_option_reorderable_list_view_dark.png b/packages/stream_chat_flutter/test/src/poll/goldens/ci/poll_option_reorderable_list_view_dark.png deleted file mode 100644 index cd0a0fcf60..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/goldens/ci/poll_option_reorderable_list_view_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/goldens/ci/poll_option_reorderable_list_view_error.png b/packages/stream_chat_flutter/test/src/poll/goldens/ci/poll_option_reorderable_list_view_error.png deleted file mode 100644 index f402fea911..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/goldens/ci/poll_option_reorderable_list_view_error.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/goldens/ci/poll_option_reorderable_list_view_light.png b/packages/stream_chat_flutter/test/src/poll/goldens/ci/poll_option_reorderable_list_view_light.png deleted file mode 100644 index ca3fd01230..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/goldens/ci/poll_option_reorderable_list_view_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/goldens/ci/poll_question_text_field_dark.png b/packages/stream_chat_flutter/test/src/poll/goldens/ci/poll_question_text_field_dark.png deleted file mode 100644 index a6dfc2bf42..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/goldens/ci/poll_question_text_field_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/goldens/ci/poll_question_text_field_error.png b/packages/stream_chat_flutter/test/src/poll/goldens/ci/poll_question_text_field_error.png deleted file mode 100644 index 4b873c709d..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/goldens/ci/poll_question_text_field_error.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/goldens/ci/poll_question_text_field_light.png b/packages/stream_chat_flutter/test/src/poll/goldens/ci/poll_question_text_field_light.png deleted file mode 100644 index 86a99f7cc9..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/goldens/ci/poll_question_text_field_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_creator_dialog_dark.png b/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_creator_dialog_dark.png deleted file mode 100644 index d0da2bb493..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_creator_dialog_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_creator_dialog_light.png b/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_creator_dialog_light.png deleted file mode 100644 index 6be35a5c05..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_creator_dialog_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_creator_full_screen_dialog_dark.png b/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_creator_full_screen_dialog_dark.png deleted file mode 100644 index feb39d3cb3..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_creator_full_screen_dialog_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_creator_full_screen_dialog_light.png b/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_creator_full_screen_dialog_light.png deleted file mode 100644 index e41113b3d2..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_creator_full_screen_dialog_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_options_dialog_dark.png b/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_options_dialog_dark.png deleted file mode 100644 index 005eae2c96..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_options_dialog_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_options_dialog_light.png b/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_options_dialog_light.png deleted file mode 100644 index 3600948ad2..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_options_dialog_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_dark.png b/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_dark.png deleted file mode 100644 index c429ea0059..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_light.png b/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_light.png deleted file mode 100644 index 20b7a24de2..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_with_show_all_dark.png b/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_with_show_all_dark.png deleted file mode 100644 index 95e879b5da..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_with_show_all_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_with_show_all_light.png b/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_with_show_all_light.png deleted file mode 100644 index b5634a5fa5..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/goldens/ci/stream_poll_results_dialog_with_show_all_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_add_comment_dialog_dark.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_add_comment_dialog_dark.png deleted file mode 100644 index e0c0818647..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_add_comment_dialog_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_add_comment_dialog_light.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_add_comment_dialog_light.png deleted file mode 100644 index aa03c1e2c8..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_add_comment_dialog_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_add_comment_dialog_with_initial_value_dark.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_add_comment_dialog_with_initial_value_dark.png deleted file mode 100644 index 1271cafe5c..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_add_comment_dialog_with_initial_value_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_add_comment_dialog_with_initial_value_light.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_add_comment_dialog_with_initial_value_light.png deleted file mode 100644 index b0a3fb329d..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_add_comment_dialog_with_initial_value_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_dark.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_dark.png deleted file mode 100644 index 31040b0822..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_light.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_light.png deleted file mode 100644 index c7c233831c..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_long_question_dark.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_long_question_dark.png deleted file mode 100644 index 64b49fed36..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_long_question_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_long_question_light.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_long_question_light.png deleted file mode 100644 index 67159eb2af..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_long_question_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_all_dark.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_all_dark.png deleted file mode 100644 index c7b28fe98e..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_all_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_all_light.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_all_light.png deleted file mode 100644 index 1e25581c3d..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_all_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_disabled_dark.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_disabled_dark.png deleted file mode 100644 index 45af8b790d..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_disabled_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_disabled_light.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_disabled_light.png deleted file mode 100644 index 5d1b41f480..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_disabled_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_limited_dark.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_limited_dark.png deleted file mode 100644 index 1bfef3bf83..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_limited_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_limited_light.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_limited_light.png deleted file mode 100644 index f7b36287dd..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_limited_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_unique_dark.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_unique_dark.png deleted file mode 100644 index 31040b0822..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_unique_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_unique_light.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_unique_light.png deleted file mode 100644 index c7c233831c..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_header_subtitle_voting_mode_unique_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_suggest_option_dialog_dark.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_suggest_option_dialog_dark.png deleted file mode 100644 index b20fd3c286..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_suggest_option_dialog_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_suggest_option_dialog_light.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_suggest_option_dialog_light.png deleted file mode 100644 index 70b79ba6fb..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_suggest_option_dialog_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_suggest_option_dialog_with_initial_option_dark.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_suggest_option_dialog_with_initial_option_dark.png deleted file mode 100644 index bf66fa5245..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_suggest_option_dialog_with_initial_option_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_suggest_option_dialog_with_initial_option_light.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_suggest_option_dialog_with_initial_option_light.png deleted file mode 100644 index 354ccd6d92..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/poll_suggest_option_dialog_with_initial_option_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_closed_dark.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_closed_dark.png deleted file mode 100644 index 974868b683..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_closed_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_closed_light.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_closed_light.png deleted file mode 100644 index 8b25be5aaa..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_closed_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_dark.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_dark.png deleted file mode 100644 index 2ef4f6913c..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_light.png b/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_light.png deleted file mode 100644 index cc724f3300..0000000000 Binary files a/packages/stream_chat_flutter/test/src/poll/interactor/goldens/ci/stream_poll_interactor_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/poll_add_comment_dialog_test.dart b/packages/stream_chat_flutter/test/src/poll/interactor/poll_add_comment_dialog_test.dart deleted file mode 100644 index 89d7f6cc23..0000000000 --- a/packages/stream_chat_flutter/test/src/poll/interactor/poll_add_comment_dialog_test.dart +++ /dev/null @@ -1,42 +0,0 @@ -// ignore_for_file: lines_longer_than_80_chars - -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/poll/interactor/poll_add_comment_dialog.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -void main() { - for (final brightness in Brightness.values) { - goldenTest( - '[${brightness.name}] -> PollAddCommentDialog looks fine', - fileName: 'poll_add_comment_dialog_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 600, height: 300), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - const PollAddCommentDialog(), - ), - ); - - goldenTest( - '[${brightness.name}] -> PollAddCommentDialog with initialValue looks fine', - fileName: 'poll_add_comment_dialog_with_initial_value_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 600, height: 300), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - const PollAddCommentDialog(initialValue: 'This is a comment'), - ), - ); - } -} - -Widget _wrapWithMaterialApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: widget, - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/poll_footer_test.dart b/packages/stream_chat_flutter/test/src/poll/interactor/poll_footer_test.dart deleted file mode 100644 index 1a61932345..0000000000 --- a/packages/stream_chat_flutter/test/src/poll/interactor/poll_footer_test.dart +++ /dev/null @@ -1,342 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/src/poll/interactor/poll_footer.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -void main() async { - final currentUser = User(id: 'user-1', name: 'User'); - - final poll = Poll( - id: 'poll-1', - name: 'Favorite color?', - options: const [ - PollOption(id: 'option-1', text: 'Red'), - PollOption(id: 'option-2', text: 'Blue'), - PollOption(id: 'option-3', text: 'Green'), - ], - ); - - testWidgets( - 'End Vote button is visible and enabled for the creator on open poll', - (WidgetTester tester) async { - await tester.pumpWidget(_wrapWithMaterialApp( - PollFooter( - poll: poll.copyWith(createdBy: currentUser), - currentUser: currentUser, - onEndVote: () {}, - ), - )); - - final endVoteButton = find.ancestor( - of: find.text('End Vote'), - matching: find.byType(PollFooterButton), - ); - - expect(endVoteButton, findsOneWidget); - - expect( - tester.widget(endVoteButton).onPressed, - isNotNull, - ); - }, - ); - - testWidgets( - 'End Vote button is not visible for non-creator', - (WidgetTester tester) async { - await tester.pumpWidget(_wrapWithMaterialApp( - PollFooter( - poll: poll, - currentUser: currentUser, - onEndVote: () {}, - ), - )); - - final endVoteButton = find.ancestor( - of: find.text('End Vote'), - matching: find.byType(PollFooterButton), - ); - - expect(endVoteButton, findsNothing); - }, - ); - - testWidgets( - 'End Vote button is not visible for closed poll', - (WidgetTester tester) async { - await tester.pumpWidget(_wrapWithMaterialApp( - PollFooter( - poll: poll.copyWith( - isClosed: true, - createdBy: currentUser, - ), - currentUser: currentUser, - onEndVote: () {}, - ), - )); - - final endVoteButton = find.ancestor( - of: find.text('End Vote'), - matching: find.byType(PollFooterButton), - ); - - expect(endVoteButton, findsNothing); - }, - ); - - testWidgets( - 'Add Comment button is visible and enabled when poll allows answers', - (WidgetTester tester) async { - await tester.pumpWidget(_wrapWithMaterialApp( - PollFooter( - poll: poll.copyWith(allowAnswers: true), - currentUser: currentUser, - onAddComment: () {}, - ), - )); - - final addCommentButton = find.ancestor( - of: find.text('Add a comment'), - matching: find.byType(PollFooterButton), - ); - - expect(addCommentButton, findsOneWidget); - expect( - tester.widget(addCommentButton).onPressed, - isNotNull, - ); - }, - ); - - testWidgets( - 'Add Comment button is not visible when poll is closed', - (WidgetTester tester) async { - await tester.pumpWidget(_wrapWithMaterialApp( - PollFooter( - poll: poll.copyWith( - isClosed: true, - allowAnswers: true, - ), - currentUser: currentUser, - onAddComment: () {}, - ), - )); - - final addCommentButton = find.ancestor( - of: find.text('Add a comment'), - matching: find.byType(PollFooterButton), - ); - - expect(addCommentButton, findsNothing); - }, - ); - - testWidgets( - 'View Comments button is visible and enabled if there are answers', - (WidgetTester tester) async { - await tester.pumpWidget(_wrapWithMaterialApp( - PollFooter( - poll: poll.copyWith(answersCount: 1), - currentUser: currentUser, - onViewComments: () {}, - ), - )); - - final viewCommentsButton = find.ancestor( - of: find.text('View Comments'), - matching: find.byType(PollFooterButton), - ); - - expect(viewCommentsButton, findsOneWidget); - expect( - tester.widget(viewCommentsButton).onPressed, - isNotNull, - ); - }, - ); - - testWidgets( - 'View Comments button is not visible when there are no answers', - (WidgetTester tester) async { - await tester.pumpWidget(_wrapWithMaterialApp( - PollFooter( - poll: poll.copyWith( - answersCount: 0, - ), - currentUser: currentUser, - onViewComments: () {}, - ), - )); - - final viewCommentsButton = find.ancestor( - of: find.text('View Comments'), - matching: find.byType(PollFooterButton), - ); - - expect(viewCommentsButton, findsNothing); - }, - ); - - testWidgets( - 'Suggest Option button is visible and enabled when allowed', - (WidgetTester tester) async { - await tester.pumpWidget(_wrapWithMaterialApp( - PollFooter( - poll: poll.copyWith( - allowUserSuggestedOptions: true, - ), - currentUser: currentUser, - onSuggestOption: () {}, - ), - )); - - final suggestOptionButton = find.ancestor( - of: find.text('Suggest an option'), - matching: find.byType(PollFooterButton), - ); - - expect(suggestOptionButton, findsOneWidget); - expect( - tester.widget(suggestOptionButton).onPressed, - isNotNull, - ); - }, - ); - - testWidgets( - 'Suggest Option button is not visible when poll is closed', - (WidgetTester tester) async { - await tester.pumpWidget(_wrapWithMaterialApp( - PollFooter( - poll: poll.copyWith( - isClosed: true, - allowUserSuggestedOptions: true, - ), - currentUser: currentUser, - onSuggestOption: () {}, - ), - )); - - final suggestOptionButton = find.ancestor( - of: find.text('Suggest an option'), - matching: find.byType(PollFooterButton), - ); - - expect(suggestOptionButton, findsNothing); - }, - ); - - testWidgets( - 'View Results button is enabled if there are votes', - (WidgetTester tester) async { - await tester.pumpWidget( - _wrapWithMaterialApp( - PollFooter( - poll: poll.copyWith(voteCount: 1), - currentUser: currentUser, - onViewResults: () {}, - ), - ), - ); - - final viewResultsButton = find.ancestor( - of: find.text('View Results'), - matching: find.byType(PollFooterButton), - ); - - expect(viewResultsButton, findsOneWidget); - expect( - tester.widget(viewResultsButton).onPressed, - isNotNull, - ); - }, - ); - - testWidgets( - 'View Results button is disabled if there are no votes', - (WidgetTester tester) async { - await tester.pumpWidget( - _wrapWithMaterialApp( - PollFooter( - poll: poll.copyWith(voteCount: 0), - currentUser: currentUser, - onViewResults: () {}, - ), - ), - ); - - final viewResultsButton = find.ancestor( - of: find.text('View Results'), - matching: find.byType(PollFooterButton), - ); - - expect(viewResultsButton, findsOneWidget); - expect( - tester.widget(viewResultsButton).onPressed, - isNull, - ); - }, - ); - - testWidgets( - 'See More Options button is visible if there are more options', - (WidgetTester tester) async { - await tester.pumpWidget( - _wrapWithMaterialApp( - PollFooter( - poll: poll, - visibleOptionCount: 2, - currentUser: currentUser, - onSeeMoreOptions: () {}, - ), - ), - ); - - final seeMoreOptionsButton = find.ancestor( - of: find.text('See all ${poll.options.length} options'), - matching: find.byType(PollFooterButton), - ); - - expect(seeMoreOptionsButton, findsOneWidget); - expect( - tester.widget(seeMoreOptionsButton).onPressed, - isNotNull, - ); - }, - ); - - testWidgets( - 'See More Options button is not visible when all options are visible', - (WidgetTester tester) async { - await tester.pumpWidget( - _wrapWithMaterialApp( - PollFooter( - poll: poll, - currentUser: currentUser, - onSeeMoreOptions: () {}, - ), - ), - ); - - final seeMoreOptionsButton = find.ancestor( - of: find.text('See all ${poll.options.length} options'), - matching: find.byType(PollFooterButton), - ); - - expect(seeMoreOptionsButton, findsNothing); - }, - ); -} - -Widget _wrapWithMaterialApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: widget, - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/poll_header_test.dart b/packages/stream_chat_flutter/test/src/poll/interactor/poll_header_test.dart deleted file mode 100644 index 301dd5d5f5..0000000000 --- a/packages/stream_chat_flutter/test/src/poll/interactor/poll_header_test.dart +++ /dev/null @@ -1,115 +0,0 @@ -// ignore_for_file: lines_longer_than_80_chars - -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/poll/interactor/poll_header.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -void main() { - final poll = Poll( - id: 'poll-1', - name: 'What is your favorite color?', - options: const [ - PollOption(id: 'option-1', text: 'Red'), - PollOption(id: 'option-2', text: 'Blue'), - PollOption(id: 'option-3', text: 'Green'), - ], - ); - - for (final brightness in Brightness.values) { - goldenTest( - '[${brightness.name}] -> PollHeader looks fine', - fileName: 'poll_header_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 300, height: 100), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - PollHeader(poll: poll), - ), - ); - - goldenTest( - '[${brightness.name}] -> PollHeader with long question looks fine', - fileName: 'poll_header_long_question_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 300, height: 100), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - PollHeader( - poll: poll.copyWith( - name: 'A very long question that does not fit in one line', - ), - ), - ), - ); - - goldenTest( - '[${brightness.name}] -> PollHeader subtitle with voting mode disabled looks fine', - fileName: 'poll_header_subtitle_voting_mode_disabled_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 300, height: 100), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - PollHeader( - poll: poll.copyWith(isClosed: true), - ), - ), - ); - - goldenTest( - '[${brightness.name}] -> PollHeader subtitle with voting mode unique looks fine', - fileName: 'poll_header_subtitle_voting_mode_unique_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 300, height: 100), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - PollHeader( - poll: poll.copyWith(enforceUniqueVote: true), - ), - ), - ); - - goldenTest( - '[${brightness.name}] -> PollHeader subtitle with voting mode limited looks fine', - fileName: 'poll_header_subtitle_voting_mode_limited_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 300, height: 100), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - PollHeader( - poll: poll.copyWith(maxVotesAllowed: 2, enforceUniqueVote: false), - ), - ), - ); - - goldenTest( - '[${brightness.name}] -> PollHeader subtitle with voting mode all looks fine', - fileName: 'poll_header_subtitle_voting_mode_all_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 300, height: 100), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - PollHeader( - poll: poll.copyWith(maxVotesAllowed: 3, enforceUniqueVote: false), - ), - ), - ); - } -} - -Widget _wrapWithMaterialApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: Builder(builder: (context) { - final theme = StreamChatTheme.of(context); - return Scaffold( - backgroundColor: theme.colorTheme.appBg, - body: Container( - color: theme.colorTheme.disabled, - padding: const EdgeInsets.all(16), - child: Center(child: widget), - ), - ); - }), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/poll_suggest_option_dialog_test.dart b/packages/stream_chat_flutter/test/src/poll/interactor/poll_suggest_option_dialog_test.dart deleted file mode 100644 index fb66e438df..0000000000 --- a/packages/stream_chat_flutter/test/src/poll/interactor/poll_suggest_option_dialog_test.dart +++ /dev/null @@ -1,55 +0,0 @@ -// ignore_for_file: lines_longer_than_80_chars - -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/poll/interactor/poll_suggest_option_dialog.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -void main() { - for (final brightness in Brightness.values) { - goldenTest( - '[${brightness.name}] -> PollSuggestOptionDialog looks fine', - fileName: 'poll_suggest_option_dialog_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 600, height: 300), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - const PollSuggestOptionDialog(), - ), - ); - - goldenTest( - '[${brightness.name}] -> PollSuggestOptionDialog with initialOption looks fine', - fileName: - 'poll_suggest_option_dialog_with_initial_option_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 600, height: 300), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - const PollSuggestOptionDialog( - initialOption: 'New option', - ), - ), - ); - } -} - -Widget _wrapWithMaterialApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: Builder(builder: (context) { - final theme = StreamChatTheme.of(context); - return Scaffold( - backgroundColor: theme.colorTheme.appBg, - body: Container( - color: theme.colorTheme.disabled, - padding: const EdgeInsets.all(16), - child: Center(child: widget), - ), - ); - }), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/poll/interactor/stream_poll_interactor_test.dart b/packages/stream_chat_flutter/test/src/poll/interactor/stream_poll_interactor_test.dart deleted file mode 100644 index acc34896a3..0000000000 --- a/packages/stream_chat_flutter/test/src/poll/interactor/stream_poll_interactor_test.dart +++ /dev/null @@ -1,127 +0,0 @@ -// ignore_for_file: lines_longer_than_80_chars - -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/poll/interactor/stream_poll_interactor.dart'; -import 'package:stream_chat_flutter/src/stream_chat_configuration.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -void main() { - final currentUser = User(id: 'curr-user', name: 'Current User'); - - final latestVotesByOption = { - 'option-1': [ - for (var i = 0; i < 3; i++) - PollVote( - userId: 'user-$i', - user: User(id: 'user-$i', name: 'User $i'), - optionId: 'option-1', - createdAt: DateTime.now(), - ), - ], - 'option-2': [ - for (var i = 0; i < 2; i++) - PollVote( - userId: 'user-$i', - user: User(id: 'user-$i', name: 'User $i'), - optionId: 'option-2', - createdAt: DateTime.now(), - ), - ], - 'option-3': [ - PollVote( - user: currentUser, - userId: currentUser.id, - optionId: 'option-3', - createdAt: DateTime.now(), - ), - ], - }; - - final voteCountsByOption = latestVotesByOption.map( - (key, value) => MapEntry(key, value.length), - ); - - final latestAnswers = [ - PollVote( - user: currentUser, - userId: currentUser.id, - answerText: 'I also like yellow', - createdAt: DateTime.now(), - ), - ]; - - final poll = Poll( - id: 'poll-1', - name: 'What is your favorite color?', - createdBy: currentUser, - allowUserSuggestedOptions: true, - options: const [ - PollOption(id: 'option-1', text: 'Red'), - PollOption(id: 'option-2', text: 'Blue'), - PollOption(id: 'option-3', text: 'Green'), - ], - voteCount: voteCountsByOption.values.reduce((a, b) => a + b), - voteCountsByOption: voteCountsByOption, - latestVotesByOption: latestVotesByOption, - allowAnswers: true, - answersCount: latestAnswers.length, - latestAnswers: latestAnswers, - ownVotesAndAnswers: [ - ...latestAnswers, - ...latestVotesByOption.values.expand((it) => it), - ].where((it) => it.userId == currentUser.id).toList(), - ); - - for (final brightness in Brightness.values) { - goldenTest( - '[${brightness.name}] -> StreamPollInteractor should look fine', - fileName: 'stream_poll_interactor_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 412, height: 500), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - StreamPollInteractor( - poll: poll, - currentUser: currentUser, - ), - ), - ); - - goldenTest( - '[${brightness.name}] -> StreamPollInteractor with closed poll should look fine', - fileName: 'stream_poll_interactor_closed_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 412, height: 500), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - StreamPollInteractor( - poll: poll.copyWith( - isClosed: true, - ), - currentUser: currentUser, - ), - ), - ); - } -} - -Widget _wrapWithMaterialApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - home: StreamChatConfiguration( - data: StreamChatConfigurationData(), - child: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: Builder(builder: (context) { - final theme = StreamChatTheme.of(context); - return Scaffold( - backgroundColor: theme.colorTheme.appBg, - body: Center(child: widget), - ); - }), - ), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/poll/poll_option_reorderable_list_view_test.dart b/packages/stream_chat_flutter/test/src/poll/poll_option_reorderable_list_view_test.dart deleted file mode 100644 index 2609417970..0000000000 --- a/packages/stream_chat_flutter/test/src/poll/poll_option_reorderable_list_view_test.dart +++ /dev/null @@ -1,85 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/poll/creator/poll_option_reorderable_list_view.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -void main() { - goldenTest( - '[Light] -> PollOptionReorderableListView should look fine', - fileName: 'poll_option_reorderable_list_view_light', - constraints: const BoxConstraints.tightFor(width: 600, height: 500), - builder: () => _wrapWithMaterialApp( - brightness: Brightness.light, - PollOptionReorderableListView( - title: 'Options', - itemHintText: 'Add an option', - initialOptions: [ - PollOptionItem(text: 'Option 1'), - PollOptionItem(text: 'Option 2'), - PollOptionItem(text: 'Option 3'), - PollOptionItem(text: 'Option 4'), - ], - ), - ), - ); - - goldenTest( - '[Dark] -> PollOptionReorderableListView should look fine', - fileName: 'poll_option_reorderable_list_view_dark', - constraints: const BoxConstraints.tightFor(width: 600, height: 500), - builder: () => _wrapWithMaterialApp( - brightness: Brightness.dark, - PollOptionReorderableListView( - title: 'Options', - itemHintText: 'Add an option', - initialOptions: [ - PollOptionItem(text: 'Option 1'), - PollOptionItem(text: 'Option 2'), - PollOptionItem(text: 'Option 3'), - PollOptionItem(text: 'Option 4'), - ], - ), - ), - ); - - goldenTest( - '[Error] -> PollOptionReorderableListView should look fine', - fileName: 'poll_option_reorderable_list_view_error', - constraints: const BoxConstraints.tightFor(width: 600, height: 500), - builder: () => _wrapWithMaterialApp( - PollOptionReorderableListView( - title: 'Options', - itemHintText: 'Add an option', - initialOptions: [ - PollOptionItem(text: 'Option 1', error: 'Option already exists'), - PollOptionItem(text: 'Option 1', error: 'Option already exists'), - PollOptionItem(text: 'Option 3'), - PollOptionItem(text: 'Option 4'), - ], - ), - ), - ); -} - -Widget _wrapWithMaterialApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: Builder(builder: (context) { - final theme = StreamChatTheme.of(context); - return Scaffold( - backgroundColor: theme.colorTheme.appBg, - body: Center( - child: Padding( - padding: const EdgeInsets.all(8), - child: widget, - ), - ), - ); - }), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/poll/poll_question_text_field_test.dart b/packages/stream_chat_flutter/test/src/poll/poll_question_text_field_test.dart deleted file mode 100644 index 24928bbc67..0000000000 --- a/packages/stream_chat_flutter/test/src/poll/poll_question_text_field_test.dart +++ /dev/null @@ -1,72 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/poll/creator/poll_question_text_field.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -void main() { - goldenTest( - '[Light] -> PollQuestionTextField should look fine', - fileName: 'poll_question_text_field_light', - constraints: const BoxConstraints.tightFor(width: 600, height: 150), - builder: () => _wrapWithMaterialApp( - brightness: Brightness.light, - PollQuestionTextField( - title: 'Question', - hintText: 'Ask a question', - initialQuestion: PollQuestion(), - ), - ), - ); - - goldenTest( - '[Error] -> PollQuestionTextField should look fine', - fileName: 'poll_question_text_field_error', - constraints: const BoxConstraints.tightFor(width: 600, height: 150), - builder: () => _wrapWithMaterialApp( - PollQuestionTextField( - title: 'Question', - initialQuestion: PollQuestion( - text: 'A very long question that should not be allowed', - error: 'Question should be at most 10 characters long', - ), - ), - ), - ); - - goldenTest( - '[Dark] -> PollQuestionTextField should look fine', - fileName: 'poll_question_text_field_dark', - constraints: const BoxConstraints.tightFor(width: 600, height: 150), - builder: () => _wrapWithMaterialApp( - brightness: Brightness.dark, - PollQuestionTextField( - title: 'Question', - hintText: 'Ask a question', - initialQuestion: PollQuestion(), - ), - ), - ); -} - -Widget _wrapWithMaterialApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: Builder(builder: (context) { - final theme = StreamChatTheme.of(context); - return Scaffold( - backgroundColor: theme.colorTheme.appBg, - body: Center( - child: Padding( - padding: const EdgeInsets.all(8), - child: widget, - ), - ), - ); - }), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/poll/stream_poll_creator_dialog_test.dart b/packages/stream_chat_flutter/test/src/poll/stream_poll_creator_dialog_test.dart deleted file mode 100644 index 4e20086fa2..0000000000 --- a/packages/stream_chat_flutter/test/src/poll/stream_poll_creator_dialog_test.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/poll/creator/stream_poll_creator_dialog.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; - -void main() { - goldenTest( - '[Light] -> StreamPollCreatorDialog should look fine', - fileName: 'stream_poll_creator_dialog_light', - constraints: const BoxConstraints.tightFor(width: 1280, height: 800), - builder: () => _wrapWithMaterialApp( - brightness: Brightness.light, - const StreamPollCreatorDialog(), - ), - ); - - goldenTest( - '[Dark] -> StreamPollCreatorDialog should look fine', - fileName: 'stream_poll_creator_dialog_dark', - constraints: const BoxConstraints.tightFor(width: 1280, height: 800), - builder: () => _wrapWithMaterialApp( - brightness: Brightness.dark, - const StreamPollCreatorDialog(), - ), - ); - - goldenTest( - '[Light] -> StreamPollCreatorFullScreenDialog should look fine', - fileName: 'stream_poll_creator_full_screen_dialog_light', - constraints: const BoxConstraints.tightFor(width: 412, height: 916), - builder: () => _wrapWithMaterialApp( - brightness: Brightness.light, - const StreamPollCreatorFullScreenDialog(), - ), - ); - - goldenTest( - '[Dark] -> StreamPollCreatorFullScreenDialog should look fine', - fileName: 'stream_poll_creator_full_screen_dialog_dark', - constraints: const BoxConstraints.tightFor(width: 412, height: 916), - builder: () => _wrapWithMaterialApp( - brightness: Brightness.dark, - const StreamPollCreatorFullScreenDialog(), - ), - ); -} - -Widget _wrapWithMaterialApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: widget, - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/poll/stream_poll_options_dialog_test.dart b/packages/stream_chat_flutter/test/src/poll/stream_poll_options_dialog_test.dart deleted file mode 100644 index 81ebd027ac..0000000000 --- a/packages/stream_chat_flutter/test/src/poll/stream_poll_options_dialog_test.dart +++ /dev/null @@ -1,107 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/poll/stream_poll_options_dialog.dart'; -import 'package:stream_chat_flutter/src/stream_chat_configuration.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -void main() { - final currentUser = User(id: 'curr-user', name: 'Current User'); - - final latestVotesByOption = { - 'option-1': [ - for (var i = 0; i < 5; i++) - PollVote( - userId: 'user-$i', - user: User(id: 'user-$i', name: 'User $i'), - optionId: 'option-1', - createdAt: DateTime.now(), - ), - ], - 'option-2': [ - for (var i = 0; i < 2; i++) - PollVote( - userId: 'user-$i', - user: User(id: 'user-$i', name: 'User $i'), - optionId: 'option-2', - createdAt: DateTime.now(), - ), - ], - 'option-3': [ - PollVote( - user: currentUser, - userId: currentUser.id, - optionId: 'option-3', - createdAt: DateTime.now(), - ), - ], - }; - - final voteCountsByOption = latestVotesByOption.map( - (key, value) => MapEntry(key, value.length), - ); - - final latestAnswers = [ - PollVote( - user: currentUser, - userId: currentUser.id, - answerText: 'I also like yellow', - createdAt: DateTime.now(), - ), - ]; - - final poll = Poll( - id: 'poll-1', - name: 'What is your favorite color?', - createdBy: currentUser, - allowUserSuggestedOptions: true, - options: const [ - PollOption(id: 'option-1', text: 'Red'), - PollOption(id: 'option-2', text: 'Blue'), - PollOption(id: 'option-3', text: 'Green'), - ], - voteCount: voteCountsByOption.values.reduce((a, b) => a + b), - voteCountsByOption: voteCountsByOption, - latestVotesByOption: latestVotesByOption, - allowAnswers: true, - answersCount: latestAnswers.length, - latestAnswers: latestAnswers, - ownVotesAndAnswers: [ - ...latestAnswers, - ...latestVotesByOption.values.expand((it) => it), - ].where((it) => it.userId == currentUser.id).toList(), - ); - - for (final brightness in Brightness.values) { - goldenTest( - '[${brightness.name}] -> StreamPollOptionsDialog looks fine', - fileName: 'stream_poll_options_dialog_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 412, height: 916), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - StreamPollOptionsDialog(poll: poll), - ), - ); - } -} - -Widget _wrapWithMaterialApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - home: StreamChatConfiguration( - data: StreamChatConfigurationData(), - child: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: Builder(builder: (context) { - final theme = StreamChatTheme.of(context); - return Scaffold( - backgroundColor: theme.colorTheme.appBg, - body: Center(child: widget), - ); - }), - ), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/poll/stream_poll_results_dialog_test.dart b/packages/stream_chat_flutter/test/src/poll/stream_poll_results_dialog_test.dart deleted file mode 100644 index e5b8df69d0..0000000000 --- a/packages/stream_chat_flutter/test/src/poll/stream_poll_results_dialog_test.dart +++ /dev/null @@ -1,127 +0,0 @@ -// ignore_for_file: lines_longer_than_80_chars - -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/poll/stream_poll_results_dialog.dart'; -import 'package:stream_chat_flutter/src/stream_chat_configuration.dart'; -import 'package:stream_chat_flutter/src/theme/stream_chat_theme.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -void main() { - final currentUser = User(id: 'curr-user', name: 'Current User'); - final createdAt = DateTime.parse('2021-07-20T16:00:00.000Z'); - final latestVotesByOption = { - 'option-1': [ - for (var i = 0; i < 5; i++) - PollVote( - userId: 'user-$i', - user: User(id: 'user-$i', name: 'User $i'), - optionId: 'option-1', - createdAt: createdAt, - updatedAt: createdAt, - ), - ], - 'option-2': [ - for (var i = 0; i < 2; i++) - PollVote( - userId: 'user-$i', - user: User(id: 'user-$i', name: 'User $i'), - optionId: 'option-2', - createdAt: createdAt, - updatedAt: createdAt, - ), - ], - 'option-3': [ - PollVote( - user: currentUser, - userId: currentUser.id, - optionId: 'option-3', - createdAt: createdAt, - updatedAt: createdAt, - ), - ], - }; - - final voteCountsByOption = latestVotesByOption.map( - (key, value) => MapEntry(key, value.length), - ); - - final latestAnswers = [ - PollVote( - user: currentUser, - userId: currentUser.id, - answerText: 'I also like yellow', - createdAt: createdAt, - updatedAt: createdAt, - ), - ]; - - final poll = Poll( - id: 'poll-1', - name: 'What is your favorite color?', - createdBy: currentUser, - allowUserSuggestedOptions: true, - options: const [ - PollOption(id: 'option-1', text: 'Red'), - PollOption(id: 'option-2', text: 'Blue'), - PollOption(id: 'option-3', text: 'Green'), - ], - voteCount: voteCountsByOption.values.reduce((a, b) => a + b), - voteCountsByOption: voteCountsByOption, - latestVotesByOption: latestVotesByOption, - allowAnswers: true, - answersCount: latestAnswers.length, - latestAnswers: latestAnswers, - ownVotesAndAnswers: [ - ...latestAnswers, - ...latestVotesByOption.values.expand((it) => it), - ].where((it) => it.userId == currentUser.id).toList(), - ); - - for (final brightness in Brightness.values) { - goldenTest( - '[${brightness.name}] -> StreamPollResultsDialog looks fine', - fileName: 'stream_poll_results_dialog_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 412, height: 916), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - StreamPollResultsDialog(poll: poll), - ), - ); - - goldenTest( - '[${brightness.name}] -> StreamPollResultsDialog with Show all looks fine', - fileName: 'stream_poll_results_dialog_with_show_all_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 412, height: 916), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - StreamPollResultsDialog( - poll: poll, - visibleVotesCount: 2, - onShowAllVotesPressed: (_) {}, - ), - ), - ); - } -} - -Widget _wrapWithMaterialApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - home: StreamChatConfiguration( - data: StreamChatConfigurationData(), - child: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: Builder(builder: (context) { - final theme = StreamChatTheme.of(context); - return Scaffold( - backgroundColor: theme.colorTheme.appBg, - body: Center(child: widget), - ); - }), - ), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/scroll_view/member_scroll_view/stream_member_list_view_test.dart b/packages/stream_chat_flutter/test/src/scroll_view/member_scroll_view/stream_member_list_view_test.dart deleted file mode 100644 index b5275a9790..0000000000 --- a/packages/stream_chat_flutter/test/src/scroll_view/member_scroll_view/stream_member_list_view_test.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../../mocks.dart'; - -void main() { - late StreamChatClient client; - late Channel channel; - late ChannelClientState channelClientState; - late ClientState clientState; - - setUp(() { - client = MockClient(); - clientState = MockClientState(); - when(() => client.state).thenAnswer((_) => clientState); - when(() => clientState.currentUser).thenReturn(OwnUser(id: 'testid')); - when(() => clientState.currentUserStream) - .thenAnswer((_) => Stream.value(OwnUser(id: 'testid'))); - channel = MockChannel(); - when(() => channel.on(any(), any(), any(), any())) - .thenAnswer((_) => const Stream.empty()); - channelClientState = MockChannelState(); - when(() => channel.client).thenReturn(client); - when(() => channel.state).thenReturn(channelClientState); - - when(() => channelClientState.membersStream) - .thenAnswer((_) => const Stream.empty()); - when(() => channelClientState.members).thenReturn([]); - }); - - testWidgets('renders empty member list view', (tester) async { - const emptyWidgetKey = Key('empty_widget'); - final controller = MockStreamMemberListController(); - - when(controller.doInitialLoad).thenAnswer((_) async { - controller.value = const PagedValue(items: []); - }); - - await tester.pumpWidget( - MaterialApp( - home: StreamChat( - client: client, - child: StreamChannel( - channel: channel, - child: Scaffold( - body: StreamMemberListView( - emptyBuilder: (_) => Container(key: emptyWidgetKey), - controller: controller, - ), - ), - ), - ), - ), - ); - await tester.pumpAndSettle(); - - expect(find.byType(StreamMemberListView), findsOneWidget); - expect(find.byKey(emptyWidgetKey), findsOneWidget); - }); -} diff --git a/packages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_thread_list_tile_dark.png b/packages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_thread_list_tile_dark.png deleted file mode 100644 index c82908a3d8..0000000000 Binary files a/packages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_thread_list_tile_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_thread_list_tile_light.png b/packages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_thread_list_tile_light.png deleted file mode 100644 index 153e5cf18f..0000000000 Binary files a/packages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_thread_list_tile_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_unread_threads_banner_dark.png b/packages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_unread_threads_banner_dark.png deleted file mode 100644 index a8b2e50f94..0000000000 Binary files a/packages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_unread_threads_banner_dark.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_unread_threads_banner_light.png b/packages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_unread_threads_banner_light.png deleted file mode 100644 index f917ecd95f..0000000000 Binary files a/packages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/goldens/ci/stream_unread_threads_banner_light.png and /dev/null differ diff --git a/packages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/stream_thread_list_tile_test.dart b/packages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/stream_thread_list_tile_test.dart deleted file mode 100644 index 976df5f307..0000000000 --- a/packages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/stream_thread_list_tile_test.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -void main() { - final user1 = User(id: 'uid1', name: 'User 1'); - final user2 = User(id: 'uid2', name: 'User 2'); - final createdAt = DateTime.parse('2021-07-20T16:00:00.000Z'); - final thread = Thread( - activeParticipantCount: 2, - channelCid: 'channel-type:channel-id', - channel: ChannelModel( - cid: 'channel-type:channel-id', - extraData: const {'name': 'Group ride'}, - ), - parentMessageId: 'parent-message-id', - parentMessage: Message( - id: 'parent-message-id', - text: "Hey everyone, who's up for a group ride this Saturday morning?", - ), - createdByUserId: 'uid1', - createdBy: user2, - participantCount: 2, - threadParticipants: [ - ThreadParticipant( - user: user1, - channelCid: '', - createdAt: createdAt, - lastReadAt: createdAt, - ), - ThreadParticipant( - user: user2, - channelCid: '', - createdAt: createdAt, - lastReadAt: createdAt, - ), - ], - lastMessageAt: createdAt, - createdAt: createdAt, - updatedAt: createdAt, - title: 'Group ride preparation and discussion', - replyCount: 1, - latestReplies: [ - Message( - id: 'mid1', - text: 'See you all there, stay safe on the roads!', - user: user1, - createdAt: createdAt, - updatedAt: createdAt, - ), - ], - read: [ - Read( - user: user2, - lastRead: createdAt, - unreadMessages: 3, - ), - ], - ); - - for (final brightness in Brightness.values) { - goldenTest( - '[${brightness.name}] -> StreamThreadListTile looks fine', - fileName: 'stream_thread_list_tile_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 600, height: 150), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - StreamThreadListTile(thread: thread, currentUser: user2), - ), - ); - } -} - -Widget _wrapWithMaterialApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - home: StreamChatConfiguration( - data: StreamChatConfigurationData(), - child: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: Builder(builder: (context) { - final theme = StreamChatTheme.of(context); - return Scaffold( - backgroundColor: theme.colorTheme.appBg, - body: Center(child: widget), - ); - }), - ), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/stream_unread_threads_banner_test.dart b/packages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/stream_unread_threads_banner_test.dart deleted file mode 100644 index 897d140aed..0000000000 --- a/packages/stream_chat_flutter/test/src/scroll_view/thread_scroll_view/stream_unread_threads_banner_test.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:alchemist/alchemist.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -void main() { - for (final brightness in Brightness.values) { - goldenTest( - '[${brightness.name}] -> StreamUnreadThreadsBanner looks fine', - fileName: 'stream_unread_threads_banner_${brightness.name}', - constraints: const BoxConstraints.tightFor(width: 400, height: 100), - builder: () => _wrapWithMaterialApp( - brightness: brightness, - const StreamUnreadThreadsBanner(unreadThreads: {'id1', 'id2', 'id3'}), - ), - ); - } -} - -Widget _wrapWithMaterialApp( - Widget widget, { - Brightness? brightness, -}) { - return MaterialApp( - home: StreamChatTheme( - data: StreamChatThemeData(brightness: brightness), - child: Builder(builder: (context) { - final theme = StreamChatTheme.of(context); - return Scaffold( - backgroundColor: theme.colorTheme.appBg, - body: Center(child: widget), - ); - }), - ), - ); -} diff --git a/packages/stream_chat_flutter/test/src/simple_frame.dart b/packages/stream_chat_flutter/test/src/simple_frame.dart deleted file mode 100644 index 830ecbf00d..0000000000 --- a/packages/stream_chat_flutter/test/src/simple_frame.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/material.dart'; - -class SimpleFrame extends StatelessWidget { - const SimpleFrame({super.key, required this.child}); - - final Widget child; - - @override - Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.all(4), - decoration: BoxDecoration( - color: const Color(0xFFFFFFFF), - border: Border.all(color: const Color(0xFF9E9E9E)), - ), - child: child, - ); - } -} diff --git a/packages/stream_chat_flutter/test/src/stream_chat_configuration_test.dart b/packages/stream_chat_flutter/test/src/stream_chat_configuration_test.dart deleted file mode 100644 index d9db650ab2..0000000000 --- a/packages/stream_chat_flutter/test/src/stream_chat_configuration_test.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/src/stream_chat_configuration.dart'; - -void main() { - group('StreamChatConfigurationProvider', () { - testWidgets( - 'should provide the StreamChatConfiguration class with default data', - (t) async { - final configuration = StreamChatConfigurationData(); - late final StreamChatConfigurationData configurationFromProvider; - await t.pumpWidget(StreamChatConfiguration( - data: configuration, - child: Builder( - builder: (context) { - configurationFromProvider = StreamChatConfiguration.of(context); - return const SizedBox(); - }, - ), - )); - - expect(configuration, configurationFromProvider); - }, - ); - - testWidgets( - 'should provide the StreamChatConfiguration class with custom data', - (t) async { - final configuration = StreamChatConfigurationData().copyWith( - enforceUniqueReactions: false, - ); - late final StreamChatConfigurationData configurationFromProvider; - await t.pumpWidget(StreamChatConfiguration( - data: configuration, - child: Builder( - builder: (context) { - configurationFromProvider = StreamChatConfiguration.of(context); - return const SizedBox(); - }, - ), - )); - - expect(configuration, configurationFromProvider); - }, - ); - }); -} diff --git a/packages/stream_chat_flutter/test/src/theme/avatar_theme_test.dart b/packages/stream_chat_flutter/test/src/theme/avatar_theme_test.dart deleted file mode 100644 index 44f6f85113..0000000000 --- a/packages/stream_chat_flutter/test/src/theme/avatar_theme_test.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -void main() { - test('AvatarThemeData copyWith, ==, hashCode basics', () { - expect(const StreamAvatarThemeData(), - const StreamAvatarThemeData().copyWith()); - expect(const StreamAvatarThemeData().hashCode, - const StreamAvatarThemeData().copyWith().hashCode); - }); - - group('AvatarThemeData lerps correctly', () { - test('Lerp completely', () { - expect( - const StreamAvatarThemeData() - .lerp(_avatarThemeDataControl1, _avatarThemeDataControl2, 1), - _avatarThemeDataControl2); - }); - - test('Lerp halfway', () { - expect( - const StreamAvatarThemeData().lerp( - _avatarThemeDataControl1, - _avatarThemeDataControl2, - 0.5, - ), - _avatarThemeDataControlMidLerp, - // TODO: Remove skip, once we drop support for flutter v3.24.0 - skip: true, - reason: 'Currently failing in flutter v3.27.0 due to new color alpha', - ); - }); - }); - - test('Merging two AvatarThemeData results in the latter', () { - expect(_avatarThemeDataControl1.merge(_avatarThemeDataControl2), - _avatarThemeDataControl2); - }); -} - -const _avatarThemeDataControl1 = StreamAvatarThemeData(); - -final _avatarThemeDataControlMidLerp = StreamAvatarThemeData( - borderRadius: BorderRadius.circular(16), - constraints: const BoxConstraints.tightFor( - height: 33, - width: 33, - ), -); - -final _avatarThemeDataControl2 = StreamAvatarThemeData( - borderRadius: BorderRadius.circular(12), - constraints: const BoxConstraints.tightFor( - height: 34, - width: 34, - ), -); diff --git a/packages/stream_chat_flutter/test/src/theme/channel_header_theme_test.dart b/packages/stream_chat_flutter/test/src/theme/channel_header_theme_test.dart deleted file mode 100644 index 3f5a546923..0000000000 --- a/packages/stream_chat_flutter/test/src/theme/channel_header_theme_test.dart +++ /dev/null @@ -1,104 +0,0 @@ -import 'package:flutter/material.dart' hide TextTheme; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -void main() { - test('ChannelHeaderThemeData copyWith, ==, hashCode basics', () { - expect(const StreamChannelHeaderThemeData(), - const StreamChannelHeaderThemeData().copyWith()); - expect(const StreamChannelHeaderThemeData().hashCode, - const StreamChannelHeaderThemeData().copyWith().hashCode); - }); - - group('ChannelHeaderThemeData lerps', () { - test( - '''Light ChannelHeaderThemeData lerps completely to dark ChannelHeaderThemeData''', - () { - expect( - const StreamChannelHeaderThemeData() - .lerp(_channelThemeControl, _channelThemeControlDark, 1), - _channelThemeControlDark); - }); - - test( - '''Light ChannelHeaderThemeData lerps halfway to dark ChannelHeaderThemeData''', - () { - expect( - const StreamChannelHeaderThemeData().lerp( - _channelThemeControl, - _channelThemeControlDark, - 0.5, - ), - _channelThemeControlMidLerp, - // TODO: Remove skip, once we drop support for flutter v3.24.0 - skip: true, - reason: 'Currently failing in flutter v3.27.0 due to new color alpha', - ); - }); - - test( - '''Dark ChannelHeaderThemeData lerps completely to light ChannelHeaderThemeData''', - () { - expect( - const StreamChannelHeaderThemeData() - .lerp(_channelThemeControlDark, _channelThemeControl, 1), - _channelThemeControl); - }); - }); - - test('Merging dark and light themes results in a dark theme', () { - expect(_channelThemeControl.merge(_channelThemeControlDark), - _channelThemeControlDark); - }); -} - -final _channelThemeControl = StreamChannelHeaderThemeData( - avatarTheme: StreamAvatarThemeData( - borderRadius: BorderRadius.circular(20), - constraints: const BoxConstraints.tightFor( - height: 40, - width: 40, - ), - ), - color: const Color(0xff101418), - titleStyle: StreamTextTheme.light().headlineBold.copyWith( - color: const Color(0xffffffff), - ), - subtitleStyle: StreamTextTheme.light().footnote.copyWith( - color: const Color(0xff7a7a7a), - ), -); - -final _channelThemeControlMidLerp = StreamChannelHeaderThemeData( - avatarTheme: StreamAvatarThemeData( - borderRadius: BorderRadius.circular(20), - constraints: const BoxConstraints.tightFor( - height: 40, - width: 40, - ), - ), - color: const Color(0xff111417), - titleStyle: const TextStyle( - color: Color(0xffffffff), - fontWeight: FontWeight.w500, - fontSize: 16, - ), - subtitleStyle: StreamTextTheme.light().footnote.copyWith( - color: const Color(0xff7a7a7a), - ), -); - -final _channelThemeControlDark = StreamChannelHeaderThemeData( - avatarTheme: StreamAvatarThemeData( - borderRadius: BorderRadius.circular(20), - constraints: const BoxConstraints.tightFor( - height: 40, - width: 40, - ), - ), - color: StreamColorTheme.dark().barsBg, - titleStyle: StreamTextTheme.dark().headlineBold, - subtitleStyle: StreamTextTheme.dark().footnote.copyWith( - color: const Color(0xff7A7A7A), - ), -); diff --git a/packages/stream_chat_flutter/test/src/theme/channel_list_header_theme_test.dart b/packages/stream_chat_flutter/test/src/theme/channel_list_header_theme_test.dart deleted file mode 100644 index b9bd1f8c04..0000000000 --- a/packages/stream_chat_flutter/test/src/theme/channel_list_header_theme_test.dart +++ /dev/null @@ -1,99 +0,0 @@ -import 'package:flutter/material.dart' hide TextTheme; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -void main() { - test('ChannelListHeaderThemeData copyWith, ==, hashCode basics', () { - expect(const StreamChannelListHeaderThemeData(), - const StreamChannelListHeaderThemeData().copyWith()); - expect(const StreamChannelListHeaderThemeData().hashCode, - const StreamChannelListHeaderThemeData().copyWith().hashCode); - }); - - group('ChannelListHeaderThemeData lerps', () { - test( - '''Light ChannelListHeaderThemeData lerps completely to dark ChannelListHeaderThemeData''', - () { - expect( - const StreamChannelListHeaderThemeData().lerp( - _channelListHeaderThemeControl, - _channelListHeaderThemeControlDark, - 1), - _channelListHeaderThemeControlDark); - }); - - test( - '''Light ChannelListHeaderThemeData lerps halfway to dark ChannelListHeaderThemeData''', - () { - expect( - const StreamChannelListHeaderThemeData().lerp( - _channelListHeaderThemeControl, - _channelListHeaderThemeControlDark, - 0.5, - ), - _channelListHeaderThemeControlMidLerp, - // TODO: Remove skip, once we drop support for flutter v3.24.0 - skip: true, - reason: 'Currently failing in flutter v3.27.0 due to new color alpha', - ); - }); - - test( - '''Dark ChannelListHeaderThemeData lerps completely to light ChannelListHeaderThemeData''', - () { - expect( - const StreamChannelListHeaderThemeData().lerp( - _channelListHeaderThemeControlDark, - _channelListHeaderThemeControl, - 1), - _channelListHeaderThemeControl); - }); - }); - - test('Merging dark and light themes results in a dark theme', () { - expect( - _channelListHeaderThemeControl - .merge(_channelListHeaderThemeControlDark), - _channelListHeaderThemeControlDark); - }); -} - -final _channelListHeaderThemeControl = StreamChannelListHeaderThemeData( - avatarTheme: StreamAvatarThemeData( - borderRadius: BorderRadius.circular(20), - constraints: const BoxConstraints.tightFor( - height: 40, - width: 40, - ), - ), - color: StreamColorTheme.light().barsBg, - titleStyle: StreamTextTheme.light().headlineBold, -); - -final _channelListHeaderThemeControlMidLerp = StreamChannelListHeaderThemeData( - avatarTheme: StreamAvatarThemeData( - borderRadius: BorderRadius.circular(20), - constraints: const BoxConstraints.tightFor( - height: 40, - width: 40, - ), - ), - color: const Color(0xff88898a), - titleStyle: const TextStyle( - color: Color(0xff7f7f7f), - fontSize: 16, - fontWeight: FontWeight.w500, - ), -); - -final _channelListHeaderThemeControlDark = StreamChannelListHeaderThemeData( - avatarTheme: StreamAvatarThemeData( - borderRadius: BorderRadius.circular(20), - constraints: const BoxConstraints.tightFor( - height: 40, - width: 40, - ), - ), - color: StreamColorTheme.dark().barsBg, - titleStyle: StreamTextTheme.dark().headlineBold, -); diff --git a/packages/stream_chat_flutter/test/src/theme/channel_preview_theme_test.dart b/packages/stream_chat_flutter/test/src/theme/channel_preview_theme_test.dart deleted file mode 100644 index b966781e2a..0000000000 --- a/packages/stream_chat_flutter/test/src/theme/channel_preview_theme_test.dart +++ /dev/null @@ -1,119 +0,0 @@ -import 'package:flutter/material.dart' hide TextTheme; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -void main() { - test('ChannelPreviewThemeData copyWith, ==, hashCode basics', () { - expect(const StreamChannelPreviewThemeData(), - const StreamChannelPreviewThemeData().copyWith()); - expect(const StreamChannelPreviewThemeData().hashCode, - const StreamChannelPreviewThemeData().copyWith().hashCode); - }); - - group('ChannelPreviewThemeData lerps', () { - test( - '''Light ChannelPreviewThemeData lerps completely to dark ChannelPreviewThemeData''', - () { - expect( - const StreamChannelPreviewThemeData().lerp( - _channelPreviewThemeControl, _channelPreviewThemeControlDark, 1), - _channelPreviewThemeControlDark); - }); - - test( - '''Light ChannelPreviewThemeData lerps halfway to dark ChannelPreviewThemeData''', - () { - expect( - const StreamChannelPreviewThemeData().lerp( - _channelPreviewThemeControl, - _channelPreviewThemeControlDark, - 0.5, - ), - _channelPreviewThemeControlMidLerp, - // TODO: Remove skip, once we drop support for flutter v3.24.0 - skip: true, - reason: 'Currently failing in flutter v3.27.0 due to new color alpha', - ); - }); - - test( - '''Dark ChannelPreviewThemeData lerps completely to light ChannelPreviewThemeData''', - () { - expect( - const StreamChannelPreviewThemeData().lerp( - _channelPreviewThemeControlDark, _channelPreviewThemeControl, 1), - _channelPreviewThemeControl); - }); - }); - - test('Merging dark and light themes results in a dark theme', () { - expect(_channelPreviewThemeControl.merge(_channelPreviewThemeControlDark), - _channelPreviewThemeControlDark); - }); -} - -final _channelPreviewThemeControl = StreamChannelPreviewThemeData( - unreadCounterColor: StreamColorTheme.light().accentError, - avatarTheme: StreamAvatarThemeData( - borderRadius: BorderRadius.circular(20), - constraints: const BoxConstraints.tightFor( - height: 40, - width: 40, - ), - ), - titleStyle: StreamTextTheme.light().bodyBold, - subtitleStyle: StreamTextTheme.light().footnote.copyWith( - color: const Color(0xff7A7A7A), - ), - lastMessageAtStyle: StreamTextTheme.light().footnote.copyWith( - // ignore: deprecated_member_use - color: StreamColorTheme.light().textHighEmphasis.withOpacity(0.5), - ), - indicatorIconSize: 16, -); - -final _channelPreviewThemeControlMidLerp = StreamChannelPreviewThemeData( - unreadCounterColor: const Color(0xffff3742), - avatarTheme: StreamAvatarThemeData( - borderRadius: BorderRadius.circular(20), - constraints: const BoxConstraints.tightFor( - height: 40, - width: 40, - ), - ), - titleStyle: const TextStyle( - color: Color(0xff7f7f7f), - fontSize: 14, - fontWeight: FontWeight.w500, - ), - subtitleStyle: const TextStyle( - color: Color(0xff7a7a7a), - fontSize: 12, - fontWeight: FontWeight.w400, - ), - lastMessageAtStyle: StreamTextTheme.light().footnote.copyWith( - // ignore: deprecated_member_use - color: const Color(0x807f7f7f).withOpacity(0.5), - ), - indicatorIconSize: 16, -); - -final _channelPreviewThemeControlDark = StreamChannelPreviewThemeData( - unreadCounterColor: StreamColorTheme.dark().accentError, - avatarTheme: StreamAvatarThemeData( - borderRadius: BorderRadius.circular(20), - constraints: const BoxConstraints.tightFor( - height: 40, - width: 40, - ), - ), - titleStyle: StreamTextTheme.dark().bodyBold, - subtitleStyle: StreamTextTheme.dark().footnote.copyWith( - color: const Color(0xff7A7A7A), - ), - lastMessageAtStyle: StreamTextTheme.dark().footnote.copyWith( - // ignore: deprecated_member_use - color: StreamColorTheme.dark().textHighEmphasis.withOpacity(0.5), - ), - indicatorIconSize: 16, -); diff --git a/packages/stream_chat_flutter/test/src/theme/gallery_footer_theme_test.dart b/packages/stream_chat_flutter/test/src/theme/gallery_footer_theme_test.dart deleted file mode 100644 index e06e0ac157..0000000000 --- a/packages/stream_chat_flutter/test/src/theme/gallery_footer_theme_test.dart +++ /dev/null @@ -1,188 +0,0 @@ -import 'package:flutter/material.dart' hide TextTheme; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -class MockStreamChatClient extends Mock implements StreamChatClient {} - -void main() { - test('GalleryFooterThemeData copyWith, ==, hashCode basics', () { - expect(const StreamGalleryFooterThemeData(), - const StreamGalleryFooterThemeData().copyWith()); - expect(const StreamGalleryFooterThemeData().hashCode, - const StreamGalleryFooterThemeData().copyWith().hashCode); - }); - - test( - '''Light GalleryFooterThemeData lerps completely to dark GalleryFooterThemeData''', - () { - expect( - const StreamGalleryFooterThemeData().lerp( - _galleryFooterThemeDataControl, - _galleryFooterThemeDataControlDark, - 1), - _galleryFooterThemeDataControlDark); - }); - - test( - '''Light GalleryFooterThemeData lerps halfway to dark GalleryFooterThemeData''', - () { - expect( - const StreamGalleryFooterThemeData().lerp( - _galleryFooterThemeDataControl, - _galleryFooterThemeDataControlDark, - 0.5, - ), - _galleryFooterThemeDataControlMidLerp, - // TODO: Remove skip, once we drop support for flutter v3.24.0 - skip: true, - reason: 'Currently failing in flutter v3.27.0 due to new color alpha', - ); - }); - - test( - '''Dark GalleryFooterThemeData lerps completely to light GalleryFooterThemeData''', - () { - expect( - const StreamGalleryFooterThemeData().lerp( - _galleryFooterThemeDataControlDark, - _galleryFooterThemeDataControl, - 1), - _galleryFooterThemeDataControl); - }); - - test('Merging dark and light themes results in a dark theme', () { - expect( - _galleryFooterThemeDataControl - .merge(_galleryFooterThemeDataControlDark), - _galleryFooterThemeDataControlDark); - }); - - test('Merging dark and light themes results in a dark theme', () { - expect( - _galleryFooterThemeDataControlDark - .merge(_galleryFooterThemeDataControl), - _galleryFooterThemeDataControl); - }); - - testWidgets( - 'Passing no GalleryFooterThemeData returns default light theme values', - (WidgetTester tester) async { - late BuildContext _context; - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: MockStreamChatClient(), - child: child, - ), - home: Builder( - builder: (context) { - _context = context; - return const SizedBox.shrink(); - }, - ), - ), - ); - - final imageFooterTheme = StreamGalleryFooterTheme.of(_context); - expect(imageFooterTheme.backgroundColor, - _galleryFooterThemeDataControl.backgroundColor); - expect(imageFooterTheme.shareIconColor, - _galleryFooterThemeDataControl.shareIconColor); - expect(imageFooterTheme.titleTextStyle, - _galleryFooterThemeDataControl.titleTextStyle); - expect(imageFooterTheme.gridIconButtonColor, - _galleryFooterThemeDataControl.gridIconButtonColor); - expect(imageFooterTheme.bottomSheetBarrierColor, - _galleryFooterThemeDataControl.bottomSheetBarrierColor); - expect(imageFooterTheme.bottomSheetBackgroundColor, - _galleryFooterThemeDataControl.bottomSheetBackgroundColor); - expect(imageFooterTheme.bottomSheetCloseIconColor, - _galleryFooterThemeDataControl.bottomSheetCloseIconColor); - expect(imageFooterTheme.bottomSheetPhotosTextStyle, - _galleryFooterThemeDataControl.bottomSheetPhotosTextStyle); - }); - - testWidgets( - 'Passing no GalleryFooterThemeData returns default dark theme values', - (WidgetTester tester) async { - late BuildContext _context; - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: MockStreamChatClient(), - streamChatThemeData: StreamChatThemeData.dark(), - child: child, - ), - home: Builder( - builder: (context) { - _context = context; - return const SizedBox.shrink(); - }, - ), - ), - ); - - final imageFooterTheme = StreamGalleryFooterTheme.of(_context); - expect(imageFooterTheme.backgroundColor, - _galleryFooterThemeDataControlDark.backgroundColor); - expect(imageFooterTheme.shareIconColor, - _galleryFooterThemeDataControlDark.shareIconColor); - expect(imageFooterTheme.titleTextStyle, - _galleryFooterThemeDataControlDark.titleTextStyle); - expect(imageFooterTheme.gridIconButtonColor, - _galleryFooterThemeDataControlDark.gridIconButtonColor); - expect(imageFooterTheme.bottomSheetBarrierColor, - _galleryFooterThemeDataControlDark.bottomSheetBarrierColor); - expect(imageFooterTheme.bottomSheetBackgroundColor, - _galleryFooterThemeDataControlDark.bottomSheetBackgroundColor); - expect(imageFooterTheme.bottomSheetCloseIconColor, - _galleryFooterThemeDataControlDark.bottomSheetCloseIconColor); - expect(imageFooterTheme.bottomSheetPhotosTextStyle, - _galleryFooterThemeDataControlDark.bottomSheetPhotosTextStyle); - }); -} - -// Light theme control -final _galleryFooterThemeDataControl = StreamGalleryFooterThemeData( - backgroundColor: StreamColorTheme.light().barsBg, - shareIconColor: StreamColorTheme.light().textHighEmphasis, - titleTextStyle: StreamTextTheme.light().headlineBold, - gridIconButtonColor: StreamColorTheme.light().textHighEmphasis, - bottomSheetBackgroundColor: StreamColorTheme.light().barsBg, - bottomSheetBarrierColor: StreamColorTheme.light().overlay, - bottomSheetCloseIconColor: StreamColorTheme.light().textHighEmphasis, - bottomSheetPhotosTextStyle: StreamTextTheme.light().headlineBold, -); - -// Mid-lerp theme control -const _galleryFooterThemeDataControlMidLerp = StreamGalleryFooterThemeData( - backgroundColor: Color(0xff88898a), - shareIconColor: Color(0xff7f7f7f), - titleTextStyle: TextStyle( - color: Color(0xff7f7f7f), - fontSize: 16, - fontWeight: FontWeight.w500, - ), - gridIconButtonColor: Color(0xff7f7f7f), - bottomSheetBarrierColor: Color(0x4c000000), - bottomSheetBackgroundColor: Color(0xff88898a), - bottomSheetPhotosTextStyle: TextStyle( - color: Color(0xff7f7f7f), - fontSize: 16, - fontWeight: FontWeight.w500, - ), - bottomSheetCloseIconColor: Color(0xff7f7f7f), -); - -// Dark theme control -final _galleryFooterThemeDataControlDark = StreamGalleryFooterThemeData( - backgroundColor: StreamColorTheme.dark().barsBg, - shareIconColor: StreamColorTheme.dark().textHighEmphasis, - titleTextStyle: StreamTextTheme.dark().headlineBold, - gridIconButtonColor: StreamColorTheme.dark().textHighEmphasis, - bottomSheetBackgroundColor: StreamColorTheme.dark().barsBg, - bottomSheetBarrierColor: StreamColorTheme.dark().overlay, - bottomSheetCloseIconColor: StreamColorTheme.dark().textHighEmphasis, - bottomSheetPhotosTextStyle: StreamTextTheme.dark().headlineBold, -); diff --git a/packages/stream_chat_flutter/test/src/theme/gallery_header_theme_test.dart b/packages/stream_chat_flutter/test/src/theme/gallery_header_theme_test.dart deleted file mode 100644 index 6c080193ed..0000000000 --- a/packages/stream_chat_flutter/test/src/theme/gallery_header_theme_test.dart +++ /dev/null @@ -1,189 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -class MockStreamChatClient extends Mock implements StreamChatClient {} - -void main() { - test('GalleryHeaderThemeData copyWith, ==, hashCode basics', () { - expect(const StreamGalleryHeaderThemeData(), - const StreamGalleryHeaderThemeData().copyWith()); - expect(const StreamGalleryHeaderThemeData().hashCode, - const StreamGalleryHeaderThemeData().copyWith().hashCode); - }); - - test( - '''Light GalleryHeaderThemeData lerps completely to dark GalleryHeaderThemeData''', - () { - expect( - const StreamGalleryHeaderThemeData().lerp( - _galleryHeaderThemeDataControl, - _galleryHeaderThemeDataDarkControl, - 1), - _galleryHeaderThemeDataDarkControl); - }); - - test( - '''Light GalleryHeaderThemeData lerps halfway to dark GalleryHeaderThemeData''', - () { - expect( - const StreamGalleryHeaderThemeData().lerp( - _galleryHeaderThemeDataControl, - _galleryHeaderThemeDataDarkControl, - 0.5, - ), - _galleryHeaderThemeDataHalfLerpControl, - // TODO: Remove skip, once we drop support for flutter v3.24.0 - skip: true, - reason: 'Currently failing in flutter v3.27.0 due to new color alpha', - ); - }); - - test( - '''Dark GalleryHeaderThemeData lerps completely to light GalleryHeaderThemeData''', - () { - expect( - const StreamGalleryHeaderThemeData().lerp( - _galleryHeaderThemeDataDarkControl, - _galleryHeaderThemeDataControl, - 1), - _galleryHeaderThemeDataControl); - }); - - test('Merging dark and light themes results in a dark theme', () { - expect( - _galleryHeaderThemeDataControl - .merge(_galleryHeaderThemeDataDarkControl), - _galleryHeaderThemeDataDarkControl); - }); - - testWidgets( - 'Passing no GalleryHeaderThemeData returns default light theme values', - (WidgetTester tester) async { - late BuildContext _context; - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: MockStreamChatClient(), - child: child, - ), - home: Builder( - builder: (context) { - _context = context; - return const SizedBox.shrink(); - }, - ), - ), - ); - - final imageHeaderTheme = StreamGalleryHeaderTheme.of(_context); - expect(imageHeaderTheme.closeButtonColor, - _galleryHeaderThemeDataControl.closeButtonColor); - expect(imageHeaderTheme.backgroundColor, - _galleryHeaderThemeDataControl.backgroundColor); - expect(imageHeaderTheme.iconMenuPointColor, - _galleryHeaderThemeDataControl.iconMenuPointColor); - expect(imageHeaderTheme.titleTextStyle, - _galleryHeaderThemeDataControl.titleTextStyle); - expect(imageHeaderTheme.subtitleTextStyle, - _galleryHeaderThemeDataControl.subtitleTextStyle); - expect(imageHeaderTheme.bottomSheetBarrierColor, - _galleryHeaderThemeDataControl.bottomSheetBarrierColor); - }); - - testWidgets( - 'Passing no GalleryHeaderThemeData returns default dark theme values', - (WidgetTester tester) async { - late BuildContext _context; - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: MockStreamChatClient(), - streamChatThemeData: StreamChatThemeData.dark(), - child: child, - ), - home: Builder( - builder: (context) { - _context = context; - return const SizedBox.shrink(); - }, - ), - ), - ); - - final imageHeaderTheme = StreamGalleryHeaderTheme.of(_context); - expect(imageHeaderTheme.closeButtonColor, - _galleryHeaderThemeDataDarkControl.closeButtonColor); - expect(imageHeaderTheme.backgroundColor, - _galleryHeaderThemeDataDarkControl.backgroundColor); - expect(imageHeaderTheme.iconMenuPointColor, - _galleryHeaderThemeDataDarkControl.iconMenuPointColor); - expect(imageHeaderTheme.titleTextStyle, - _galleryHeaderThemeDataDarkControl.titleTextStyle); - expect(imageHeaderTheme.subtitleTextStyle, - _galleryHeaderThemeDataDarkControl.subtitleTextStyle); - expect(imageHeaderTheme.bottomSheetBarrierColor, - _galleryHeaderThemeDataDarkControl.bottomSheetBarrierColor); - }); -} - -// Light theme test control. -final _galleryHeaderThemeDataControl = StreamGalleryHeaderThemeData( - closeButtonColor: const Color(0xff000000), - backgroundColor: const Color(0xffffffff), - iconMenuPointColor: const Color(0xff000000), - titleTextStyle: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Colors.black, - ), - subtitleTextStyle: const TextStyle( - fontSize: 12, - color: Colors.black, - fontWeight: FontWeight.w400, - ).copyWith( - color: const Color(0xff7A7A7A), - ), - bottomSheetBarrierColor: const Color.fromRGBO(0, 0, 0, 0.2), -); - -// Light theme test control. -final _galleryHeaderThemeDataHalfLerpControl = StreamGalleryHeaderThemeData( - closeButtonColor: const Color(0xff7f7f7f), - backgroundColor: const Color(0xff88898a), - iconMenuPointColor: const Color(0xff7f7f7f), - titleTextStyle: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Color(0xff7f7f7f), - ), - subtitleTextStyle: const TextStyle( - fontSize: 12, - color: Color(0xff7a7a7a), - fontWeight: FontWeight.w400, - ).copyWith( - color: const Color(0xff7A7A7A), - ), - bottomSheetBarrierColor: const Color(0x4c000000), -); - -// Dark theme test control. -final _galleryHeaderThemeDataDarkControl = StreamGalleryHeaderThemeData( - closeButtonColor: const Color(0xffffffff), - backgroundColor: const Color(0xff121416), - iconMenuPointColor: const Color(0xffffffff), - titleTextStyle: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Colors.white, - ), - subtitleTextStyle: const TextStyle( - fontSize: 12, - color: Colors.white, - fontWeight: FontWeight.w400, - ).copyWith( - color: const Color(0xff7A7A7A), - ), - bottomSheetBarrierColor: const Color.fromRGBO(0, 0, 0, 0.4), -); diff --git a/packages/stream_chat_flutter/test/src/theme/message_input_theme_test.dart b/packages/stream_chat_flutter/test/src/theme/message_input_theme_test.dart deleted file mode 100644 index 425fb5c151..0000000000 --- a/packages/stream_chat_flutter/test/src/theme/message_input_theme_test.dart +++ /dev/null @@ -1,129 +0,0 @@ -import 'package:flutter/material.dart' hide TextTheme; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -void main() { - test('MessageInputThemeData copyWith, ==, hashCode basics', () { - expect(const StreamMessageInputThemeData(), - const StreamMessageInputThemeData().copyWith()); - expect(const StreamMessageInputThemeData().hashCode, - const StreamMessageInputThemeData().copyWith().hashCode); - }); - - group('MessageInputThemeData lerps correctly', () { - test('Lerp completely from light to dark', () { - expect( - const StreamMessageInputThemeData().lerp( - _messageInputThemeControl, _messageInputThemeControlDark, 1), - _messageInputThemeControlDark); - }); - - test('Lerp halfway from light to dark', () { - expect( - const StreamMessageInputThemeData().lerp( - _messageInputThemeControl, - _messageInputThemeControlDark, - 0.5, - ), - _messageInputThemeControlMidLerp, - // TODO: Remove skip, once we drop support for flutter v3.24.0 - skip: true, - reason: 'Currently failing in flutter v3.27.0 due to new color alpha', - ); - }); - - test('Lerp completely from dark to light', () { - expect( - const StreamMessageInputThemeData().lerp( - _messageInputThemeControlDark, _messageInputThemeControl, 1), - _messageInputThemeControl); - }); - }); - - test('Merging two MessageInputThemeData results in the latter', () { - expect(_messageInputThemeControl.merge(_messageInputThemeControlDark), - _messageInputThemeControlDark); - }); -} - -final _messageInputThemeControl = StreamMessageInputThemeData( - borderRadius: BorderRadius.circular(20), - sendAnimationDuration: const Duration(milliseconds: 300), - actionButtonColor: StreamColorTheme.light().accentPrimary, - actionButtonIdleColor: StreamColorTheme.light().textLowEmphasis, - expandButtonColor: StreamColorTheme.light().accentPrimary, - sendButtonColor: StreamColorTheme.light().accentPrimary, - sendButtonIdleColor: StreamColorTheme.light().disabled, - inputBackgroundColor: StreamColorTheme.light().barsBg, - inputTextStyle: StreamTextTheme.light().body, - idleBorderGradient: LinearGradient( - stops: const [0.0, 1.0], - colors: [ - StreamColorTheme.light().disabled, - StreamColorTheme.light().disabled, - ], - ), - activeBorderGradient: LinearGradient( - stops: const [0.0, 1.0], - colors: [ - StreamColorTheme.light().disabled, - StreamColorTheme.light().disabled, - ], - ), -); - -final _messageInputThemeControlMidLerp = StreamMessageInputThemeData( - borderRadius: BorderRadius.circular(20), - sendAnimationDuration: const Duration(milliseconds: 300), - inputBackgroundColor: const Color(0xff88898a), - actionButtonColor: const Color(0xff196eff), - actionButtonIdleColor: const Color(0xff7a7a7a), - sendButtonColor: const Color(0xff196eff), - sendButtonIdleColor: const Color(0xff848585), - expandButtonColor: const Color(0xff196eff), - inputTextStyle: const TextStyle( - color: Color(0xff7f7f7f), - fontSize: 14, - fontWeight: FontWeight.w400, - ), - idleBorderGradient: const LinearGradient( - stops: [0.0, 1.0], - colors: [ - Color(0xff848585), - Color(0xff848585), - ], - ), - activeBorderGradient: const LinearGradient( - stops: [0.0, 1.0], - colors: [ - Color(0xff848585), - Color(0xff848585), - ], - ), -); - -final _messageInputThemeControlDark = StreamMessageInputThemeData( - borderRadius: BorderRadius.circular(20), - sendAnimationDuration: const Duration(milliseconds: 300), - actionButtonColor: StreamColorTheme.dark().accentPrimary, - actionButtonIdleColor: StreamColorTheme.dark().textLowEmphasis, - expandButtonColor: StreamColorTheme.dark().accentPrimary, - sendButtonColor: StreamColorTheme.dark().accentPrimary, - sendButtonIdleColor: StreamColorTheme.dark().disabled, - inputBackgroundColor: StreamColorTheme.dark().barsBg, - inputTextStyle: StreamTextTheme.dark().body, - idleBorderGradient: LinearGradient( - stops: const [0.0, 1.0], - colors: [ - StreamColorTheme.dark().disabled, - StreamColorTheme.dark().disabled, - ], - ), - activeBorderGradient: LinearGradient( - stops: const [0.0, 1.0], - colors: [ - StreamColorTheme.dark().disabled, - StreamColorTheme.dark().disabled, - ], - ), -); diff --git a/packages/stream_chat_flutter/test/src/theme/message_list_view_theme_test.dart b/packages/stream_chat_flutter/test/src/theme/message_list_view_theme_test.dart deleted file mode 100644 index ebcc8fbccb..0000000000 --- a/packages/stream_chat_flutter/test/src/theme/message_list_view_theme_test.dart +++ /dev/null @@ -1,161 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -import '../mocks.dart'; - -class MockStreamChatClient extends Mock implements StreamChatClient {} - -void main() { - test('MessageListViewThemeData copyWith, ==, hashCode basics', () { - expect(const StreamMessageListViewThemeData(), - const StreamMessageListViewThemeData().copyWith()); - expect(const StreamMessageListViewThemeData().hashCode, - const StreamMessageListViewThemeData().copyWith().hashCode); - }); - - test( - '''Light MessageListViewThemeData lerps completely to dark MessageListViewThemeData''', - () { - expect( - const StreamMessageListViewThemeData().lerp( - _messageListViewThemeDataControl, - _messageListViewThemeDataControlDark, - 1), - _messageListViewThemeDataControlDark); - }); - - test( - '''Light MessageListViewThemeData lerps halfway to dark MessageListViewThemeData''', - () { - expect( - const StreamMessageListViewThemeData().lerp( - _messageListViewThemeDataControl, - _messageListViewThemeDataControlDark, - 0.5, - ), - _messageListViewThemeDataControlHalfLerp, - // TODO: Remove skip, once we drop support for flutter v3.24.0 - skip: true, - reason: 'Currently failing in flutter v3.27.0 due to new color alpha', - ); - }); - - test( - '''Dark MessageListViewThemeData lerps completely to light MessageListViewThemeData''', - () { - expect( - const StreamMessageListViewThemeData().lerp( - _messageListViewThemeDataControlDark, - _messageListViewThemeDataControl, - 1), - _messageListViewThemeDataControl); - }); - - test('Merging dark and light themes results in a dark theme', () { - expect( - _messageListViewThemeDataControl - .merge(_messageListViewThemeDataControlDark), - _messageListViewThemeDataControlDark); - }); - - testWidgets( - 'Passing no MessageListViewThemeData returns default light theme values', - (WidgetTester tester) async { - late BuildContext _context; - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: MockStreamChatClient(), - child: child, - ), - home: Builder( - builder: (BuildContext context) { - _context = context; - return const SizedBox.shrink(); - }, - ), - ), - ); - - final messageListViewTheme = StreamMessageListViewTheme.of(_context); - expect(messageListViewTheme.backgroundColor, - _messageListViewThemeDataControl.backgroundColor); - }); - - testWidgets( - 'Passing no MessageListViewThemeData returns default dark theme values', - (WidgetTester tester) async { - late BuildContext _context; - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: MockStreamChatClient(), - streamChatThemeData: StreamChatThemeData.dark(), - child: child, - ), - home: Builder( - builder: (BuildContext context) { - _context = context; - return const SizedBox.shrink(); - }, - ), - ), - ); - - final messageListViewTheme = StreamMessageListViewTheme.of(_context); - expect(messageListViewTheme.backgroundColor, - _messageListViewThemeDataControlDark.backgroundColor); - }); - - testWidgets( - 'Pass backgroundImage to MessageListViewThemeData return backgroundImage', - (WidgetTester tester) async { - late BuildContext _context; - await tester.pumpWidget( - MaterialApp( - builder: (context, child) => StreamChat( - client: MockStreamChatClient(), - streamChatThemeData: StreamChatThemeData.light() - .copyWith(messageListViewTheme: _messageListViewThemeDataImage), - child: child, - ), - home: Builder( - builder: (BuildContext context) { - _context = context; - return Scaffold( - body: StreamChannel( - channel: MockChannel(), - child: const StreamMessageListView(), - ), - ); - }, - ), - ), - ); - - final messageListViewTheme = StreamMessageListViewTheme.of(_context); - expect(messageListViewTheme.backgroundImage, - _messageListViewThemeDataImage.backgroundImage); - }); -} - -final _messageListViewThemeDataControl = StreamMessageListViewThemeData( - backgroundColor: StreamColorTheme.light().barsBg, -); - -const _messageListViewThemeDataControlHalfLerp = StreamMessageListViewThemeData( - backgroundColor: Color(0xff88898a), -); - -final _messageListViewThemeDataControlDark = StreamMessageListViewThemeData( - backgroundColor: StreamColorTheme.dark().barsBg, -); - -const _messageListViewThemeDataImage = StreamMessageListViewThemeData( - backgroundImage: DecorationImage( - image: AssetImage('example/assets/background_doodle.png'), - fit: BoxFit.cover, - ), -); diff --git a/packages/stream_chat_flutter/test/src/theme/message_theme_test.dart b/packages/stream_chat_flutter/test/src/theme/message_theme_test.dart deleted file mode 100644 index 246d9d9070..0000000000 --- a/packages/stream_chat_flutter/test/src/theme/message_theme_test.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'package:flutter/material.dart' hide TextTheme; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -void main() { - test('MessageThemeData copyWith, ==, hashCode basics', () { - expect(const StreamMessageThemeData(), - const StreamMessageThemeData().copyWith()); - expect(const StreamMessageThemeData().hashCode, - const StreamMessageThemeData().copyWith().hashCode); - }); - - group('MessageThemeData lerps', () { - test('''Light MessageThemeData lerps completely to dark MessageThemeData''', - () { - expect( - const StreamMessageThemeData() - .lerp(_messageThemeControl, _messageThemeControlDark, 1), - _messageThemeControlDark); - }); - - test('''Dark MessageThemeData lerps completely to light MessageThemeData''', - () { - expect( - const StreamMessageThemeData() - .lerp(_messageThemeControlDark, _messageThemeControl, 1), - _messageThemeControl); - }); - }); - - test('Merging dark and light themes results in a dark theme', () { - expect(_messageThemeControl.merge(_messageThemeControlDark), - _messageThemeControlDark); - }); -} - -final _messageThemeControl = StreamMessageThemeData( - messageAuthorStyle: StreamTextTheme.light().footnote.copyWith( - color: StreamColorTheme.light().textLowEmphasis, - ), - messageTextStyle: StreamTextTheme.light().body, - createdAtStyle: StreamTextTheme.light().footnote.copyWith( - color: StreamColorTheme.light().textLowEmphasis, - ), - repliesStyle: StreamTextTheme.light().footnoteBold.copyWith( - color: StreamColorTheme.light().accentPrimary, - ), - messageBackgroundColor: StreamColorTheme.light().disabled, - reactionsBackgroundColor: StreamColorTheme.light().barsBg, - reactionsBorderColor: StreamColorTheme.light().borders, - reactionsMaskColor: StreamColorTheme.light().appBg, - messageBorderColor: StreamColorTheme.light().disabled, - avatarTheme: StreamAvatarThemeData( - borderRadius: BorderRadius.circular(20), - constraints: const BoxConstraints.tightFor( - height: 32, - width: 32, - ), - ), - messageLinksStyle: TextStyle( - color: StreamColorTheme.light().accentPrimary, - ), - urlAttachmentBackgroundColor: StreamColorTheme.light().linkBg, -); - -final _messageThemeControlDark = StreamMessageThemeData( - messageAuthorStyle: StreamTextTheme.dark().footnote.copyWith( - color: StreamColorTheme.dark().textLowEmphasis, - ), - messageTextStyle: StreamTextTheme.dark().body, - createdAtStyle: StreamTextTheme.dark().footnote.copyWith( - color: StreamColorTheme.dark().textLowEmphasis, - ), - repliesStyle: StreamTextTheme.dark().footnoteBold.copyWith( - color: StreamColorTheme.dark().accentPrimary, - ), - messageBackgroundColor: StreamColorTheme.dark().disabled, - reactionsBackgroundColor: StreamColorTheme.dark().barsBg, - reactionsBorderColor: StreamColorTheme.dark().borders, - reactionsMaskColor: StreamColorTheme.dark().appBg, - messageBorderColor: StreamColorTheme.dark().disabled, - avatarTheme: StreamAvatarThemeData( - borderRadius: BorderRadius.circular(20), - constraints: const BoxConstraints.tightFor( - height: 32, - width: 32, - ), - ), - messageLinksStyle: TextStyle( - color: StreamColorTheme.dark().accentPrimary, - ), - urlAttachmentBackgroundColor: StreamColorTheme.dark().linkBg, -); diff --git a/packages/stream_chat_flutter/test/src/utils/extension_test.dart b/packages/stream_chat_flutter/test/src/utils/extension_test.dart deleted file mode 100644 index 0ea96fd9dc..0000000000 --- a/packages/stream_chat_flutter/test/src/utils/extension_test.dart +++ /dev/null @@ -1,393 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -void main() { - group('List.search', () { - test('should work fine', () { - final tommaso = User(id: 'tommaso', name: 'Tommaso'); - final thierry = User(id: 'thierry', name: 'Thierry'); - final users = [tommaso, thierry]; - - expect(users.search('Tom'), [tommaso]); - expect(users.search('Thier'), [thierry]); - }); - - test('should search using UpperCased', () { - final tommaso = User(id: 'tommaso', name: 'Tommaso'); - final thierry = User(id: 'thierry', name: 'Thierry'); - final users = [tommaso, thierry]; - - expect(users.search('tom'), [tommaso]); - expect(users.search('thier'), [thierry]); - }); - - test('should search by .id or .name', () { - final user1 = User(id: 'searchingThis'); - final user2 = User(id: 'x', name: 'searchingThis'); - - expect([user1].search('sear'), [user1]); - expect([user2].search('sear'), [user2]); - }); - - test('should search transliterated', () { - final tommaso = User(id: 'tommaso', name: 'Tommaso'); - final thierry = User(id: 'thierry', name: 'Thierry'); - final users = [tommaso, thierry]; - - expect(users.search('tóm'), [tommaso]); - expect(users.search('thĆ­er'), [thierry]); - }); - - test('search and sorted by distance', () { - final tommaso = User(id: 'tommaso', name: 'Tommaso'); - final tomas = User(id: 'tomas', name: 'Tomas'); - final users = [tommaso, tomas]; - - expect(users.search('tom'), [tomas, tommaso]); - }); - - test('should work fine with cyrillic diacritics', () { - final petyo = User(id: '42', name: 'ŠŸŠµŃ‚ŃŒŠ¾'); - final anastasia = User(id: '13', name: 'Анастасiя'); - final dmitriy = User(id: '99', name: 'Дмитрий'); - final users = [petyo, anastasia, dmitriy]; - - expect(users.search('petyo'), []); - expect(users.search('Пе'), [petyo]); - expect(users.search('Ана'), [anastasia]); - expect(users.search('Дмитри'), [dmitriy]); - expect(users.search('Дмитрии'), [dmitriy]); - }); - - test('should work fine with french diacritics', () { - final user = User(id: 'fra', name: 'franƧois'); - - expect([user].search('franƧois'), [user]); - expect([user].search('franc'), [user]); - }); - }); - - group('String.isOnlyEmoji', () { - test('should return false for empty or > 3 strings', () { - expect(''.isOnlyEmoji, false); - expect('aaašŸ“šŸ’œ'.isOnlyEmoji, false); - expect('šŸ“šŸ’œšŸ“šŸ’œ'.isOnlyEmoji, false); - }); - - test('should detect strings made only by emojis', () { - expect('ašŸ“šŸ’œ'.isOnlyEmoji, false); - expect('šŸ“šŸ’œšŸ“'.isOnlyEmoji, true); - expect('🌶'.isOnlyEmoji, true); - expect('🌶1'.isOnlyEmoji, false); - expect('šŸ‘Øā€šŸ‘ØšŸ‘Øā€šŸ‘Ø'.isOnlyEmoji, true); - expect('šŸ‘Øā€šŸ‘ØšŸ‘Øā€šŸ‘Ø '.isOnlyEmoji, true); - expect('šŸ‘ØšŸ‘ØšŸ‘ØšŸ‘Ø'.isOnlyEmoji, false); - expect('⭐⭐⭐'.isOnlyEmoji, true); - expect('⭕⭕⭐'.isOnlyEmoji, true); - expect('āœ…'.isOnlyEmoji, true); - expect('ā˜ŗļø'.isOnlyEmoji, true); - }); - - test('Korean vowels', () { - expect('慏'.isOnlyEmoji, false); - expect('慑'.isOnlyEmoji, false); - expect('慓'.isOnlyEmoji, false); - expect('慕'.isOnlyEmoji, false); - expect('慗'.isOnlyEmoji, false); - expect('慛'.isOnlyEmoji, false); - expect('慜'.isOnlyEmoji, false); - expect('慠'.isOnlyEmoji, false); - expect('ć…”'.isOnlyEmoji, false); - expect('ć…£'.isOnlyEmoji, false); - }); - - test('Korean consonants', () { - expect('愱'.isOnlyEmoji, false); - expect('ć„“'.isOnlyEmoji, false); - expect('ć„·'.isOnlyEmoji, false); - expect('ㄹ'.isOnlyEmoji, false); - expect('慁'.isOnlyEmoji, false); - expect('慂'.isOnlyEmoji, false); - expect('慅'.isOnlyEmoji, false); - expect('慇'.isOnlyEmoji, false); - expect('慈'.isOnlyEmoji, false); - expect('慊'.isOnlyEmoji, false); - expect('態'.isOnlyEmoji, false); - expect('慌'.isOnlyEmoji, false); - expect('慍'.isOnlyEmoji, false); - expect('慎'.isOnlyEmoji, false); - }); - - test('Korean syllables', () { - expect('ź°€'.isOnlyEmoji, false); - expect('ė‚˜'.isOnlyEmoji, false); - expect('다'.isOnlyEmoji, false); - expect('ė¼'.isOnlyEmoji, false); - expect('마'.isOnlyEmoji, false); - expect('ė°”'.isOnlyEmoji, false); - expect('사'.isOnlyEmoji, false); - expect('ģ•„'.isOnlyEmoji, false); - expect('ģž'.isOnlyEmoji, false); - expect('ģ°Ø'.isOnlyEmoji, false); - expect('칓'.isOnlyEmoji, false); - expect('ķƒ€'.isOnlyEmoji, false); - expect('파'.isOnlyEmoji, false); - expect('ķ•˜'.isOnlyEmoji, false); - }); - - // https://github.com/GetStream/stream-chat-flutter/issues/1502 - test('Issue:#1502', () { - expect('ć„“'.isOnlyEmoji, false); - expect('ć„“ć…‡'.isOnlyEmoji, false); - expect('慇態'.isOnlyEmoji, false); - }); - - // https://github.com/GetStream/stream-chat-flutter/issues/1505 - test('Issue:#1505', () { - expect('慎慎慎'.isOnlyEmoji, false); - expect('慎慎慎慎'.isOnlyEmoji, false); - }); - }); - - group('Message Extension Tests', () { - test('replaceMentions should replace user mentions with names and IDs', () { - final user1 = User(id: 'user1', name: 'Alice'); - final user2 = User(id: 'user2', name: 'Bob'); - - final message = Message( - text: 'Hello, @user1 and @user2!', - mentionedUsers: [user1, user2], - ); - - final modifiedMessage = message.replaceMentions(); - - expect(modifiedMessage.text, contains('[@Alice](user1)')); - expect(modifiedMessage.text, contains('[@Bob](user2)')); - }); - - test('replaceMentions without linkify should not add links', () { - final user = User(id: 'user1', name: 'Alice'); - - final message = Message( - text: 'Hello, @user1!', - mentionedUsers: [user], - ); - - final modifiedMessage = message.replaceMentions(linkify: false); - - expect(modifiedMessage.text, contains('@Alice')); - }); - - test('replaceMentions should handle mentions with usernames', () { - final user = User(id: 'user1', name: 'Alice'); - - final message = Message( - text: 'Hello, @Alice!', - mentionedUsers: [user], - ); - - final modifiedMessage = message.replaceMentions(); - - expect(modifiedMessage.text, contains('[@Alice](user1)')); - }); - - test( - '''replaceMentions without linkify should not change mentions with usernames''', - () { - final user = User(id: 'user1', name: 'Alice'); - - final message = Message( - text: 'Hello, @Alice!', - mentionedUsers: [user], - ); - - final modifiedMessage = message.replaceMentions(linkify: false); - - expect(modifiedMessage.text, contains('@Alice')); - }, - ); - - test( - 'replaceMentions should replace mixed user mentions with names and IDs', - () { - final user1 = User(id: 'user1', name: 'Alice'); - final user2 = User(id: 'user2', name: 'Bob'); - - final message = Message( - text: 'Hello, @user1 and @Bob!', - mentionedUsers: [user1, user2], - ); - - final modifiedMessage = message.replaceMentions(); - - expect(modifiedMessage.text, contains('[@Alice](user1)')); - expect(modifiedMessage.text, contains('[@Bob](user2)')); - }, - ); - - test('replaceMentionsWithId should replace user names with IDs', () { - final user1 = User(id: 'user1', name: 'Alice'); - final user2 = User(id: 'user2', name: 'Bob'); - - final message = Message( - text: 'Hello, @Alice and @Bob!', - mentionedUsers: [user1, user2], - ); - - final modifiedMessage = message.replaceMentionsWithId(); - - expect(modifiedMessage.text, contains('@user1')); - expect(modifiedMessage.text, contains('@user2')); - expect(modifiedMessage.text, isNot(contains('@Alice'))); - expect(modifiedMessage.text, isNot(contains('@Bob'))); - }); - - test( - 'replaceMentionsWithId should not change message without mentions', - () { - final message = Message( - text: 'Hello, @Alice!', - ); - - final modifiedMessage = message.replaceMentionsWithId(); - - expect(modifiedMessage.text, equals('Hello, @Alice!')); - expect(modifiedMessage.text, isNot(contains('@user1'))); - }, - ); - - test('replaceMentionsWithId should handle message with only mention', () { - final user = User(id: 'user1', name: 'Alice'); - - final message = Message( - text: '@Alice', - mentionedUsers: [user], - ); - - final modifiedMessage = message.replaceMentionsWithId(); - - expect(modifiedMessage.text, contains('@user1')); - expect(modifiedMessage.text, isNot(contains('@Alice'))); - }); - }); - - group('Message List Extension Tests', () { - group('lastUnreadMessage', () { - test('should return null when list is empty', () { - final messages = []; - final userRead = Read( - lastRead: DateTime.now(), - user: User(id: 'user1'), - ); - expect(messages.lastUnreadMessage(userRead), isNull); - }); - - test('should return null when userRead is null', () { - final messages = [ - Message(id: '1'), - Message(id: '2'), - ]; - expect(messages.lastUnreadMessage(null), isNull); - }); - - test('should return null when all messages are read', () { - final lastRead = DateTime.now(); - final messages = [ - Message( - id: '1', - createdAt: lastRead.subtract(const Duration(seconds: 1))), - Message(id: '2', createdAt: lastRead), - ]; - final userRead = Read( - lastRead: lastRead, - user: User(id: 'user1'), - ); - expect(messages.lastUnreadMessage(userRead), isNull); - }); - - test('should return null when all messages are mine', () { - final lastRead = DateTime.now(); - final userRead = Read( - lastRead: lastRead, - user: User(id: 'user1'), - ); - final messages = [ - Message( - id: '1', - user: userRead.user, - createdAt: lastRead.add(const Duration(seconds: 1))), - Message(id: '2', user: userRead.user, createdAt: lastRead), - ]; - expect(messages.lastUnreadMessage(userRead), isNull); - }); - - test('should return the message', () { - final lastRead = DateTime.now(); - final otherUser = User(id: 'user2'); - final userRead = Read( - lastRead: lastRead, - user: User(id: 'user1'), - ); - - final messages = [ - Message( - id: '1', - user: otherUser, - createdAt: lastRead.add(const Duration(seconds: 2)), - ), - Message( - id: '2', - user: otherUser, - createdAt: lastRead.add(const Duration(seconds: 1)), - ), - Message( - id: '3', - user: otherUser, - createdAt: lastRead.subtract(const Duration(seconds: 1)), - ), - ]; - - final lastUnreadMessage = messages.lastUnreadMessage(userRead); - expect(lastUnreadMessage, isNotNull); - expect(lastUnreadMessage!.id, '2'); - }); - - test('should not return the last message read', () { - final lastRead = DateTime.timestamp(); - final otherUser = User(id: 'user2'); - final userRead = Read( - lastRead: lastRead, - user: User(id: 'user1'), - lastReadMessageId: '3', - ); - - final messages = [ - Message( - id: '1', - user: otherUser, - createdAt: lastRead.add(const Duration(seconds: 2)), - ), - Message( - id: '2', - user: otherUser, - createdAt: lastRead.add(const Duration(milliseconds: 1)), - ), - Message( - id: '3', - user: otherUser, - createdAt: lastRead.add(const Duration(microseconds: 1)), - ), - Message( - id: '4', - user: otherUser, - createdAt: lastRead.subtract(const Duration(seconds: 1)), - ), - ]; - - final lastUnreadMessage = messages.lastUnreadMessage(userRead); - expect(lastUnreadMessage, isNotNull); - expect(lastUnreadMessage!.id, '2'); - }); - }); - }); -} diff --git a/packages/stream_chat_flutter/test/src/utils/finders.dart b/packages/stream_chat_flutter/test/src/utils/finders.dart deleted file mode 100644 index c03ae7b712..0000000000 --- a/packages/stream_chat_flutter/test/src/utils/finders.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:svg_icon_widget/svg_icon_widget.dart'; - -extension SvgIconWidgetFinder on CommonFinders { - /// Asserts that the SvgIcon widget is found. - /// - /// ## Sample code - /// - /// ```dart - /// expect(find.bySvgIcon(StreamSvgIcons.mic), findsOneWidget); - /// ``` - Finder bySvgIcon(SvgIconData icon) => _SvgIconWidgetFinder(icon); -} - -class _SvgIconWidgetFinder extends MatchFinder { - _SvgIconWidgetFinder(this.icon); - - final SvgIconData icon; - - @override - String get description => 'SvgIcon "$icon"'; - - @override - bool matches(Element candidate) { - final widget = candidate.widget; - return widget is SvgIcon && widget.icon == icon; - } -} diff --git a/packages/stream_chat_flutter/test/src/widget_tester_extension.dart b/packages/stream_chat_flutter/test/src/widget_tester_extension.dart deleted file mode 100644 index 4f557bdbb8..0000000000 --- a/packages/stream_chat_flutter/test/src/widget_tester_extension.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -extension WidgetTesterExtension on WidgetTester { - Future pumpWidgetWithSize( - Widget widget, { - required Size size, - double textScaleSize = 1.0, - }) async { - await binding.setSurfaceSize(size); - view - ..physicalSize = size - ..devicePixelRatio = 1.0; - platformDispatcher.textScaleFactorTestValue = textScaleSize; - - await pumpWidget(widget); - await pump(); - } -} diff --git a/packages/stream_chat_flutter/test/test_utils/data_generator.dart b/packages/stream_chat_flutter/test/test_utils/data_generator.dart deleted file mode 100644 index 5759136ba6..0000000000 --- a/packages/stream_chat_flutter/test/test_utils/data_generator.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:faker_dart/faker_dart.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -List generateConversation( - int count, { - List? users, - int? noOfUsers, - int unreadCount = 0, -}) { - assert( - users == null || noOfUsers == null, - 'Only one of users or noOfUsers ' - 'should be provided'); - assert(count > 0, 'Count should be greater than 0'); - assert(count > unreadCount, 'Count should be greater than unreadCount'); - - users ??= generateUsers(noOfUsers!); - - final faker = Faker.instance; - - final messages = []; - for (var i = 0; i < count - unreadCount; i++) { - final user = users[i % users.length]; - messages.add( - Message( - id: faker.datatype.uuid(), - text: faker.lorem.sentence(), - user: user, - createdAt: DateTime.now().subtract(Duration(minutes: i)), - ), - ); - } - - for (var i = 0; i < unreadCount; i++) { - final user = users.where((element) => element is! OwnUser).first; - messages.add( - Message( - id: faker.datatype.uuid(), - text: faker.lorem.sentence(), - user: user, - createdAt: - DateTime.now().subtract(Duration(minutes: i + count - unreadCount)), - ), - ); - } - - return messages; -} - -List generateUsers(int count) { - final faker = Faker.instance; - final users = []; - for (var i = 0; i < count; i++) { - users.add( - User( - id: faker.datatype.uuid(), - name: faker.name.fullName(), - ), - ); - } - - return users; -} diff --git a/packages/stream_chat_flutter_core/.metadata b/packages/stream_chat_flutter_core/.metadata deleted file mode 100644 index 5eb5034781..0000000000 --- a/packages/stream_chat_flutter_core/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: 78910062997c3a836feee883712c241a5fd22983 - channel: stable - -project_type: package diff --git a/packages/stream_chat_flutter_core/CHANGELOG.md b/packages/stream_chat_flutter_core/CHANGELOG.md deleted file mode 100644 index e8fa547b10..0000000000 --- a/packages/stream_chat_flutter_core/CHANGELOG.md +++ /dev/null @@ -1,516 +0,0 @@ -## 9.4.0 - -- Updated minimum Flutter version to 3.27.4 for the SDK. - -## 9.3.0 - -- Updated `stream_chat` dependency to [`9.3.0`](https://pub.dev/packages/stream_chat/changelog). - -## 9.2.0 - -- Updated `stream_chat` dependency to [`9.2.0`](https://pub.dev/packages/stream_chat/changelog). - -## 9.1.0 - -āœ… Added - -- Added `StreamThreadListController` to load and paginate list of threads. - -## 9.0.0 - -āœ… Added - -- Added `StreamPollController` to create and manage a poll based on the passed configs. -- Added `StreamPollVoteListController` to manage the list of votes for a poll. - -šŸ”„ Changed - -- Updated minimum Flutter version to 3.24.5 for the SDK. - -## 8.3.0 - -- Updated `stream_chat` dependency to [`8.3.0`](https://pub.dev/packages/stream_chat/changelog). - -## 8.2.0 - -- Updated `stream_chat` dependency to [`8.2.0`](https://pub.dev/packages/stream_chat/changelog). - -## 8.1.0 - -šŸ”„ Changed - -- Changed minimum Flutter version to 3.22 for the SDK. -- Updated `stream_chat` dependency to [`8.1.0`](https://pub.dev/packages/stream_chat/changelog). - -## 8.0.0 - -šŸž Fixed - -- Fixed bug causing background events to be sent in foreground. - -šŸ”„ Changed - -- Updated `stream_chat` dependency to [`8.0.0`](https://pub.dev/packages/stream_chat/changelog). - -## 7.3.0 - -šŸ”„ Changed - -- Changed minimum Flutter version to 3.19 for the SDK. -- Updated `stream_chat` dependency to [`7.3.0`](https://pub.dev/packages/stream_chat/changelog). - -## 7.2.2 - -- Updated `stream_chat` dependency to [`7.2.2`](https://pub.dev/packages/stream_chat/changelog). - -## 7.2.1 - - - Updated `stream_chat` dependency to [`7.2.1`](https://pub.dev/packages/stream_chat/changelog). - -## 7.2.0-hotfix.1 - - - Updated `stream_chat` dependency to [`7.2.0-hotfix.1`](https://pub.dev/packages/stream_chat/changelog). - - Reverted the `connectivity_plus` dependency bump causing [1889](https://github.com/GetStream/stream-chat-flutter/issues/1889) - -## 7.2.0 - -- Updated `stream_chat` dependency to [`7.2.0`](https://pub.dev/packages/stream_chat/changelog). - -## 7.1.0 - -- Updated `stream_chat` dependency to [`7.1.0`](https://pub.dev/packages/stream_chat/changelog). - -## 7.0.2 - -- Updated `stream_chat` dependency to [`7.0.2`](https://pub.dev/packages/stream_chat/changelog). - -## 7.0.1 - -- Updated `stream_chat` dependency to [`7.0.1`](https://pub.dev/packages/stream_chat/changelog). - -## 7.0.0 - -- šŸ›‘ **BREAKING** Removed deprecated `StreamChannelListController.sort` parameter. - Use `StreamChannelListController.channelStateSort` instead. -- Updated minimum supported `SDK` version to Flutter 3.13/Dart 3.1 -- Updated `stream_chat` dependency to [`7.0.0`](https://pub.dev/packages/stream_chat/changelog). - -## 6.11.0 - -šŸž Fixed - -- Fixed video attachment uploading. [#1754](https://github.com/GetStream/stream-chat-flutter/pull/1754) - -## 6.10.0 - -- Added mixin support to `StreamChannelListEventHandler`. - -## 6.9.0 - -- Added support for `StreamChannel.loadingBuilder` and `StreamChannel.errorBuilder` to customize - loading and error states. - -## 6.8.0 - -- Updated minimum supported `SDK` version to Flutter 3.10/Dart 3.0 -- Updated `stream_chat` dependency to [`6.8.0`](https://pub.dev/packages/stream_chat/changelog). - -## 6.7.0 - -- Updated `stream_chat` dependency to [`6.7.0`](https://pub.dev/packages/stream_chat/changelog). - -## 6.6.0 - -- Updated `stream_chat` dependency to [`6.6.0`](https://pub.dev/packages/stream_chat/changelog). - -## 6.5.0 - -- Updated minimum supported `SDK` version to Flutter 3.7/Dart 2.19 -- Updated `stream_chat` dependency to [`6.5.0`](https://pub.dev/packages/stream_chat/changelog). - -## 6.4.0 - -- Updated `stream_chat` dependency to [`6.4.0`](https://pub.dev/packages/stream_chat/changelog). - -## 6.3.0 - -- Updated `stream_chat` dependency to [`6.3.0`](https://pub.dev/packages/stream_chat/changelog). - -## 6.2.0 - -- Fixed `StreamMessageInputController.textPatternStyle` not matching case-insensitive patterns. -- Updated `connectivity_plus` dependency to `^4.0.0` -- Fixed `StreamChannel` shows black screen while loading in some cases. -- Updated `stream_chat` dependency to [`6.2.0`](https://pub.dev/packages/stream_chat/changelog). - -## 6.1.0 - -- Updated `dart` sdk environment range to support `3.0.0`. -- Updated `stream_chat` dependency to [`6.1.0`](https://pub.dev/packages/stream_chat/changelog). -- [[#1356]](https://github.com/GetStream/stream-chat-flutter/issues/1356) Channel doesn't auto - display again after being - hidden. -- [[#1540]](https://github.com/GetStream/stream-chat-flutter/issues/1540) - Use `CircularProgressIndicator.adaptive` - instead of material indicator. - -## 6.0.0 - -- Updated dependencies to resolvable versions. - -## 5.3.0 - -- Updated `stream_chat` dependency to [`5.3.0`](https://pub.dev/packages/stream_chat/changelog). - -## 5.2.0 - -šŸ”„ Changed - -- Updated `connectivity_plus` dependency to `^3.0.2` - -## 5.1.0 - -- Deprecated the `sort` parameter in the `StreamChannelListController` in favor - of `channelStateSort`. - -## 5.0.0 - -- Included the changes from version [4.5.0](#450). - -āœ… Added - -- Added `StreamMemberListController`. - -## 5.0.0-beta.2 - -- Included the changes from version [4.4.0](#440) and [4.4.1](#441). - -## 5.0.0-beta.1 - -- Updated `stream_chat` dependency - to [`5.0.0-beta.1`](https://pub.dev/packages/stream_chat/changelog). -- Removed deprecated code. - -## 4.6.0 - -- Updated `stream_chat` dependency to [`4.6.0`](https://pub.dev/packages/stream_chat/changelog). - -## 4.5.0 - -- Updated `stream_chat` dependency to [`4.5.0`](https://pub.dev/packages/stream_chat/changelog). -- [#1269](https://github.com/GetStream/stream-chat-flutter/issues/1269) - Fix `ChannelListEventHandler` castError at PagedValue.asSuccess. -- [#1241](https://github.com/GetStream/stream-chat-flutter/issues/1241) StreamChannelListView load - more indicator non stop. - -## 4.4.1 - -- Updated `stream_chat` dependency to [`4.4.1`](https://pub.dev/packages/stream_chat/changelog). - -## 4.4.0 - -- Updated `stream_chat` dependency to [`4.4.0`](https://pub.dev/packages/stream_chat/changelog). - -## 4.3.0 - -- Updated `stream_chat` dependency to [`4.3.0`](https://pub.dev/packages/stream_chat/changelog). - -## 4.2.0 - -- Updated `stream_chat` dependency to [`4.2.0`](https://pub.dev/packages/stream_chat/changelog). - -šŸ”„ Changed - -- Deprecated `before` and `after` parameters in `StreamChannel.queryAroundMessage`. Use `limit` - instead. -- Deprecated `before` and `after` parameters in `StreamChannel.loadChannelAtMessage`. Use `limit` - instead. - -## 4.1.0 - -- Updated `stream_chat` dependency to [`4.1.0`](https://pub.dev/packages/stream_chat/changelog). - -## 4.0.1 - -- Minor fixes -- Updated `stream_chat` dependency to [`4.0.1`](https://pub.dev/packages/stream_chat/changelog). - -## 4.0.0 - -For upgrading to V4, please refer to -the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migration_guide_4_0/) - -- Deprecated `UsersBloc` in favor of `StreamUserListController` to control the user list. -- Deprecated `MessageSearchBloc` in favor of `StreamMessageSearchListController` to control the user - list. - -## 4.0.0-beta.2 - -- Updated `stream_chat` dependency - to [`4.0.0-beta.2`](https://pub.dev/packages/stream_chat/changelog). - -## 4.0.0-beta.0 - -āœ… Added - -- Added `MessageInputController` to hold `Message` related data. -- Deprecated old widgets in favor of Stream-prefixed ones. -- Deprecated `ChannelsBloc` in favor of `StreamChannelListController` to control the channel list. -- Added `MessageTextFieldController` to be used with the new `StreamTextField` ui widget. - -- Updated `stream_chat` dependency - to [`4.0.0-beta.0`](https://pub.dev/packages/stream_chat/changelog). - -## 3.6.1 - -- Updated `stream_chat` dependency to [`3.6.1`](https://pub.dev/packages/stream_chat/changelog). - -## 3.6.0 - -- Updated `stream_chat` dependency to [`3.6.0`](https://pub.dev/packages/stream_chat/changelog). - -## 3.5.1 - -- Updated `stream_chat` dependency to [`3.5.1`](https://pub.dev/packages/stream_chat/changelog). - -## 3.5.0 - -- Updated `stream_chat` dependency to [`3.5.0`](https://pub.dev/packages/stream_chat/changelog). - -## 3.4.0 - -- Updated `stream_chat` dependency to [`3.4.0`](https://pub.dev/packages/stream_chat/changelog). - -šŸž Fixed - -- Do not move a channel to top if the new message is from a thread. -- [[#848]](https://github.com/GetStream/stream-chat-flutter/issues/848) Fixed "Bad state: Cannot add - new events after calling close" by replacing all `.add` methods with a new `.safeAdd`. - -## 3.3.1 - -- Updated `stream_chat` dependency to [`3.3.1`](https://pub.dev/packages/stream_chat/changelog). - -## 3.3.0 - -- Updated `stream_chat` dependency to [`3.3.0`](https://pub.dev/packages/stream_chat/changelog). - -## 3.2.0 - -- Updated `stream_chat` dependency to [`3.2.0`](https://pub.dev/packages/stream_chat/changelog). - -## 3.1.1 - -- Updated `stream_chat` dependency to [`3.1.1`](https://pub.dev/packages/stream_chat/changelog). - -## 3.0.0 - -- Updated `stream_chat` dependency to [`3.0.0`](https://pub.dev/packages/stream_chat/changelog). - -šŸ›‘ļø Breaking Changes from `2.2.1` - -- `MessageSearchListViewCore` `paginationParams` property is now deprecated in favor of `limit`. - ```dart - // previous - paginationParams = const PaginationParams(limit: 30) - - // new - limit = 30 - ``` -- `UserListCore` `pagination` property is now deprecated in favor of `limit`. - ```dart - // previous - pagination = const PaginationParams(limit: 30) - - // new - limit = 30 - ``` -- `ChannelListCore` `pagination` property is now deprecated in favor of `limit`. - ```dart - // previous - pagination = const PaginationParams(limit: 30) - - // new - limit = 30 - ``` - -- `UserListCore` `filter` property now is non-nullable. - -šŸ”„ Changed - -- `UserListCore` filter property now has a default value. - ```dart - filter = const Filter.empty() - ``` - -šŸž Fixed - -- Fixed `MessageSearchBloc` pagination. -- [[#673]](https://github.com/GetStream/stream-chat-flutter/issues/673): Fix `Core Widgets` not - getting rebuild with new data on configuration change. - -## 2.2.1 - -- Updated `stream_chat` dependency to 2.2.1 - -## 2.2.0 - -šŸ›‘ļø Breaking Changes from `2.1.1` - -- Renamed `BetterStreamBuilder.loadingBuilder` to `.noDataBuilder` - -šŸ”„ Changed - -- `BetterStreamBuilder.initialData` is now nullable/not-required. - -šŸž Fixed - -- [#612](https://github.com/GetStream/stream-chat-flutter/issues/612) `ChannelListView` pagination - doesn't work after refresh - -## 2.1.1 - -- Updated llc dependency - -## 2.1.0 - -šŸ›‘ļø Breaking Changes from `2.0.0` - -- Changed default message filter of `MessageListCore` - -āœ… Added - -- Added `MessageListCore.paginationLimit` - -šŸ”„ Changed - -- `StreamChatCore.of(context).user` is now deprecated in favor - of `StreamChatCore.of(context).currentUser`. -- `StreamChatCore.of(context).userStream` is now deprecated in favor - of `StreamChatCore.of(context).currentUserStream`. - -## 2.0.0 - -šŸ›‘ļø Breaking Changes from `1.5.3` - -- migrate this package to null safety -- `channelsBloc.queryChannels()`, `ChannelListCore` options param/property is removed in favor of - individual params/properties - - `options.state` -> bool state - - `options.watch` -> bool watch - - `options.presence` -> bool presence -- `usersBloc.queryUsers()`, `UserListCore` options param/property is removed in favor of individual - params/properties - - `options.presence` -> bool presence - -āœ… Added - -- Monitor connection using `connectivity_plus` package - -šŸž Fixed - -- Minor fixes -- Performance improvements - -## 2.0.0-nullsafety.9 - -- Update llc dependency - -## 2.0.0-nullsafety.8 - -šŸ›‘ļø Breaking Changes from `2.0.0-nullsafety.7` - -- `channelsBloc.queryChannels()`, `ChannelListCore` options param/property is removed in favor of - individual params/properties - - `options.state` -> bool state - - `options.watch` -> bool watch - - `options.presence` -> bool presence -- `usersBloc.queryUsers()`, `UserListCore` options param/property is removed in favor of individual - params/properties - - `options.presence` -> bool presence - -## 2.0.0-nullsafety.7 - -* Fixed a bug with connectivity implementation - -## 2.0.0-nullsafety.6 - -* Update llc dependency -* Minor fixes and improvements - -## 2.0.0-nullsafety.5 - -* Update llc dependency -* Minor fixes and improvements -* Performance improvements -* Monitor connection using `connectivity_plus` package - -## 2.0.0-nullsafety.3 - -* Update llc dependency -* Minor fixes and improvements - -## 2.0.0-nullsafety.2 - -* Fix ChannelsBloc not performing calls if pagination ended - -## 2.0.0-nullsafety.1 - -* Migrate this package to null safety -* Update llc dependency - -## 1.5.3 - -* Fix ChannelsBloc not performing calls if pagination ended - -## 1.5.2 - -* Update llc dependency - -## 1.5.1 - -* Improved test coverage to > 90% -* Minor fixes and improvements - -## 1.5.0 - -* Minor fixes and improvements - -## 1.4.0-beta - -* Added `MessageListCore.messageFilter` to filter messages locally -* Minor fixes and improvements - -## 1.3.2-beta - -* Update llc dependency - -## 1.3.1-beta - -* Update llc dependency - -## 1.3.0-beta - -* Update llc dependency -* Minor fixes - -## 1.2.0-beta - -* Update llc dependency -* Minor fixes - -## 1.1.0-beta - -* Update llc dependency - -## 1.0.2-beta - -* Update llc dependency - -## 1.0.1-beta - -* Update llc dependency - -## 1.0.0-beta - -* First release diff --git a/packages/stream_chat_flutter_core/LICENSE b/packages/stream_chat_flutter_core/LICENSE deleted file mode 100644 index 49088d477f..0000000000 --- a/packages/stream_chat_flutter_core/LICENSE +++ /dev/null @@ -1,219 +0,0 @@ -SOURCE CODE LICENSE AGREEMENT - -IMPORTANT - READ THIS CAREFULLY BEFORE DOWNLOADING, INSTALLING, USING OR -ELECTRONICALLY ACCESSING THIS PROPRIETARY PRODUCT. - -THIS IS A LEGAL AGREEMENT BETWEEN STREAM.IO, INC. (ā€œSTREAM.IOā€) AND THE -BUSINESS ENTITY OR PERSON FOR WHOM YOU (ā€œYOUā€) ARE ACTING (ā€œCUSTOMERā€) AS THE -LICENSEE OF THE PROPRIETARY SOFTWARE INTO WHICH THIS AGREEMENT HAS BEEN -INCLUDED (THE ā€œAGREEMENTā€). YOU AGREE THAT YOU ARE THE CUSTOMER, OR YOU ARE AN -EMPLOYEE OR AGENT OF CUSTOMER AND ARE ENTERING INTO THIS AGREEMENT FOR LICENSE -OF THE SOFTWARE BY CUSTOMER FOR CUSTOMER’S BUSINESS PURPOSES AS DESCRIBED IN -AND IN ACCORDANCE WITH THIS AGREEMENT. YOU HEREBY AGREE THAT YOU ENTER INTO -THIS AGREEMENT ON BEHALF OF CUSTOMER AND THAT YOU HAVE THE AUTHORITY TO BIND -CUSTOMER TO THIS AGREEMENT. - -STREAM.IO IS WILLING TO LICENSE THE SOFTWARE TO CUSTOMER ONLY ON THE FOLLOWING -CONDITIONS: (1) YOU ARE A CURRENT CUSTOMER OF STREAM.IO; (2) YOU ARE NOT A -COMPETITOR OF STREAM.IO; AND (3) THAT YOU ACCEPT ALL THE TERMS IN THIS -AGREEMENT. BY DOWNLOADING, INSTALLING, CONFIGURING, ACCESSING OR OTHERWISE -USING THE SOFTWARE, INCLUDING ANY UPDATES, UPGRADES, OR NEWER VERSIONS, YOU -REPRESENT, WARRANT AND ACKNOWLEDGE THAT (A) CUSTOMER IS A CURRENT CUSTOMER OF -STREAM.IO; (B) CUSTOMER IS NOT A COMPETITOR OF STREAM.IO; AND THAT (C) YOU HAVE -READ THIS AGREEMENT, UNDERSTAND THIS AGREEMENT, AND THAT CUSTOMER AGREES TO BE -BOUND BY ALL THE TERMS OF THIS AGREEMENT. - -IF YOU DO NOT AGREE TO ALL THE TERMS AND CONDITIONS OF THIS AGREEMENT, -STREAM.IO IS UNWILLING TO LICENSE THE SOFTWARE TO CUSTOMER, AND THEREFORE, DO -NOT COMPLETE THE DOWNLOAD PROCESS, ACCESS OR OTHERWISE USE THE SOFTWARE, AND -CUSTOMER SHOULD IMMEDIATELY RETURN THE SOFTWARE AND CEASE ANY USE OF THE -SOFTWARE. - -1. SOFTWARE. The Stream.io software accompanying this Agreement, may include -Source Code, Executable Object Code, associated media, printed materials and -documentation (collectively, the ā€œSoftwareā€). The Software also includes any -updates or upgrades to or new versions of the original Software, if and when -made available to you by Stream.io. ā€œSource Codeā€ means computer programming -code in human readable form that is not suitable for machine execution without -the intervening steps of interpretation or compilation. ā€œExecutable Object -Code" means the computer programming code in any other form than Source Code -that is not readily perceivable by humans and suitable for machine execution -without the intervening steps of interpretation or compilation. ā€œSiteā€ means a -Customer location controlled by Customer. ā€œAuthorized Userā€ means any employee -or contractor of Customer working at the Site, who has signed a written -confidentiality agreement with Customer or is otherwise bound in writing by -confidentiality and use obligations at least as restrictive as those imposed -under this Agreement. - -2. LICENSE GRANT. Subject to the terms and conditions of this Agreement, in -consideration for the representations, warranties, and covenants made by -Customer in this Agreement, Stream.io grants to Customer, during the term of -this Agreement, a personal, non-exclusive, non-transferable, non-sublicensable -license to: - -a. install and use Software Source Code on password protected computers at a Site, -restricted to Authorized Users; - -b. create derivative works, improvements (whether or not patentable), extensions -and other modifications to the Software Source Code (ā€œModificationsā€) to build -unique scalable newsfeeds, activity streams, and in-app messaging via Stream’s -application program interface (ā€œAPIā€); - -c. compile the Software Source Code to create Executable Object Code versions of -the Software Source Code and Modifications to build such newsfeeds, activity -streams, and in-app messaging via the API; - -d. install, execute and use such Executable Object Code versions solely for -Customer’s internal business use (including development of websites through -which data generated by Stream services will be streamed (ā€œAppsā€)); - -e. use and distribute such Executable Object Code as part of Customer’s Apps; and - -f. make electronic copies of the Software and Modifications as required for backup -or archival purposes. - -3. RESTRICTIONS. Customer is responsible for all activities that occur in -connection with the Software. Customer will not, and will not attempt to: (a) -sublicense or transfer the Software or any Source Code related to the Software -or any of Customer’s rights under this Agreement, except as otherwise provided -in this Agreement, (b) use the Software Source Code for the benefit of a third -party or to operate a service; (c) allow any third party to access or use the -Software Source Code; (d) sublicense or distribute the Software Source Code or -any Modifications in Source Code or other derivative works based on any part of -the Software Source Code; (e) use the Software in any manner that competes with -Stream.io or its business; or (e) otherwise use the Software in any manner that -exceeds the scope of use permitted in this Agreement. Customer shall use the -Software in compliance with any accompanying documentation any laws applicable -to Customer. - -4. OPEN SOURCE. Customer and its Authorized Users shall not use any software or -software components that are open source in conjunction with the Software -Source Code or any Modifications in Source Code or in any way that could -subject the Software to any open source licenses. - -5. CONTRACTORS. Under the rights granted to Customer under this Agreement, -Customer may permit its employees, contractors, and agencies of Customer to -become Authorized Users to exercise the rights to the Software granted to -Customer in accordance with this Agreement solely on behalf of Customer to -provide services to Customer; provided that Customer shall be liable for the -acts and omissions of all Authorized Users to the extent any of such acts or -omissions, if performed by Customer, would constitute a breach of, or otherwise -give rise to liability to Customer under, this Agreement. Customer shall not -and shall not permit any Authorized User to use the Software except as -expressly permitted in this Agreement. - -6. COMPETITIVE PRODUCT DEVELOPMENT. Customer shall not use the Software in any way -to engage in the development of products or services which could be reasonably -construed to provide a complete or partial functional or commercial alternative -to Stream.io’s products or services (a ā€œCompetitive Productā€). Customer shall -ensure that there is no direct or indirect use of, or sharing of, Software -source code, or other information based upon or derived from the Software to -develop such products or services. Without derogating from the generality of -the foregoing, development of Competitive Products shall include having direct -or indirect access to, supervising, consulting or assisting in the development -of, or producing any specifications, documentation, object code or source code -for, all or part of a Competitive Product. - -7. LIMITATION ON MODIFICATIONS. Notwithstanding any provision in this Agreement, -Modifications may only be created and used by Customer as permitted by this -Agreement and Modification Source Code may not be distributed to third parties. -Customer will not assert against Stream.io, its affiliates, or their customers, -direct or indirect, agents and contractors, in any way, any patent rights that -Customer may obtain relating to any Modifications for Stream.io, its -affiliates’, or their customers’, direct or indirect, agents’ and contractors’ -manufacture, use, import, offer for sale or sale of any Stream.io products or -services. - -8. DELIVERY AND ACCEPTANCE. The Software will be delivered electronically pursuant -to Stream.io standard download procedures. The Software is deemed accepted upon -delivery. - -9. IMPLEMENTATION AND SUPPORT. Stream.io has no obligation under this Agreement to -provide any support or consultation concerning the Software. - -10. TERM AND TERMINATION. The term of this Agreement begins when the Software is -downloaded or accessed and shall continue until terminated. Either party may -terminate this Agreement upon written notice. This Agreement shall -automatically terminate if Customer is or becomes a competitor of Stream.io or -makes or sells any Competitive Products. Upon termination of this Agreement for -any reason, (a) all rights granted to Customer in this Agreement immediately -cease to exist, (b) Customer must promptly discontinue all use of the Software -and return to Stream.io or destroy all copies of the Software in Customer’s -possession or control. Any continued use of the Software by Customer or attempt -by Customer to exercise any rights under this Agreement after this Agreement -has terminated shall be considered copyright infringement and subject Customer -to applicable remedies for copyright infringement. Sections 2, 5, 6, 8 and 9 -shall survive expiration or termination of this Agreement for any reason. - -11. OWNERSHIP. As between the parties, the Software and all worldwide intellectual -property rights and proprietary rights relating thereto or embodied therein, -are the exclusive property of Stream.io and its suppliers. Stream.io and its -suppliers reserve all rights in and to the Software not expressly granted to -Customer in this Agreement, and no other licenses or rights are granted by -implication, estoppel or otherwise. - -12. WARRANTY DISCLAIMER. USE OF THIS SOFTWARE IS ENTIRELY AT YOURS AND CUSTOMER’S -OWN RISK. THE SOFTWARE IS PROVIDED ā€œAS ISā€ WITHOUT ANY WARRANTY OF ANY KIND -WHATSOEVER. STREAM.IO DOES NOT MAKE, AND HEREBY DISCLAIMS, ANY WARRANTY OF ANY -KIND, WHETHER EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING WITHOUT -LIMITATION, THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -PURPOSE, TITLE, NON-INFRINGEMENT OF THIRD-PARTY RIGHTS, RESULTS, EFFORTS, -QUALITY OR QUIET ENJOYMENT. STREAM.IO DOES NOT WARRANT THAT THE SOFTWARE IS -ERROR-FREE, WILL FUNCTION WITHOUT INTERRUPTION, WILL MEET ANY SPECIFIC NEED -THAT CUSTOMER HAS, THAT ALL DEFECTS WILL BE CORRECTED OR THAT IT IS -SUFFICIENTLY DOCUMENTED TO BE USABLE BY CUSTOMER. TO THE EXTENT THAT STREAM.IO -MAY NOT DISCLAIM ANY WARRANTY AS A MATTER OF APPLICABLE LAW, THE SCOPE AND -DURATION OF SUCH WARRANTY WILL BE THE MINIMUM PERMITTED UNDER SUCH LAW. -CUSTOMER ACKNOWLEDGES THAT IT HAS RELIED ON NO WARRANTIES OTHER THAN THE -EXPRESS WARRANTIES IN THIS AGREEMENT. - -13. LIMITATION OF LIABILITY. TO THE FULLEST EXTENT PERMISSIBLE BY LAW, STREAM.IO’S -TOTAL LIABILITY FOR ALL DAMAGES ARISING OUT OF OR RELATED TO THE SOFTWARE OR -THIS AGREEMENT, WHETHER IN CONTRACT, TORT (INCLUDING NEGLIGENCE) OR OTHERWISE, -SHALL NOT EXCEED $100. IN NO EVENT WILL STREAM.IO BE LIABLE FOR ANY INDIRECT, -CONSEQUENTIAL, EXEMPLARY, PUNITIVE, SPECIAL OR INCIDENTAL DAMAGES OF ANY KIND -WHATSOEVER, INCLUDING ANY LOST DATA AND LOST PROFITS, ARISING FROM OR RELATING -TO THE SOFTWARE EVEN IF STREAM.IO HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. CUSTOMER ACKNOWLEDGES THAT THIS PROVISION REFLECTS THE AGREED UPON -ALLOCATION OF RISK FOR THIS AGREEMENT AND THAT STREAM.IO WOULD NOT ENTER INTO -THIS AGREEMENT WITHOUT THESE LIMITATIONS ON ITS LIABILITY. - -14. General. Customer may not assign or transfer this Agreement, by operation of -law or otherwise, or any of its rights under this Agreement (including the -license rights granted to Customer) to any third party without Stream.io’s -prior written consent, which consent will not be unreasonably withheld or -delayed. Stream.io may assign this Agreement, without consent, including, but -limited to, affiliate or any successor to all or substantially all its business -or assets to which this Agreement relates, whether by merger, sale of assets, -sale of stock, reorganization or otherwise. Any attempted assignment or -transfer in violation of the foregoing will be null and void. Stream.io shall -not be liable hereunder by reason of any failure or delay in the performance of -its obligations hereunder for any cause which is beyond the reasonable control. -All notices, consents, and approvals under this Agreement must be delivered in -writing by courier, by electronic mail, or by certified or registered mail, -(postage prepaid and return receipt requested) to the other party at the -address set forth in the customer agreement between Stream.io and Customer and -will be effective upon receipt or when delivery is refused. This Agreement will -be governed by and interpreted in accordance with the laws of the State of -Colorado, without reference to its choice of laws rules. The United Nations -Convention on Contracts for the International Sale of Goods does not apply to -this Agreement. Any action or proceeding arising from or relating to this -Agreement shall be brought in a federal or state court in Denver, Colorado, and -each party irrevocably submits to the jurisdiction and venue of any such court -in any such action or proceeding. All waivers must be in writing. Any waiver or -failure to enforce any provision of this Agreement on one occasion will not be -deemed a waiver of any other provision or of such provision on any other -occasion. If any provision of this Agreement is unenforceable, such provision -will be changed and interpreted to accomplish the objectives of such provision -to the greatest extent possible under applicable law and the remaining -provisions will continue in full force and effect. Customer shall not violate -any applicable law, rule or regulation, including those regarding the export of -technical data. The headings of Sections of this Agreement are for convenience -and are not to be used in interpreting this Agreement. As used in this -Agreement, the word ā€œincludingā€ means ā€œincluding but not limited to.ā€ This -Agreement (including all exhibits and attachments) constitutes the entire -agreement between the parties regarding the subject hereof and supersedes all -prior or contemporaneous agreements, understandings and communication, whether -written or oral. This Agreement may be amended only by a written document -signed by both parties. The terms of any purchase order or similar document -submitted by Customer to Stream.io will have no effect. diff --git a/packages/stream_chat_flutter_core/README.md b/packages/stream_chat_flutter_core/README.md deleted file mode 100644 index 695cd9cdf0..0000000000 --- a/packages/stream_chat_flutter_core/README.md +++ /dev/null @@ -1,88 +0,0 @@ -# Official Core [Flutter SDK](https://getstream.io/chat/sdk/flutter/) for [Stream Chat](https://getstream.io/chat/) - -> The official Flutter core components for Stream Chat, a service for -> building chat applications. - -[![Pub](https://img.shields.io/pub/v/stream_chat_flutter_core.svg)](https://pub.dartlang.org/packages/stream_chat_flutter_core) -![](https://img.shields.io/badge/platform-flutter%20%7C%20flutter%20web-ff69b4.svg?style=flat-square) -![CI](https://github.com/GetStream/stream-chat-flutter/workflows/stream_flutter_workflow/badge.svg?branch=master) - -**Quick Links** - -- [Register](https://getstream.io/chat/trial/) to get an API key for Stream Chat -- [Flutter Chat Tutorial](https://getstream.io/chat/flutter/tutorial/) -- [Chat UI Kit](https://getstream.io/chat/ui-kit/) -- [Core Docs](https://getstream.io/chat/docs/sdk/flutter/stream_chat_flutter_core/introduction/) - -**V4 Migration Guide** - -For upgrading from V3 to V4, please refer to the [V4 Migration Guide](https://getstream.io/chat/docs/sdk/flutter/guides/migration_guide_4_0/) - -### Changelog - -Check out the [changelog on pub.dev](https://pub.dev/packages/stream_chat_flutter_core/changelog) to see the latest changes in the package. - -## Flutter Chat Tutorial - -The best place to start is the [Flutter Chat Tutorial](https://getstream.io/chat/flutter/tutorial/). -It teaches you how to use this SDK and also shows how to make frequently required changes. - -## Example App - -This repo includes a fully functional example app with setup instructions. -The example is available under the [example](https://github.com/GetStream/stream-chat-flutter/tree/main/packages/stream_chat_flutter_core/example) folder. - -## Add dependency -Add this to your package's pubspec.yaml file, use the latest version [![Pub](https://img.shields.io/pub/v/stream_chat_flutter_core.svg)](https://pub.dartlang.org/packages/stream_chat_flutter_core) -```yaml -dependencies: - stream_chat_flutter_core: ^latest_version -``` - -You should then run `flutter packages get` - -This package requires no custom setup on any platform since it does not depend on any platform-specific dependency - -## Docs - -This package provides business logic to fetch common things required for integrating Stream Chat into your application. -The core package allows more customisation and hence provides business logic but no UI components. -Please use the `stream_chat_flutter` package for the full fledged suite of UI components or `stream_chat` for the low-level client. - -### Business Logic Components - -These components allow you to have the maximum and lower-level control of the queries being executed. -The BLoCs we provide are: - -1) ChannelsBloc -2) MessageSearchBloc -3) UsersBloc - -### Core Components - -In the early days of the Flutter SDK, the SDK was only split into the LLC (`stream_chat`) and -the UI package (`stream_chat_flutter`). With this you could use a fully built interface with the UI package -or a fully custom interface with the LLC. However, we soon recognised the need for a third intermediary -package which made tasks like building and modifying a list of channels or messages easy but without -the complexity of using low level components. The Core package (`stream_chat_flutter_core`) is a manifestation -of the same idea and allows you to build an interface with Stream Chat without having to deal with -low level code and architecture as well as implementing your own theme and UI effortlessly. -Also, it has very few dependencies. - -The package primarily contains a bunch of controller classes. -Controllers are used to handle the business logic of the chat. You can use them together with our UI widgets, or you can even use them to build your own UI. - -* StreamChannelListController -* StreamUserListController -* StreamMessageSearchListController -* StreamMessageInputController -* LazyLoadScrollView -* PagedValueListenableBuilder - -## Contributing - -We welcome code changes that improve this library or fix a problem, -please make sure to follow all best practices and add tests if applicable before submitting a Pull Request on Github. -We are pleased to merge your code into the official repository. -Make sure to sign our [Contributor License Agreement (CLA)](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) first. -See our license file for more details. diff --git a/packages/stream_chat_flutter_core/example/.gitignore b/packages/stream_chat_flutter_core/example/.gitignore deleted file mode 100644 index 9d532b18a0..0000000000 --- a/packages/stream_chat_flutter_core/example/.gitignore +++ /dev/null @@ -1,41 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -**/doc/api/ -**/ios/Flutter/.last_build_id -.dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies -.packages -.pub-cache/ -.pub/ -/build/ - -# Web related -lib/generated_plugin_registrant.dart - -# Symbolication related -app.*.symbols - -# Obfuscation related -app.*.map.json diff --git a/packages/stream_chat_flutter_core/example/.metadata b/packages/stream_chat_flutter_core/example/.metadata deleted file mode 100644 index 182cccafd9..0000000000 --- a/packages/stream_chat_flutter_core/example/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: 78910062997c3a836feee883712c241a5fd22983 - channel: stable - -project_type: app diff --git a/packages/stream_chat_flutter_core/example/README.md b/packages/stream_chat_flutter_core/example/README.md deleted file mode 100644 index fef640e199..0000000000 --- a/packages/stream_chat_flutter_core/example/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# example - -Example app for testing stream_chat_flutter_core - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) - -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/packages/stream_chat_flutter_core/example/analysis_options.yaml b/packages/stream_chat_flutter_core/example/analysis_options.yaml deleted file mode 100644 index d56da71f91..0000000000 --- a/packages/stream_chat_flutter_core/example/analysis_options.yaml +++ /dev/null @@ -1,28 +0,0 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. -# -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. - -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. - -linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at - # https://dart-lang.github.io/linter/lints/index.html. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. - rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/packages/stream_chat_flutter_core/example/android/.gitignore b/packages/stream_chat_flutter_core/example/android/.gitignore deleted file mode 100644 index 0a741cb43d..0000000000 --- a/packages/stream_chat_flutter_core/example/android/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -gradle-wrapper.jar -/.gradle -/captures/ -/gradlew -/gradlew.bat -/local.properties -GeneratedPluginRegistrant.java - -# Remember to never publicly share your keystore. -# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app -key.properties diff --git a/packages/stream_chat_flutter_core/example/android/app/build.gradle b/packages/stream_chat_flutter_core/example/android/app/build.gradle deleted file mode 100644 index 2e0efa5557..0000000000 --- a/packages/stream_chat_flutter_core/example/android/app/build.gradle +++ /dev/null @@ -1,54 +0,0 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -android { - compileSdkVersion 33 - - lintOptions { - disable 'InvalidPackage' - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "example.example" - minSdkVersion flutter.minSdkVersion - targetSdkVersion 33 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} diff --git a/packages/stream_chat_flutter_core/example/android/app/src/debug/AndroidManifest.xml b/packages/stream_chat_flutter_core/example/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index 397a94923f..0000000000 --- a/packages/stream_chat_flutter_core/example/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/packages/stream_chat_flutter_core/example/android/app/src/main/AndroidManifest.xml b/packages/stream_chat_flutter_core/example/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index 79969daa59..0000000000 --- a/packages/stream_chat_flutter_core/example/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/packages/stream_chat_flutter_core/example/android/app/src/main/java/example/example/MainActivity.java b/packages/stream_chat_flutter_core/example/android/app/src/main/java/example/example/MainActivity.java deleted file mode 100644 index 334c0856e4..0000000000 --- a/packages/stream_chat_flutter_core/example/android/app/src/main/java/example/example/MainActivity.java +++ /dev/null @@ -1,5 +0,0 @@ -package example.example; - -import io.flutter.embedding.android.FlutterActivity; - -public class MainActivity extends FlutterActivity {} diff --git a/packages/stream_chat_flutter_core/example/android/app/src/main/kotlin/example/example/MainActivity.kt b/packages/stream_chat_flutter_core/example/android/app/src/main/kotlin/example/example/MainActivity.kt deleted file mode 100644 index e152681a12..0000000000 --- a/packages/stream_chat_flutter_core/example/android/app/src/main/kotlin/example/example/MainActivity.kt +++ /dev/null @@ -1,6 +0,0 @@ -package example.example - -import io.flutter.embedding.android.FlutterActivity - -class MainActivity: FlutterActivity() { -} diff --git a/packages/stream_chat_flutter_core/example/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/stream_chat_flutter_core/example/android/app/src/main/res/drawable-v21/launch_background.xml deleted file mode 100644 index f74085f3f6..0000000000 --- a/packages/stream_chat_flutter_core/example/android/app/src/main/res/drawable-v21/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/packages/stream_chat_flutter_core/example/android/app/src/main/res/drawable/launch_background.xml b/packages/stream_chat_flutter_core/example/android/app/src/main/res/drawable/launch_background.xml deleted file mode 100644 index 304732f884..0000000000 --- a/packages/stream_chat_flutter_core/example/android/app/src/main/res/drawable/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/packages/stream_chat_flutter_core/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/stream_chat_flutter_core/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index db77bb4b7b..0000000000 Binary files a/packages/stream_chat_flutter_core/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/stream_chat_flutter_core/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 17987b79bb..0000000000 Binary files a/packages/stream_chat_flutter_core/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/stream_chat_flutter_core/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 09d4391482..0000000000 Binary files a/packages/stream_chat_flutter_core/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/stream_chat_flutter_core/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index d5f1c8d34e..0000000000 Binary files a/packages/stream_chat_flutter_core/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/stream_chat_flutter_core/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 4d6372eebd..0000000000 Binary files a/packages/stream_chat_flutter_core/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/android/app/src/main/res/values-night/styles.xml b/packages/stream_chat_flutter_core/example/android/app/src/main/res/values-night/styles.xml deleted file mode 100644 index 3db14bb539..0000000000 --- a/packages/stream_chat_flutter_core/example/android/app/src/main/res/values-night/styles.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/packages/stream_chat_flutter_core/example/android/app/src/main/res/values/styles.xml b/packages/stream_chat_flutter_core/example/android/app/src/main/res/values/styles.xml deleted file mode 100644 index 1f83a33fd4..0000000000 --- a/packages/stream_chat_flutter_core/example/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/packages/stream_chat_flutter_core/example/android/app/src/profile/AndroidManifest.xml b/packages/stream_chat_flutter_core/example/android/app/src/profile/AndroidManifest.xml deleted file mode 100644 index 397a94923f..0000000000 --- a/packages/stream_chat_flutter_core/example/android/app/src/profile/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/packages/stream_chat_flutter_core/example/android/build.gradle b/packages/stream_chat_flutter_core/example/android/build.gradle deleted file mode 100644 index c743dd2ba4..0000000000 --- a/packages/stream_chat_flutter_core/example/android/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -buildscript { - ext.kotlin_version = '1.7.21' - repositories { - google() - jcenter() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.3.1' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -allprojects { - repositories { - google() - jcenter() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -tasks.register("clean", Delete) { - delete rootProject.buildDir -} diff --git a/packages/stream_chat_flutter_core/example/android/gradle.properties b/packages/stream_chat_flutter_core/example/android/gradle.properties deleted file mode 100644 index a6738207fd..0000000000 --- a/packages/stream_chat_flutter_core/example/android/gradle.properties +++ /dev/null @@ -1,4 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M -android.useAndroidX=true -android.enableJetifier=true -android.enableR8=true diff --git a/packages/stream_chat_flutter_core/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/stream_chat_flutter_core/example/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 02e5f58171..0000000000 --- a/packages/stream_chat_flutter_core/example/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Fri Jun 23 08:50:38 CEST 2017 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip diff --git a/packages/stream_chat_flutter_core/example/android/settings.gradle b/packages/stream_chat_flutter_core/example/android/settings.gradle deleted file mode 100644 index 44e62bcf06..0000000000 --- a/packages/stream_chat_flutter_core/example/android/settings.gradle +++ /dev/null @@ -1,11 +0,0 @@ -include ':app' - -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() - -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } - -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/packages/stream_chat_flutter_core/example/ios/.gitignore b/packages/stream_chat_flutter_core/example/ios/.gitignore deleted file mode 100644 index e96ef602b8..0000000000 --- a/packages/stream_chat_flutter_core/example/ios/.gitignore +++ /dev/null @@ -1,32 +0,0 @@ -*.mode1v3 -*.mode2v3 -*.moved-aside -*.pbxuser -*.perspectivev3 -**/*sync/ -.sconsign.dblite -.tags* -**/.vagrant/ -**/DerivedData/ -Icon? -**/Pods/ -**/.symlinks/ -profile -xcuserdata -**/.generated/ -Flutter/App.framework -Flutter/Flutter.framework -Flutter/Flutter.podspec -Flutter/Generated.xcconfig -Flutter/app.flx -Flutter/app.zip -Flutter/flutter_assets/ -Flutter/flutter_export_environment.sh -ServiceDefinitions.json -Runner/GeneratedPluginRegistrant.* - -# Exceptions to above rules. -!default.mode1v3 -!default.mode2v3 -!default.pbxuser -!default.perspectivev3 diff --git a/packages/stream_chat_flutter_core/example/ios/Flutter/AppFrameworkInfo.plist b/packages/stream_chat_flutter_core/example/ios/Flutter/AppFrameworkInfo.plist deleted file mode 100644 index 4f8d4d2456..0000000000 --- a/packages/stream_chat_flutter_core/example/ios/Flutter/AppFrameworkInfo.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - MinimumOSVersion - 11.0 - - diff --git a/packages/stream_chat_flutter_core/example/ios/Flutter/Debug.xcconfig b/packages/stream_chat_flutter_core/example/ios/Flutter/Debug.xcconfig deleted file mode 100644 index e8efba1146..0000000000 --- a/packages/stream_chat_flutter_core/example/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "Generated.xcconfig" diff --git a/packages/stream_chat_flutter_core/example/ios/Flutter/Release.xcconfig b/packages/stream_chat_flutter_core/example/ios/Flutter/Release.xcconfig deleted file mode 100644 index 399e9340e6..0000000000 --- a/packages/stream_chat_flutter_core/example/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "Generated.xcconfig" diff --git a/packages/stream_chat_flutter_core/example/ios/Podfile b/packages/stream_chat_flutter_core/example/ios/Podfile deleted file mode 100644 index d97f17e223..0000000000 --- a/packages/stream_chat_flutter_core/example/ios/Podfile +++ /dev/null @@ -1,44 +0,0 @@ -# Uncomment this line to define a global platform for your project -# platform :ios, '12.0' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_ios_podfile_setup - -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) - target 'RunnerTests' do - inherit! :search_paths - end -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_ios_build_settings(target) - end -end diff --git a/packages/stream_chat_flutter_core/example/ios/Runner.xcodeproj/project.pbxproj b/packages/stream_chat_flutter_core/example/ios/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index e914b29b5e..0000000000 --- a/packages/stream_chat_flutter_core/example/ios/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,588 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXBuildFile section */ - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 627D91BDE1F8541F3F7ED6F4 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 61A8B412FF5760B3A4AB4DA8 /* Pods_Runner.framework */; }; - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 2842CDA96EAEE77FE3C70E9C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 61A8B412FF5760B3A4AB4DA8 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 8E351346A04B12ED4F50245F /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - CBA476032850F38752563B57 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 97C146EB1CF9000F007C117D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 627D91BDE1F8541F3F7ED6F4 /* Pods_Runner.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 56B30EE92A6A07DA0FBC9C29 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 61A8B412FF5760B3A4AB4DA8 /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - 9740EEB11CF90186004384FC /* Flutter */ = { - isa = PBXGroup; - children = ( - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 9740EEB31CF90195004384FC /* Generated.xcconfig */, - ); - name = Flutter; - sourceTree = ""; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - 9740EEB11CF90186004384FC /* Flutter */, - 97C146F01CF9000F007C117D /* Runner */, - 97C146EF1CF9000F007C117D /* Products */, - BB44F05EB4BC2FB48CA62970 /* Pods */, - 56B30EE92A6A07DA0FBC9C29 /* Frameworks */, - ); - sourceTree = ""; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - ); - name = Products; - sourceTree = ""; - }; - 97C146F01CF9000F007C117D /* Runner */ = { - isa = PBXGroup; - children = ( - 97C146FA1CF9000F007C117D /* Main.storyboard */, - 97C146FD1CF9000F007C117D /* Assets.xcassets */, - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 97C147021CF9000F007C117D /* Info.plist */, - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, - ); - path = Runner; - sourceTree = ""; - }; - BB44F05EB4BC2FB48CA62970 /* Pods */ = { - isa = PBXGroup; - children = ( - 2842CDA96EAEE77FE3C70E9C /* Pods-Runner.debug.xcconfig */, - CBA476032850F38752563B57 /* Pods-Runner.release.xcconfig */, - 8E351346A04B12ED4F50245F /* Pods-Runner.profile.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 97C146ED1CF9000F007C117D /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 7DAF68A8285D06012F1C3611 /* [CP] Check Pods Manifest.lock */, - 9740EEB61CF901F6004384FC /* Run Script */, - 97C146EA1CF9000F007C117D /* Sources */, - 97C146EB1CF9000F007C117D /* Frameworks */, - 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - EDC4CAB19281E65D2B18E888 /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Runner; - productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* Runner.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 97C146E61CF9000F007C117D /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1430; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 97C146ED1CF9000F007C117D = { - CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 1100; - }; - }; - }; - buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 97C146E51CF9000F007C117D; - productRefGroup = 97C146EF1CF9000F007C117D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 97C146ED1CF9000F007C117D /* Runner */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 97C146EC1CF9000F007C117D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 7DAF68A8285D06012F1C3611 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; - EDC4CAB19281E65D2B18E888 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 97C146EA1CF9000F007C117D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 97C146FA1CF9000F007C117D /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C146FB1CF9000F007C117D /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 249021D3217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Profile; - }; - 249021D4217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = EHV7XZLAHA; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = example.example; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Profile; - }; - 97C147031CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 97C147041CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 97C147061CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = EHV7XZLAHA; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = example.example; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - 97C147071CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = EHV7XZLAHA; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = example.example; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147031CF9000F007C117D /* Debug */, - 97C147041CF9000F007C117D /* Release */, - 249021D3217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147061CF9000F007C117D /* Debug */, - 97C147071CF9000F007C117D /* Release */, - 249021D4217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 97C146E61CF9000F007C117D /* Project object */; -} diff --git a/packages/stream_chat_flutter_core/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/stream_chat_flutter_core/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a625..0000000000 --- a/packages/stream_chat_flutter_core/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/packages/stream_chat_flutter_core/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/stream_chat_flutter_core/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/packages/stream_chat_flutter_core/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/packages/stream_chat_flutter_core/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/stream_chat_flutter_core/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5ea..0000000000 --- a/packages/stream_chat_flutter_core/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/packages/stream_chat_flutter_core/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/stream_chat_flutter_core/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index b52b2e698b..0000000000 --- a/packages/stream_chat_flutter_core/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/stream_chat_flutter_core/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/stream_chat_flutter_core/example/ios/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 21a3cc14c7..0000000000 --- a/packages/stream_chat_flutter_core/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/packages/stream_chat_flutter_core/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/stream_chat_flutter_core/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/packages/stream_chat_flutter_core/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/packages/stream_chat_flutter_core/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/stream_chat_flutter_core/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5ea..0000000000 --- a/packages/stream_chat_flutter_core/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/AppDelegate.swift b/packages/stream_chat_flutter_core/example/ios/Runner/AppDelegate.swift deleted file mode 100644 index 70693e4a8c..0000000000 --- a/packages/stream_chat_flutter_core/example/ios/Runner/AppDelegate.swift +++ /dev/null @@ -1,13 +0,0 @@ -import UIKit -import Flutter - -@UIApplicationMain -@objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } -} diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d36b1fab2d..0000000000 --- a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - }, - { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "Icon-App-1024x1024@1x.png", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png deleted file mode 100644 index dc9ada4725..0000000000 Binary files a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png deleted file mode 100644 index 28c6bf0301..0000000000 Binary files a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png deleted file mode 100644 index 2ccbfd967d..0000000000 Binary files a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index f091b6b0bc..0000000000 Binary files a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png deleted file mode 100644 index 4cde12118d..0000000000 Binary files a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index d0ef06e7ed..0000000000 Binary files a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png deleted file mode 100644 index dcdc2306c2..0000000000 Binary files a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png deleted file mode 100644 index 2ccbfd967d..0000000000 Binary files a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index c8f9ed8f5c..0000000000 Binary files a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png deleted file mode 100644 index a6d6b8609d..0000000000 Binary files a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index a6d6b8609d..0000000000 Binary files a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index 75b2d164a5..0000000000 Binary files a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png deleted file mode 100644 index c4df70d39d..0000000000 Binary files a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png deleted file mode 100644 index 6a84f41e14..0000000000 Binary files a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png deleted file mode 100644 index d0e1f58536..0000000000 Binary files a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json deleted file mode 100644 index 0bedcf2fd4..0000000000 --- a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "LaunchImage.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png deleted file mode 100644 index 9da19eacad..0000000000 Binary files a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png deleted file mode 100644 index 9da19eacad..0000000000 Binary files a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png deleted file mode 100644 index 9da19eacad..0000000000 Binary files a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md deleted file mode 100644 index 89c2725b70..0000000000 --- a/packages/stream_chat_flutter_core/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Launch Screen Assets - -You can customize the launch screen with your own desired assets by replacing the image files in this directory. - -You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/stream_chat_flutter_core/example/ios/Runner/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index f2e259c7c9..0000000000 --- a/packages/stream_chat_flutter_core/example/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Base.lproj/Main.storyboard b/packages/stream_chat_flutter_core/example/ios/Runner/Base.lproj/Main.storyboard deleted file mode 100644 index f3c28516fb..0000000000 --- a/packages/stream_chat_flutter_core/example/ios/Runner/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Info.plist b/packages/stream_chat_flutter_core/example/ios/Runner/Info.plist deleted file mode 100644 index 4f68a2cee1..0000000000 --- a/packages/stream_chat_flutter_core/example/ios/Runner/Info.plist +++ /dev/null @@ -1,49 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - example - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - CADisableMinimumFrameDurationOnPhone - - UIApplicationSupportsIndirectInputEvents - - - diff --git a/packages/stream_chat_flutter_core/example/ios/Runner/Runner-Bridging-Header.h b/packages/stream_chat_flutter_core/example/ios/Runner/Runner-Bridging-Header.h deleted file mode 100644 index 308a2a560b..0000000000 --- a/packages/stream_chat_flutter_core/example/ios/Runner/Runner-Bridging-Header.h +++ /dev/null @@ -1 +0,0 @@ -#import "GeneratedPluginRegistrant.h" diff --git a/packages/stream_chat_flutter_core/example/lib/main.dart b/packages/stream_chat_flutter_core/example/lib/main.dart deleted file mode 100644 index 0a523f81ac..0000000000 --- a/packages/stream_chat_flutter_core/example/lib/main.dart +++ /dev/null @@ -1,370 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -Future main() async { - /// Create a new instance of [StreamChatClient] passing the apikey obtained - /// from your project dashboard. - final client = StreamChatClient('b67pax5b2wdq'); - - /// Set the current user. In a production scenario, this should be done using - /// a backend to generate a user token using our server SDK. - /// Please see the following for more information: - /// https://getstream.io/chat/docs/ios_user_setup_and_tokens/ - await client.connectUser( - User( - id: 'cool-shadow-7', - image: - 'https://getstream.io/random_png/?id=cool-shadow-7&name=Cool+shadow', - ), - '''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1zaGFkb3ctNyJ9.gkOlCRb1qgy4joHPaxFwPOdXcGvSPvp6QY0S4mpRkVo''', - ); - - runApp( - StreamExample( - client: client, - ), - ); -} - -/// Example application using Stream Chat core widgets. -/// Stream Chat Core is a set of Flutter wrappers which provide basic -/// functionality for building Flutter applications using Stream. -/// -/// If you'd prefer using pre-made UI widgets for your app, please see our other -/// package, `stream_chat_flutter`. -class StreamExample extends StatelessWidget { - /// Minimal example using Stream's core Flutter package. - /// - /// If you'd prefer using pre-made UI widgets for your app, please see our - /// other package, `stream_chat_flutter`. - const StreamExample({ - Key? key, - required this.client, - }) : super(key: key); - - /// Instance of Stream Client. - /// Stream's [StreamChatClient] can be used to connect to our servers and - /// set the default user for the application. Performing these actions - /// trigger a websocket connection allowing for real-time updates. - final StreamChatClient client; - - @override - Widget build(BuildContext context) => MaterialApp( - title: 'Stream Chat Core Example', - home: HomeScreen(), - builder: (context, child) => StreamChatCore( - client: client, - child: child!, - ), - ); -} - -/// Basic layout displaying a list of [Channel]s the user is a part of. -/// This is implemented using a [StreamChannelListController]. -/// -/// [StreamChannelListController] is a controller that lets you manage a list of -/// channels. -class HomeScreen extends StatefulWidget { - /// Builds a basic layout displaying a list of [Channel]s the user is a - /// part of. - const HomeScreen({Key? key}) : super(key: key); - - @override - State createState() => _HomeScreenState(); -} - -class _HomeScreenState extends State { - /// Controller used for loading more data and controlling pagination in - /// [StreamChannelListController]. - late final channelListController = StreamChannelListController( - client: StreamChatCore.of(context).client, - filter: Filter.and([ - Filter.equal('type', 'messaging'), - Filter.in_( - 'members', - [ - StreamChatCore.of(context).currentUser!.id, - ], - ), - ]), - ); - - @override - void initState() { - channelListController.doInitialLoad(); - super.initState(); - } - - @override - void dispose() { - channelListController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) => Scaffold( - appBar: AppBar( - title: const Text('Channels'), - ), - body: PagedValueListenableBuilder( - valueListenable: channelListController, - builder: (context, value, child) { - return value.when( - (channels, nextPageKey, error) => LazyLoadScrollView( - onEndOfPage: () async { - if (nextPageKey != null) { - channelListController.loadMore(nextPageKey); - } - }, - child: ListView.builder( - /// We're using the channels length when there are no more - /// pages to load and there are no errors with pagination. - /// In case we need to show a loading indicator or and error - /// tile we're increasing the count by 1. - itemCount: (nextPageKey != null || error != null) - ? channels.length + 1 - : channels.length, - itemBuilder: (BuildContext context, int index) { - if (index == channels.length) { - if (error != null) { - return TextButton( - onPressed: () { - channelListController.retry(); - }, - child: Text(error.message), - ); - } - return CircularProgressIndicator(); - } - - final _item = channels[index]; - return ListTile( - title: Text(_item.name ?? ''), - subtitle: StreamBuilder( - stream: _item.state!.lastMessageStream, - initialData: _item.state!.lastMessage, - builder: (context, snapshot) { - if (snapshot.hasData) { - return Text(snapshot.data!.text!); - } - - return const SizedBox(); - }, - ), - onTap: () { - /// Display a list of messages when the user taps on - /// an item. We can use [StreamChannel] to wrap our - /// [MessageScreen] screen with the selected channel. - /// - /// This allows us to use a built-in inherited widget - /// for accessing our `channel` later on. - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => StreamChannel( - channel: _item, - child: const MessageScreen(), - ), - ), - ); - }, - ); - }, - ), - ), - loading: () => const Center( - child: SizedBox( - height: 100, - width: 100, - child: CircularProgressIndicator(), - ), - ), - error: (e) => Center( - child: Text( - 'Oh no, something went wrong. ' - 'Please check your config. $e', - ), - ), - ); - }, - ), - ); -} - -/// A list of messages sent in the current channel. -/// When a user taps on a channel in [HomeScreen], a navigator push -/// [MessageScreen] to display the list of messages in the selected channel. -/// -/// This is implemented using [MessageListCore], a convenience builder with -/// callbacks for building UIs based on different api results. -class MessageScreen extends StatefulWidget { - /// Build a MessageScreen - const MessageScreen({Key? key}) : super(key: key); - - @override - _MessageScreenState createState() => _MessageScreenState(); -} - -class _MessageScreenState extends State { - final StreamMessageInputController messageInputController = - StreamMessageInputController(); - late final ScrollController _scrollController; - final messageListController = MessageListController(); - - @override - void initState() { - super.initState(); - _scrollController = ScrollController(); - } - - @override - void dispose() { - messageInputController.dispose(); - _scrollController.dispose(); - super.dispose(); - } - - void _updateList() { - _scrollController.animateTo( - 0, - duration: const Duration(milliseconds: 200), - curve: Curves.easeOut, - ); - } - - @override - Widget build(BuildContext context) { - /// To access the current channel, we can use the `.of()` method on - /// [StreamChannel] to fetch the closest instance. - final channel = StreamChannel.of(context).channel; - return Scaffold( - appBar: AppBar( - title: StreamBuilder>( - initialData: channel.state?.typingEvents.keys, - stream: channel.state?.typingEventsStream.map((it) => it.keys), - builder: (context, snapshot) { - if (snapshot.hasData && snapshot.data!.isNotEmpty) { - return Text('${snapshot.data!.first.name} is typing...'); - } - return const SizedBox(); - }, - ), - ), - body: SafeArea( - child: Column( - children: [ - Expanded( - child: LazyLoadScrollView( - onEndOfPage: () async { - messageListController.paginateData!(); - }, - child: MessageListCore( - messageListController: messageListController, - emptyBuilder: (BuildContext context) => const Center( - child: Text('Nothing here yet'), - ), - loadingBuilder: (BuildContext context) => const Center( - child: SizedBox( - height: 100, - width: 100, - child: CircularProgressIndicator(), - ), - ), - messageListBuilder: ( - BuildContext context, - List messages, - ) => - ListView.builder( - controller: _scrollController, - itemCount: messages.length, - reverse: true, - itemBuilder: (BuildContext context, int index) { - final item = messages[index]; - final client = StreamChatCore.of(context).client; - if (item.user!.id == client.uid) { - return Align( - alignment: Alignment.centerRight, - child: Padding( - padding: const EdgeInsets.all(8), - child: Text(item.text!), - ), - ); - } else { - return Align( - alignment: Alignment.centerLeft, - child: Padding( - padding: const EdgeInsets.all(8), - child: Text(item.text!), - ), - ); - } - }, - ), - errorBuilder: (BuildContext context, error) { - print(error.toString()); - return const Center( - child: SizedBox( - height: 100, - width: 100, - child: - Text('Oh no, an error occured. Please see logs.'), - ), - ); - }, - ), - ), - ), - Padding( - padding: const EdgeInsets.all(8), - child: Row( - children: [ - Expanded( - child: TextField( - controller: messageInputController.textFieldController, - decoration: const InputDecoration( - hintText: 'Enter your message', - ), - ), - ), - Material( - type: MaterialType.circle, - color: Colors.blue, - clipBehavior: Clip.hardEdge, - child: InkWell( - onTap: () async { - if (messageInputController.text.isNotEmpty) { - await channel.sendMessage( - messageInputController.message, - ); - messageInputController.clear(); - if (mounted) { - _updateList(); - } - } - }, - child: const Padding( - padding: EdgeInsets.all(8), - child: Center( - child: Icon( - Icons.send, - color: Colors.white, - ), - ), - ), - ), - ), - ], - ), - ), - ], - ), - ), - ); - } -} - -/// Extensions can be used to add functionality to the SDK. In the example -/// below, we add a simple extensions to the [StreamChatClient]. -extension on StreamChatClient { - /// Fetches the current user id. - String get uid => state.currentUser!.id; -} diff --git a/packages/stream_chat_flutter_core/example/pubspec.yaml b/packages/stream_chat_flutter_core/example/pubspec.yaml deleted file mode 100644 index 99316f706f..0000000000 --- a/packages/stream_chat_flutter_core/example/pubspec.yaml +++ /dev/null @@ -1,29 +0,0 @@ -name: stream_chat_flutter_core_example -description: Example app for testing stream_chat_flutter_core -publish_to: 'none' -version: 1.0.0+1 - -# Note: The environment configuration and dependency versions are managed by Melos. -# -# Do not edit them manually. -# -# Steps to update dependencies: -# 1. Modify the version in the melos.yaml file. -# 2. Run `melos bootstrap` to apply changes. -# -# Steps to add a new dependency: -# 1. Add the dependency to this list. -# 2. Add it to the melos.yaml file for future updates. - -environment: - sdk: ^3.6.2 - flutter: ">=3.27.4" - -dependencies: - cupertino_icons: ^1.0.3 - flutter: - sdk: flutter - stream_chat_flutter_core: ^9.4.0 - -flutter: - uses-material-design: true diff --git a/packages/stream_chat_flutter_core/example/web/favicon.png b/packages/stream_chat_flutter_core/example/web/favicon.png deleted file mode 100644 index 8aaa46ac1a..0000000000 Binary files a/packages/stream_chat_flutter_core/example/web/favicon.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/web/icons/Icon-192.png b/packages/stream_chat_flutter_core/example/web/icons/Icon-192.png deleted file mode 100644 index b749bfef07..0000000000 Binary files a/packages/stream_chat_flutter_core/example/web/icons/Icon-192.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/web/icons/Icon-512.png b/packages/stream_chat_flutter_core/example/web/icons/Icon-512.png deleted file mode 100644 index 88cfd48dff..0000000000 Binary files a/packages/stream_chat_flutter_core/example/web/icons/Icon-512.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/web/icons/Icon-maskable-192.png b/packages/stream_chat_flutter_core/example/web/icons/Icon-maskable-192.png deleted file mode 100644 index eb9b4d76e5..0000000000 Binary files a/packages/stream_chat_flutter_core/example/web/icons/Icon-maskable-192.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/web/icons/Icon-maskable-512.png b/packages/stream_chat_flutter_core/example/web/icons/Icon-maskable-512.png deleted file mode 100644 index d69c56691f..0000000000 Binary files a/packages/stream_chat_flutter_core/example/web/icons/Icon-maskable-512.png and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/web/index.html b/packages/stream_chat_flutter_core/example/web/index.html deleted file mode 100644 index b6b9dd2349..0000000000 --- a/packages/stream_chat_flutter_core/example/web/index.html +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - - - - - - - - - - - - - - example - - - - - - - diff --git a/packages/stream_chat_flutter_core/example/web/manifest.json b/packages/stream_chat_flutter_core/example/web/manifest.json deleted file mode 100644 index 096edf8fe4..0000000000 --- a/packages/stream_chat_flutter_core/example/web/manifest.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "example", - "short_name": "example", - "start_url": ".", - "display": "standalone", - "background_color": "#0175C2", - "theme_color": "#0175C2", - "description": "A new Flutter project.", - "orientation": "portrait-primary", - "prefer_related_applications": false, - "icons": [ - { - "src": "icons/Icon-192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "icons/Icon-512.png", - "sizes": "512x512", - "type": "image/png" - }, - { - "src": "icons/Icon-maskable-192.png", - "sizes": "192x192", - "type": "image/png", - "purpose": "maskable" - }, - { - "src": "icons/Icon-maskable-512.png", - "sizes": "512x512", - "type": "image/png", - "purpose": "maskable" - } - ] -} diff --git a/packages/stream_chat_flutter_core/example/windows/.gitignore b/packages/stream_chat_flutter_core/example/windows/.gitignore deleted file mode 100644 index d492d0d98c..0000000000 --- a/packages/stream_chat_flutter_core/example/windows/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -flutter/ephemeral/ - -# Visual Studio user-specific files. -*.suo -*.user -*.userosscache -*.sln.docstates - -# Visual Studio build-related files. -x64/ -x86/ - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ diff --git a/packages/stream_chat_flutter_core/example/windows/CMakeLists.txt b/packages/stream_chat_flutter_core/example/windows/CMakeLists.txt deleted file mode 100644 index 1633297a0c..0000000000 --- a/packages/stream_chat_flutter_core/example/windows/CMakeLists.txt +++ /dev/null @@ -1,95 +0,0 @@ -cmake_minimum_required(VERSION 3.14) -project(example LANGUAGES CXX) - -set(BINARY_NAME "example") - -cmake_policy(SET CMP0063 NEW) - -set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") - -# Configure build options. -get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(IS_MULTICONFIG) - set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" - CACHE STRING "" FORCE) -else() - if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE "Debug" CACHE - STRING "Flutter build mode" FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Profile" "Release") - endif() -endif() - -set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") -set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") -set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") -set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") - -# Use Unicode for all projects. -add_definitions(-DUNICODE -D_UNICODE) - -# Compilation settings that should be applied to most targets. -function(APPLY_STANDARD_SETTINGS TARGET) - target_compile_features(${TARGET} PUBLIC cxx_std_17) - target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") - target_compile_options(${TARGET} PRIVATE /EHsc) - target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") - target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") -endfunction() - -set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") - -# Flutter library and tool build rules. -add_subdirectory(${FLUTTER_MANAGED_DIR}) - -# Application build -add_subdirectory("runner") - -# Generated plugin build rules, which manage building the plugins and adding -# them to the application. -include(flutter/generated_plugins.cmake) - - -# === Installation === -# Support files are copied into place next to the executable, so that it can -# run in place. This is done instead of making a separate bundle (as on Linux) -# so that building and running from within Visual Studio will work. -set(BUILD_BUNDLE_DIR "$") -# Make the "install" step default, as it's required to run. -set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) -endif() - -set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") -set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") - -install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) - -if(PLUGIN_BUNDLED_LIBRARIES) - install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" - DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endif() - -# Fully re-copy the assets directory on each build to avoid having stale files -# from a previous install. -set(FLUTTER_ASSET_DIR_NAME "flutter_assets") -install(CODE " - file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") - " COMPONENT Runtime) -install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" - DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) - -# Install the AOT library on non-Debug builds only. -install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - CONFIGURATIONS Profile;Release - COMPONENT Runtime) diff --git a/packages/stream_chat_flutter_core/example/windows/flutter/CMakeLists.txt b/packages/stream_chat_flutter_core/example/windows/flutter/CMakeLists.txt deleted file mode 100644 index b2e4bd8d65..0000000000 --- a/packages/stream_chat_flutter_core/example/windows/flutter/CMakeLists.txt +++ /dev/null @@ -1,103 +0,0 @@ -cmake_minimum_required(VERSION 3.14) - -set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") - -# Configuration provided via flutter tool. -include(${EPHEMERAL_DIR}/generated_config.cmake) - -# TODO: Move the rest of this into files in ephemeral. See -# https://github.com/flutter/flutter/issues/57146. -set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") - -# === Flutter Library === -set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") - -# Published to parent scope for install step. -set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) -set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) -set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) -set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) - -list(APPEND FLUTTER_LIBRARY_HEADERS - "flutter_export.h" - "flutter_windows.h" - "flutter_messenger.h" - "flutter_plugin_registrar.h" - "flutter_texture_registrar.h" -) -list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") -add_library(flutter INTERFACE) -target_include_directories(flutter INTERFACE - "${EPHEMERAL_DIR}" -) -target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") -add_dependencies(flutter flutter_assemble) - -# === Wrapper === -list(APPEND CPP_WRAPPER_SOURCES_CORE - "core_implementations.cc" - "standard_codec.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_PLUGIN - "plugin_registrar.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_APP - "flutter_engine.cc" - "flutter_view_controller.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") - -# Wrapper sources needed for a plugin. -add_library(flutter_wrapper_plugin STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} -) -apply_standard_settings(flutter_wrapper_plugin) -set_target_properties(flutter_wrapper_plugin PROPERTIES - POSITION_INDEPENDENT_CODE ON) -set_target_properties(flutter_wrapper_plugin PROPERTIES - CXX_VISIBILITY_PRESET hidden) -target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) -target_include_directories(flutter_wrapper_plugin PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_plugin flutter_assemble) - -# Wrapper sources needed for the runner. -add_library(flutter_wrapper_app STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_APP} -) -apply_standard_settings(flutter_wrapper_app) -target_link_libraries(flutter_wrapper_app PUBLIC flutter) -target_include_directories(flutter_wrapper_app PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_app flutter_assemble) - -# === Flutter tool backend === -# _phony_ is a non-existent file to force this command to run every time, -# since currently there's no way to get a full input/output list from the -# flutter tool. -set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") -set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) -add_custom_command( - OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} - ${PHONY_OUTPUT} - COMMAND ${CMAKE_COMMAND} -E env - ${FLUTTER_TOOL_ENVIRONMENT} - "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ - VERBATIM -) -add_custom_target(flutter_assemble DEPENDS - "${FLUTTER_LIBRARY}" - ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} -) diff --git a/packages/stream_chat_flutter_core/example/windows/flutter/generated_plugins.cmake b/packages/stream_chat_flutter_core/example/windows/flutter/generated_plugins.cmake deleted file mode 100644 index cc1361d8d8..0000000000 --- a/packages/stream_chat_flutter_core/example/windows/flutter/generated_plugins.cmake +++ /dev/null @@ -1,24 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST - connectivity_plus -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/packages/stream_chat_flutter_core/example/windows/runner/CMakeLists.txt b/packages/stream_chat_flutter_core/example/windows/runner/CMakeLists.txt deleted file mode 100644 index de2d8916b7..0000000000 --- a/packages/stream_chat_flutter_core/example/windows/runner/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -cmake_minimum_required(VERSION 3.14) -project(runner LANGUAGES CXX) - -add_executable(${BINARY_NAME} WIN32 - "flutter_window.cpp" - "main.cpp" - "utils.cpp" - "win32_window.cpp" - "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" - "Runner.rc" - "runner.exe.manifest" -) -apply_standard_settings(${BINARY_NAME}) -target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") -target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) -target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") -add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/packages/stream_chat_flutter_core/example/windows/runner/Runner.rc b/packages/stream_chat_flutter_core/example/windows/runner/Runner.rc deleted file mode 100644 index 79f0da9ac6..0000000000 --- a/packages/stream_chat_flutter_core/example/windows/runner/Runner.rc +++ /dev/null @@ -1,121 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#pragma code_page(65001) -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "winres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDI_APP_ICON ICON "resources\\app_icon.ico" - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER -#else -#define VERSION_AS_NUMBER 1,0,0 -#endif - -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME -#else -#define VERSION_AS_STRING "1.0.0" -#endif - -VS_VERSION_INFO VERSIONINFO - FILEVERSION VERSION_AS_NUMBER - PRODUCTVERSION VERSION_AS_NUMBER - FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#ifdef _DEBUG - FILEFLAGS VS_FF_DEBUG -#else - FILEFLAGS 0x0L -#endif - FILEOS VOS__WINDOWS32 - FILETYPE VFT_APP - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904e4" - BEGIN - VALUE "CompanyName", "example" "\0" - VALUE "FileDescription", "example" "\0" - VALUE "FileVersion", VERSION_AS_STRING "\0" - VALUE "InternalName", "example" "\0" - VALUE "LegalCopyright", "Copyright (C) 2022 example. All rights reserved." "\0" - VALUE "OriginalFilename", "example.exe" "\0" - VALUE "ProductName", "example" "\0" - VALUE "ProductVersion", VERSION_AS_STRING "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED diff --git a/packages/stream_chat_flutter_core/example/windows/runner/flutter_window.cpp b/packages/stream_chat_flutter_core/example/windows/runner/flutter_window.cpp deleted file mode 100644 index b43b9095ea..0000000000 --- a/packages/stream_chat_flutter_core/example/windows/runner/flutter_window.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "flutter_window.h" - -#include - -#include "flutter/generated_plugin_registrant.h" - -FlutterWindow::FlutterWindow(const flutter::DartProject& project) - : project_(project) {} - -FlutterWindow::~FlutterWindow() {} - -bool FlutterWindow::OnCreate() { - if (!Win32Window::OnCreate()) { - return false; - } - - RECT frame = GetClientArea(); - - // The size here must match the window dimensions to avoid unnecessary surface - // creation / destruction in the startup path. - flutter_controller_ = std::make_unique( - frame.right - frame.left, frame.bottom - frame.top, project_); - // Ensure that basic setup of the controller was successful. - if (!flutter_controller_->engine() || !flutter_controller_->view()) { - return false; - } - RegisterPlugins(flutter_controller_->engine()); - SetChildContent(flutter_controller_->view()->GetNativeWindow()); - return true; -} - -void FlutterWindow::OnDestroy() { - if (flutter_controller_) { - flutter_controller_ = nullptr; - } - - Win32Window::OnDestroy(); -} - -LRESULT -FlutterWindow::MessageHandler(HWND hwnd, UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - // Give Flutter, including plugins, an opportunity to handle window messages. - if (flutter_controller_) { - std::optional result = - flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, - lparam); - if (result) { - return *result; - } - } - - switch (message) { - case WM_FONTCHANGE: - flutter_controller_->engine()->ReloadSystemFonts(); - break; - } - - return Win32Window::MessageHandler(hwnd, message, wparam, lparam); -} diff --git a/packages/stream_chat_flutter_core/example/windows/runner/flutter_window.h b/packages/stream_chat_flutter_core/example/windows/runner/flutter_window.h deleted file mode 100644 index 6da0652f05..0000000000 --- a/packages/stream_chat_flutter_core/example/windows/runner/flutter_window.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef RUNNER_FLUTTER_WINDOW_H_ -#define RUNNER_FLUTTER_WINDOW_H_ - -#include -#include - -#include - -#include "win32_window.h" - -// A window that does nothing but host a Flutter view. -class FlutterWindow : public Win32Window { - public: - // Creates a new FlutterWindow hosting a Flutter view running |project|. - explicit FlutterWindow(const flutter::DartProject& project); - virtual ~FlutterWindow(); - - protected: - // Win32Window: - bool OnCreate() override; - void OnDestroy() override; - LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, - LPARAM const lparam) noexcept override; - - private: - // The project to run. - flutter::DartProject project_; - - // The Flutter instance hosted by this window. - std::unique_ptr flutter_controller_; -}; - -#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/stream_chat_flutter_core/example/windows/runner/main.cpp b/packages/stream_chat_flutter_core/example/windows/runner/main.cpp deleted file mode 100644 index bcb57b0e2a..0000000000 --- a/packages/stream_chat_flutter_core/example/windows/runner/main.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include -#include - -#include "flutter_window.h" -#include "utils.h" - -int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, - _In_ wchar_t *command_line, _In_ int show_command) { - // Attach to console when present (e.g., 'flutter run') or create a - // new console when running with a debugger. - if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { - CreateAndAttachConsole(); - } - - // Initialize COM, so that it is available for use in the library and/or - // plugins. - ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); - - flutter::DartProject project(L"data"); - - std::vector command_line_arguments = - GetCommandLineArguments(); - - project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); - - FlutterWindow window(project); - Win32Window::Point origin(10, 10); - Win32Window::Size size(1280, 720); - if (!window.CreateAndShow(L"example", origin, size)) { - return EXIT_FAILURE; - } - window.SetQuitOnClose(true); - - ::MSG msg; - while (::GetMessage(&msg, nullptr, 0, 0)) { - ::TranslateMessage(&msg); - ::DispatchMessage(&msg); - } - - ::CoUninitialize(); - return EXIT_SUCCESS; -} diff --git a/packages/stream_chat_flutter_core/example/windows/runner/resource.h b/packages/stream_chat_flutter_core/example/windows/runner/resource.h deleted file mode 100644 index 66a65d1e4a..0000000000 --- a/packages/stream_chat_flutter_core/example/windows/runner/resource.h +++ /dev/null @@ -1,16 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by Runner.rc -// -#define IDI_APP_ICON 101 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/packages/stream_chat_flutter_core/example/windows/runner/resources/app_icon.ico b/packages/stream_chat_flutter_core/example/windows/runner/resources/app_icon.ico deleted file mode 100644 index c04e20caf6..0000000000 Binary files a/packages/stream_chat_flutter_core/example/windows/runner/resources/app_icon.ico and /dev/null differ diff --git a/packages/stream_chat_flutter_core/example/windows/runner/runner.exe.manifest b/packages/stream_chat_flutter_core/example/windows/runner/runner.exe.manifest deleted file mode 100644 index c977c4a425..0000000000 --- a/packages/stream_chat_flutter_core/example/windows/runner/runner.exe.manifest +++ /dev/null @@ -1,20 +0,0 @@ - - - - - PerMonitorV2 - - - - - - - - - - - - - - - diff --git a/packages/stream_chat_flutter_core/example/windows/runner/utils.cpp b/packages/stream_chat_flutter_core/example/windows/runner/utils.cpp deleted file mode 100644 index d19bdbbcc3..0000000000 --- a/packages/stream_chat_flutter_core/example/windows/runner/utils.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "utils.h" - -#include -#include -#include -#include - -#include - -void CreateAndAttachConsole() { - if (::AllocConsole()) { - FILE *unused; - if (freopen_s(&unused, "CONOUT$", "w", stdout)) { - _dup2(_fileno(stdout), 1); - } - if (freopen_s(&unused, "CONOUT$", "w", stderr)) { - _dup2(_fileno(stdout), 2); - } - std::ios::sync_with_stdio(); - FlutterDesktopResyncOutputStreams(); - } -} - -std::vector GetCommandLineArguments() { - // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. - int argc; - wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); - if (argv == nullptr) { - return std::vector(); - } - - std::vector command_line_arguments; - - // Skip the first argument as it's the binary name. - for (int i = 1; i < argc; i++) { - command_line_arguments.push_back(Utf8FromUtf16(argv[i])); - } - - ::LocalFree(argv); - - return command_line_arguments; -} - -std::string Utf8FromUtf16(const wchar_t* utf16_string) { - if (utf16_string == nullptr) { - return std::string(); - } - int target_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, nullptr, 0, nullptr, nullptr); - if (target_length == 0) { - return std::string(); - } - std::string utf8_string; - utf8_string.resize(target_length); - int converted_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, utf8_string.data(), - target_length, nullptr, nullptr); - if (converted_length == 0) { - return std::string(); - } - return utf8_string; -} diff --git a/packages/stream_chat_flutter_core/example/windows/runner/utils.h b/packages/stream_chat_flutter_core/example/windows/runner/utils.h deleted file mode 100644 index 3879d54755..0000000000 --- a/packages/stream_chat_flutter_core/example/windows/runner/utils.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef RUNNER_UTILS_H_ -#define RUNNER_UTILS_H_ - -#include -#include - -// Creates a console for the process, and redirects stdout and stderr to -// it for both the runner and the Flutter library. -void CreateAndAttachConsole(); - -// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string -// encoded in UTF-8. Returns an empty std::string on failure. -std::string Utf8FromUtf16(const wchar_t* utf16_string); - -// Gets the command line arguments passed in as a std::vector, -// encoded in UTF-8. Returns an empty std::vector on failure. -std::vector GetCommandLineArguments(); - -#endif // RUNNER_UTILS_H_ diff --git a/packages/stream_chat_flutter_core/example/windows/runner/win32_window.cpp b/packages/stream_chat_flutter_core/example/windows/runner/win32_window.cpp deleted file mode 100644 index c10f08dc7d..0000000000 --- a/packages/stream_chat_flutter_core/example/windows/runner/win32_window.cpp +++ /dev/null @@ -1,245 +0,0 @@ -#include "win32_window.h" - -#include - -#include "resource.h" - -namespace { - -constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; - -// The number of Win32Window objects that currently exist. -static int g_active_window_count = 0; - -using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); - -// Scale helper to convert logical scaler values to physical using passed in -// scale factor -int Scale(int source, double scale_factor) { - return static_cast(source * scale_factor); -} - -// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. -// This API is only needed for PerMonitor V1 awareness mode. -void EnableFullDpiSupportIfAvailable(HWND hwnd) { - HMODULE user32_module = LoadLibraryA("User32.dll"); - if (!user32_module) { - return; - } - auto enable_non_client_dpi_scaling = - reinterpret_cast( - GetProcAddress(user32_module, "EnableNonClientDpiScaling")); - if (enable_non_client_dpi_scaling != nullptr) { - enable_non_client_dpi_scaling(hwnd); - FreeLibrary(user32_module); - } -} - -} // namespace - -// Manages the Win32Window's window class registration. -class WindowClassRegistrar { - public: - ~WindowClassRegistrar() = default; - - // Returns the singleton registar instance. - static WindowClassRegistrar* GetInstance() { - if (!instance_) { - instance_ = new WindowClassRegistrar(); - } - return instance_; - } - - // Returns the name of the window class, registering the class if it hasn't - // previously been registered. - const wchar_t* GetWindowClass(); - - // Unregisters the window class. Should only be called if there are no - // instances of the window. - void UnregisterWindowClass(); - - private: - WindowClassRegistrar() = default; - - static WindowClassRegistrar* instance_; - - bool class_registered_ = false; -}; - -WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; - -const wchar_t* WindowClassRegistrar::GetWindowClass() { - if (!class_registered_) { - WNDCLASS window_class{}; - window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); - window_class.lpszClassName = kWindowClassName; - window_class.style = CS_HREDRAW | CS_VREDRAW; - window_class.cbClsExtra = 0; - window_class.cbWndExtra = 0; - window_class.hInstance = GetModuleHandle(nullptr); - window_class.hIcon = - LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); - window_class.hbrBackground = 0; - window_class.lpszMenuName = nullptr; - window_class.lpfnWndProc = Win32Window::WndProc; - RegisterClass(&window_class); - class_registered_ = true; - } - return kWindowClassName; -} - -void WindowClassRegistrar::UnregisterWindowClass() { - UnregisterClass(kWindowClassName, nullptr); - class_registered_ = false; -} - -Win32Window::Win32Window() { - ++g_active_window_count; -} - -Win32Window::~Win32Window() { - --g_active_window_count; - Destroy(); -} - -bool Win32Window::CreateAndShow(const std::wstring& title, - const Point& origin, - const Size& size) { - Destroy(); - - const wchar_t* window_class = - WindowClassRegistrar::GetInstance()->GetWindowClass(); - - const POINT target_point = {static_cast(origin.x), - static_cast(origin.y)}; - HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); - UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); - double scale_factor = dpi / 96.0; - - HWND window = CreateWindow( - window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, - Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), - Scale(size.width, scale_factor), Scale(size.height, scale_factor), - nullptr, nullptr, GetModuleHandle(nullptr), this); - - if (!window) { - return false; - } - - return OnCreate(); -} - -// static -LRESULT CALLBACK Win32Window::WndProc(HWND const window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - if (message == WM_NCCREATE) { - auto window_struct = reinterpret_cast(lparam); - SetWindowLongPtr(window, GWLP_USERDATA, - reinterpret_cast(window_struct->lpCreateParams)); - - auto that = static_cast(window_struct->lpCreateParams); - EnableFullDpiSupportIfAvailable(window); - that->window_handle_ = window; - } else if (Win32Window* that = GetThisFromHandle(window)) { - return that->MessageHandler(window, message, wparam, lparam); - } - - return DefWindowProc(window, message, wparam, lparam); -} - -LRESULT -Win32Window::MessageHandler(HWND hwnd, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - switch (message) { - case WM_DESTROY: - window_handle_ = nullptr; - Destroy(); - if (quit_on_close_) { - PostQuitMessage(0); - } - return 0; - - case WM_DPICHANGED: { - auto newRectSize = reinterpret_cast(lparam); - LONG newWidth = newRectSize->right - newRectSize->left; - LONG newHeight = newRectSize->bottom - newRectSize->top; - - SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, - newHeight, SWP_NOZORDER | SWP_NOACTIVATE); - - return 0; - } - case WM_SIZE: { - RECT rect = GetClientArea(); - if (child_content_ != nullptr) { - // Size and position the child window. - MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, - rect.bottom - rect.top, TRUE); - } - return 0; - } - - case WM_ACTIVATE: - if (child_content_ != nullptr) { - SetFocus(child_content_); - } - return 0; - } - - return DefWindowProc(window_handle_, message, wparam, lparam); -} - -void Win32Window::Destroy() { - OnDestroy(); - - if (window_handle_) { - DestroyWindow(window_handle_); - window_handle_ = nullptr; - } - if (g_active_window_count == 0) { - WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); - } -} - -Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { - return reinterpret_cast( - GetWindowLongPtr(window, GWLP_USERDATA)); -} - -void Win32Window::SetChildContent(HWND content) { - child_content_ = content; - SetParent(content, window_handle_); - RECT frame = GetClientArea(); - - MoveWindow(content, frame.left, frame.top, frame.right - frame.left, - frame.bottom - frame.top, true); - - SetFocus(child_content_); -} - -RECT Win32Window::GetClientArea() { - RECT frame; - GetClientRect(window_handle_, &frame); - return frame; -} - -HWND Win32Window::GetHandle() { - return window_handle_; -} - -void Win32Window::SetQuitOnClose(bool quit_on_close) { - quit_on_close_ = quit_on_close; -} - -bool Win32Window::OnCreate() { - // No-op; provided for subclasses. - return true; -} - -void Win32Window::OnDestroy() { - // No-op; provided for subclasses. -} diff --git a/packages/stream_chat_flutter_core/example/windows/runner/win32_window.h b/packages/stream_chat_flutter_core/example/windows/runner/win32_window.h deleted file mode 100644 index 17ba431125..0000000000 --- a/packages/stream_chat_flutter_core/example/windows/runner/win32_window.h +++ /dev/null @@ -1,98 +0,0 @@ -#ifndef RUNNER_WIN32_WINDOW_H_ -#define RUNNER_WIN32_WINDOW_H_ - -#include - -#include -#include -#include - -// A class abstraction for a high DPI-aware Win32 Window. Intended to be -// inherited from by classes that wish to specialize with custom -// rendering and input handling -class Win32Window { - public: - struct Point { - unsigned int x; - unsigned int y; - Point(unsigned int x, unsigned int y) : x(x), y(y) {} - }; - - struct Size { - unsigned int width; - unsigned int height; - Size(unsigned int width, unsigned int height) - : width(width), height(height) {} - }; - - Win32Window(); - virtual ~Win32Window(); - - // Creates and shows a win32 window with |title| and position and size using - // |origin| and |size|. New windows are created on the default monitor. Window - // sizes are specified to the OS in physical pixels, hence to ensure a - // consistent size to will treat the width height passed in to this function - // as logical pixels and scale to appropriate for the default monitor. Returns - // true if the window was created successfully. - bool CreateAndShow(const std::wstring& title, - const Point& origin, - const Size& size); - - // Release OS resources associated with window. - void Destroy(); - - // Inserts |content| into the window tree. - void SetChildContent(HWND content); - - // Returns the backing Window handle to enable clients to set icon and other - // window properties. Returns nullptr if the window has been destroyed. - HWND GetHandle(); - - // If true, closing this window will quit the application. - void SetQuitOnClose(bool quit_on_close); - - // Return a RECT representing the bounds of the current client area. - RECT GetClientArea(); - - protected: - // Processes and route salient window messages for mouse handling, - // size change and DPI. Delegates handling of these to member overloads that - // inheriting classes can handle. - virtual LRESULT MessageHandler(HWND window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept; - - // Called when CreateAndShow is called, allowing subclass window-related - // setup. Subclasses should return false if setup fails. - virtual bool OnCreate(); - - // Called when Destroy is called. - virtual void OnDestroy(); - - private: - friend class WindowClassRegistrar; - - // OS callback called by message pump. Handles the WM_NCCREATE message which - // is passed when the non-client area is being created and enables automatic - // non-client DPI scaling so that the non-client area automatically - // responsponds to changes in DPI. All other messages are handled by - // MessageHandler. - static LRESULT CALLBACK WndProc(HWND const window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept; - - // Retrieves a class instance pointer for |window| - static Win32Window* GetThisFromHandle(HWND const window) noexcept; - - bool quit_on_close_ = false; - - // window handle for top level window. - HWND window_handle_ = nullptr; - - // window handle for hosted content. - HWND child_content_ = nullptr; -}; - -#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/stream_chat_flutter_core/lib/src/better_stream_builder.dart b/packages/stream_chat_flutter_core/lib/src/better_stream_builder.dart deleted file mode 100644 index 6305224b61..0000000000 --- a/packages/stream_chat_flutter_core/lib/src/better_stream_builder.dart +++ /dev/null @@ -1,114 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/widgets.dart'; - -/// A more efficient [StreamBuilder] -/// It requires [initialData] and will rebuild -/// only when the new data is different than the current data -/// The [comparator] is used to check if the new data is different -class BetterStreamBuilder extends StatefulWidget { - /// Creates a new BetterStreamBuilder - const BetterStreamBuilder({ - required this.stream, - required this.builder, - this.initialData, - this.noDataBuilder, - this.errorBuilder, - this.comparator, - super.key, - }); - - /// The stream to listen to - final Stream? stream; - - /// The initial data available - final T? initialData; - - /// Comparator used to check if the new data is different than the last one - final bool Function(T?, T?)? comparator; - - /// Builder that builds based on the new snapshot - final Widget Function(BuildContext context, T data) builder; - - /// Builder that builds when the data is null - final Widget Function(BuildContext context)? noDataBuilder; - - /// Builder used when there is an error - final Widget Function(BuildContext context, Object error)? errorBuilder; - - @override - _BetterStreamBuilderState createState() => _BetterStreamBuilderState(); -} - -class _BetterStreamBuilderState - extends State> { - T? _lastEvent; - StreamSubscription? _subscription; - Object? _lastError; - - @override - Widget build(BuildContext context) { - final error = _lastError; - if (error != null) { - final errorBuilder = widget.errorBuilder; - if (errorBuilder != null) { - return errorBuilder(context, error); - } - } - final event = _lastEvent; - if (event == null) { - return widget.noDataBuilder?.call(context) ?? const Offstage(); - } - return widget.builder(context, event); - } - - @override - void initState() { - _lastEvent = widget.initialData; - _subscription = widget.stream?.listen( - _onEvent, - onError: _onError, - ); - super.initState(); - } - - @override - void didUpdateWidget(covariant BetterStreamBuilder oldWidget) { - if (oldWidget.stream != widget.stream) { - _subscription?.cancel(); - _subscription = widget.stream?.listen( - _onEvent, - onError: _onError, - ); - } - super.didUpdateWidget(oldWidget); - } - - @override - void dispose() { - _subscription?.cancel(); - super.dispose(); - } - - void _onError(error) { - if (widget.errorBuilder != null && error != _lastError) { - _lastError = error; - if (mounted) { - // ignore: no-empty-block - setState(() {}); - } - } - } - - void _onEvent(T? event) { - _lastError = null; - final isEqual = - widget.comparator?.call(_lastEvent, event) ?? event == _lastEvent; - if (!isEqual) { - _lastEvent = event; - if (mounted) { - setState(() {}); // ignore: no-empty-block - } - } - } -} diff --git a/packages/stream_chat_flutter_core/lib/src/lazy_load_scroll_view.dart b/packages/stream_chat_flutter_core/lib/src/lazy_load_scroll_view.dart deleted file mode 100644 index d56d5f239a..0000000000 --- a/packages/stream_chat_flutter_core/lib/src/lazy_load_scroll_view.dart +++ /dev/null @@ -1,139 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; - -enum _LoadingStatus { loading, stable } - -/// Wrapper around a [Scrollable] which triggers [onEndOfPage]/[onStartOfPage] the Scrollable -/// reaches to the start or end of the view extent. -class LazyLoadScrollView extends StatefulWidget { - /// Creates a new instance of [LazyLoadScrollView]. The parameter [child] - /// must be supplied and not null. - const LazyLoadScrollView({ - super.key, - required this.child, - this.onStartOfPage, - this.onEndOfPage, - this.onPageScrollStart, - this.onPageScrollEnd, - this.onInBetweenOfPage, - this.scrollOffset = 100, - this.allowNotificationBubbling = false, - }); - - /// The [Widget] that this widget watches for changes on - final Widget child; - - /// Called when the [child] reaches the start of the list - final AsyncCallback? onStartOfPage; - - /// Called when the [child] reaches the end of the list - final AsyncCallback? onEndOfPage; - - /// Called when the list scrolling starts - final VoidCallback? onPageScrollStart; - - /// Called when the list scrolling ends - final VoidCallback? onPageScrollEnd; - - /// Called every time the [child] is in-between the list - final VoidCallback? onInBetweenOfPage; - - /// The offset to take into account when triggering [onEndOfPage]/[onStartOfPage] in pixels - final double scrollOffset; - - /// If true the notifications will keep bubbling up the tree - final bool allowNotificationBubbling; - - @override - State createState() => _LazyLoadScrollViewState(); -} - -class _LazyLoadScrollViewState extends State { - var _loadMoreStatus = _LoadingStatus.stable; - double _scrollPosition = 0; - - @override - Widget build(BuildContext context) => - NotificationListener( - onNotification: _onNotification, - child: widget.child, - ); - - bool _onNotification(ScrollNotification notification) { - if (notification is ScrollStartNotification) { - if (widget.onPageScrollStart != null) { - widget.onPageScrollStart!(); - return !widget.allowNotificationBubbling; - } - } - if (notification is ScrollEndNotification) { - if (widget.onPageScrollEnd != null) { - widget.onPageScrollEnd!(); - return !widget.allowNotificationBubbling; - } - } - if (notification is ScrollUpdateNotification) { - final pixels = notification.metrics.pixels; - final maxScrollExtent = notification.metrics.maxScrollExtent; - final minScrollExtent = notification.metrics.minScrollExtent; - final scrollOffset = widget.scrollOffset; - - if (pixels > (minScrollExtent + scrollOffset) && - pixels < (maxScrollExtent - scrollOffset)) { - if (widget.onInBetweenOfPage != null) { - widget.onInBetweenOfPage!(); - return !widget.allowNotificationBubbling; - } - } - - final extentBefore = notification.metrics.extentBefore; - final extentAfter = notification.metrics.extentAfter; - final scrollingDown = _scrollPosition < pixels; - _scrollPosition = pixels; - - if (scrollingDown) { - if (extentAfter <= scrollOffset) { - _onEndOfPage(); - return !widget.allowNotificationBubbling; - } - } else { - if (extentBefore <= scrollOffset) { - _onStartOfPage(); - return !widget.allowNotificationBubbling; - } - } - } - if (notification is OverscrollNotification) { - if (notification.overscroll > 0) { - _onEndOfPage(); - } - if (notification.overscroll < 0) { - _onStartOfPage(); - } - return !widget.allowNotificationBubbling; - } - return false; - } - - void _onEndOfPage() { - if (_loadMoreStatus == _LoadingStatus.stable) { - if (widget.onEndOfPage != null) { - _loadMoreStatus = _LoadingStatus.loading; - widget.onEndOfPage!().whenComplete(() { - _loadMoreStatus = _LoadingStatus.stable; - }); - } - } - } - - void _onStartOfPage() { - if (_loadMoreStatus == _LoadingStatus.stable) { - if (widget.onStartOfPage != null) { - _loadMoreStatus = _LoadingStatus.loading; - widget.onStartOfPage!().whenComplete(() { - _loadMoreStatus = _LoadingStatus.stable; - }); - } - } - } -} diff --git a/packages/stream_chat_flutter_core/lib/src/message_list_core.dart b/packages/stream_chat_flutter_core/lib/src/message_list_core.dart deleted file mode 100644 index 40dd142e17..0000000000 --- a/packages/stream_chat_flutter_core/lib/src/message_list_core.dart +++ /dev/null @@ -1,249 +0,0 @@ -import 'dart:async'; - -import 'package:collection/collection.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_flutter_core/src/better_stream_builder.dart'; -import 'package:stream_chat_flutter_core/src/stream_channel.dart'; -import 'package:stream_chat_flutter_core/src/typedef.dart'; - -/// Default filter for the message list -bool Function(Message) defaultMessageFilter(String currentUserId) => - (Message m) { - final isMyMessage = m.user?.id == currentUserId; - if (m.shadowed && !isMyMessage) return false; - return true; - }; - -/// [MessageListCore] is a simplified class that allows fetching a list of -/// messages while exposing UI builders. -/// -/// A [MessageListController] is used to paginate data. -/// -/// ```dart -/// class ChannelPage extends StatelessWidget { -/// const ChannelPage({ -/// Key key, -/// }) : super(key: key); -/// -/// @override -/// Widget build(BuildContext context) { -/// return Scaffold( -/// body: Column( -/// children: [ -/// Expanded( -/// child: MessageListCore( -/// emptyBuilder: (context) { -/// return Center( -/// child: Text('Nothing here...'), -/// ); -/// }, -/// loadingBuilder: (context) { -/// return Center( -/// child: CircularProgressIndicator.adaptive(), -/// ); -/// }, -/// messageListBuilder: (context, list) { -/// return MessagesPage(list); -/// }, -/// errorBuilder: (context, err) { -/// return Center( -/// child: Text('Error'), -/// ); -/// }, -/// ), -/// ), -/// ], -/// ), -/// ); -/// } -/// } -/// ``` -/// -/// -/// Make sure to have a [StreamChannel] ancestor in order to provide the -/// information about the channels. -/// -/// The widget uses a [ListView.custom] to render the list of channels. -/// -class MessageListCore extends StatefulWidget { - /// Instantiate a new [MessageListView]. - const MessageListCore({ - super.key, - required this.loadingBuilder, - required this.emptyBuilder, - required this.messageListBuilder, - required this.errorBuilder, - this.parentMessage, - this.messageListController, - this.messageFilter, - this.paginationLimit = 20, - }); - - /// A [MessageListController] allows pagination. - /// Use [ChannelListController.paginateData] pagination. - final MessageListController? messageListController; - - /// Function called when messages are fetched - final Widget Function(BuildContext, List) messageListBuilder; - - /// Function used to build a loading widget - final WidgetBuilder loadingBuilder; - - /// Function used to build an empty widget - final WidgetBuilder emptyBuilder; - - /// Limit used to paginate messages - final int paginationLimit; - - /// Callback triggered when an error occurs while performing the given - /// request. - /// - /// This parameter can be used to display an error message to users in the - /// event of a connection failure. - final ErrorBuilder errorBuilder; - - /// If the current message belongs to a `thread`, this property represents the - /// first message or the parent of the conversation. - final Message? parentMessage; - - /// Predicate used to filter messages - final bool Function(Message)? messageFilter; - - @override - MessageListCoreState createState() => MessageListCoreState(); -} - -/// The current state of the [MessageListCore]. -class MessageListCoreState extends State { - StreamChannelState? _streamChannel; - - bool get _upToDate => _streamChannel!.channel.state?.isUpToDate ?? true; - - bool get _isThreadConversation => widget.parentMessage != null; - - OwnUser? get _currentUser => _streamChannel!.channel.client.state.currentUser; - - var _messages = []; - - @override - Widget build(BuildContext context) { - final messagesStream = _isThreadConversation - ? _streamChannel!.channel.state?.threadsStream - .where((threads) => threads.containsKey(widget.parentMessage!.id)) - .map((threads) => threads[widget.parentMessage!.id]) - : _streamChannel!.channel.state?.messagesStream; - - final initialData = _isThreadConversation - ? _streamChannel!.channel.state?.threads[widget.parentMessage!.id] - : _streamChannel!.channel.state?.messages; - - return BetterStreamBuilder>( - initialData: initialData, - comparator: const ListEquality().equals, - stream: messagesStream, - errorBuilder: widget.errorBuilder, - noDataBuilder: widget.loadingBuilder, - builder: (context, data) { - final messageList = data - .where( - widget.messageFilter ?? defaultMessageFilter(_currentUser!.id), - ) - .toList(growable: false) - .reversed - .toList(growable: false); - if (messageList.isEmpty && !_isThreadConversation) { - if (_upToDate) { - return widget.emptyBuilder(context); - } - } else { - _messages = messageList; - } - return widget.messageListBuilder(context, _messages); - }, - ); - } - - /// Fetches more messages with updated pagination and updates the widget. - /// - /// Optionally pass the fetch direction, defaults to [QueryDirection.top] - /// Optionally pass a limit, defaults to 20 - Future paginateData({ - QueryDirection direction = QueryDirection.top, - }) { - if (!_isThreadConversation) { - return _streamChannel!.queryMessages( - direction: direction, - limit: widget.paginationLimit, - ); - } else { - return _streamChannel!.getReplies( - widget.parentMessage!.id, - limit: widget.paginationLimit, - ); - } - } - - @override - void didChangeDependencies() { - final newStreamChannel = StreamChannel.of(context); - - if (newStreamChannel != _streamChannel) { - if (_streamChannel == null /*only first time*/ && _isThreadConversation) { - newStreamChannel.getReplies( - widget.parentMessage!.id, - limit: widget.paginationLimit, - ); - } - _streamChannel = newStreamChannel; - } - - super.didChangeDependencies(); - } - - @override - void didUpdateWidget(covariant MessageListCore oldWidget) { - super.didUpdateWidget(oldWidget); - - if (widget.messageListController != oldWidget.messageListController) { - _setupController(); - } - - if (widget.parentMessage?.id != widget.parentMessage?.id) { - if (_isThreadConversation) { - _streamChannel!.getReplies( - widget.parentMessage!.id, - limit: widget.paginationLimit, - ); - } - } - } - - @override - void initState() { - _setupController(); - - super.initState(); - } - - void _setupController() { - if (widget.messageListController != null) { - widget.messageListController!.paginateData = paginateData; - } - } - - @override - void dispose() { - if (!_upToDate) { - _streamChannel!.reloadChannel(); - } - super.dispose(); - } -} - -/// Controller used for paginating data in [ChannelListView] -class MessageListController { - /// Call this function to load further data - Future Function({QueryDirection direction})? paginateData; -} diff --git a/packages/stream_chat_flutter_core/lib/src/message_text_field_controller.dart b/packages/stream_chat_flutter_core/lib/src/message_text_field_controller.dart deleted file mode 100644 index ae730c06b8..0000000000 --- a/packages/stream_chat_flutter_core/lib/src/message_text_field_controller.dart +++ /dev/null @@ -1,89 +0,0 @@ -import 'package:flutter/material.dart'; - -/// A function that takes a [BuildContext] and returns a [TextStyle]. -typedef TextStyleBuilder = TextStyle? Function( - BuildContext context, - String text, -); - -/// Controller for the [StreamTextField] widget. -class MessageTextFieldController extends TextEditingController { - /// Returns a new MessageTextFieldController - MessageTextFieldController({ - super.text, - this.textPatternStyle, - }); - - /// Returns a new MessageTextFieldController with the given text [value]. - MessageTextFieldController.fromValue( - super.value, { - this.textPatternStyle, - }) : super.fromValue(); - - /// A map of style to apply to the text matching the RegExp patterns. - final Map? textPatternStyle; - - /// Builds a [TextSpan] from the current text, - /// highlighting the matches for [textPatternStyle]. - @override - TextSpan buildTextSpan({ - required BuildContext context, - TextStyle? style, - required bool withComposing, - }) { - final pattern = textPatternStyle; - if (pattern == null || pattern.isEmpty) { - return super.buildTextSpan( - context: context, - style: style, - withComposing: withComposing, - ); - } - - return TextSpan(text: text, style: style).splitMapJoin( - RegExp( - pattern.keys.map((it) => it.pattern).join('|'), - caseSensitive: false, - ), - onMatch: (match) { - final text = match[0]!; - final key = pattern.keys.firstWhere((it) => it.hasMatch(text)); - return TextSpan( - text: text, - style: pattern[key]?.call( - context, - text, - ), - ); - }, - ); - } -} - -extension _TextSpanX on TextSpan { - TextSpan splitMapJoin( - Pattern pattern, { - TextSpan Function(Match)? onMatch, - TextSpan Function(TextSpan)? onNonMatch, - }) { - final children = []; - - toPlainText().splitMapJoin( - pattern, - onMatch: (match) { - final span = TextSpan(text: match.group(0), style: style); - final updated = onMatch?.call(match); - children.add(updated ?? span); - return span.toPlainText(); - }, - onNonMatch: (text) { - final span = TextSpan(text: text, style: style); - final updatedSpan = onNonMatch?.call(span); - children.add(updatedSpan ?? span); - return span.toPlainText(); - }, - ); - - return TextSpan(style: style, children: children); - } -} diff --git a/packages/stream_chat_flutter_core/lib/src/paged_value_notifier.dart b/packages/stream_chat_flutter_core/lib/src/paged_value_notifier.dart deleted file mode 100644 index aebd3ad2d4..0000000000 --- a/packages/stream_chat_flutter_core/lib/src/paged_value_notifier.dart +++ /dev/null @@ -1,139 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:stream_chat/stream_chat.dart' show StreamChatError; - -part 'paged_value_notifier.freezed.dart'; - -/// Default initial page size multiplier. -const defaultInitialPagedLimitMultiplier = 3; - -/// Value listenable for paged data. -typedef PagedValueListenableBuilder - = ValueListenableBuilder>; - -/// A [PagedValueNotifier] that uses a [PagedListenable] to load data. -/// -/// This class is useful when you need to load data from a server -/// using a [PagedListenable] and want to keep the UI-driven refresh -/// signals in the [PagedListenable]. -/// -/// [PagedValueNotifier] is a [ValueNotifier] that emits a [PagedValue] -/// whenever the data is loaded or an error occurs. -abstract class PagedValueNotifier - extends ValueNotifier> { - /// Creates a [PagedValueNotifier] - PagedValueNotifier(this._initialValue) : super(_initialValue); - - /// Stores initialValue in case we need to call [refresh]. - final PagedValue _initialValue; - - /// Returns the currently loaded items - List get currentItems => value.asSuccess.items; - - /// Appends [newItems] to the previously loaded ones and replaces - /// the next page's key. - void appendPage({ - required List newItems, - required Key nextPageKey, - }) { - final updatedItems = currentItems + newItems; - value = PagedValue(items: updatedItems, nextPageKey: nextPageKey); - } - - /// Appends [newItems] to the previously loaded ones and sets the next page - /// key to `null`. - void appendLastPage(List newItems) { - final updatedItems = currentItems + newItems; - value = PagedValue(items: updatedItems); - } - - /// Retry any failed load requests. - /// - /// Unlike [refresh], this does not resets the whole [value], - /// it only retries the last failed load request. - Future retry() { - final lastValue = value.asSuccess; - assert(lastValue.hasError, ''); - - final nextPageKey = lastValue.nextPageKey; - // resetting the error - value = lastValue.copyWith(error: null); - // ignore: null_check_on_nullable_type_parameter - return loadMore(nextPageKey!); - } - - /// Refresh the data presented by this [PagedValueNotifier]. - /// - /// Resets the [value] to the initial value in case [resetValue] is true. - /// - /// Note: This API is intended for UI-driven refresh signals, - /// such as swipe-to-refresh. - Future refresh({bool resetValue = true}) { - if (resetValue) value = _initialValue; - return doInitialLoad(); - } - - /// Load initial data from the server. - Future doInitialLoad(); - - /// Load more data from the server using [nextPageKey]. - Future loadMore(Key nextPageKey); -} - -/// Paged value that can be used with [PagedValueNotifier]. -@freezed -sealed class PagedValue with _$PagedValue { - /// Represents the success state of the [PagedValue] - // @Assert( - // 'nextPageKey != null', - // 'Cannot set an error if all the pages are already fetched', - // ) - const factory PagedValue({ - /// List with all items loaded so far. - required List items, - - /// The key for the next page to be fetched. - Key? nextPageKey, - - /// The current error, if any. - StreamChatError? error, - }) = Success; - - const PagedValue._(); - - /// Represents the loading state of the [PagedValue]. - const factory PagedValue.loading() = Loading; - - /// Represents the error state of the [PagedValue]. - const factory PagedValue.error(StreamChatError error) = Error; - - /// Returns `true` if the [PagedValue] is [Success]. - bool get isSuccess => this is Success; - - /// Returns `true` if the [PagedValue] is not [Success]. - bool get isNotSuccess => !isSuccess; - - /// Returns the [PagedValue] as [Success]. - Success get asSuccess { - assert( - isSuccess, - 'Cannot get asSuccess if the PagedValue is not in the Success state', - ); - return this as Success; - } - - /// Returns `true` if the [PagedValue] is [Success] - /// and has more items to load. - bool get hasNextPage => asSuccess.nextPageKey != null; - - /// Returns `true` if the [PagedValue] is [Success] and has an error. - bool get hasError => asSuccess.error != null; - - /// - int get itemCount { - final count = asSuccess.items.length; - if (hasNextPage || hasError) return count + 1; - return count; - } -} diff --git a/packages/stream_chat_flutter_core/lib/src/paged_value_notifier.freezed.dart b/packages/stream_chat_flutter_core/lib/src/paged_value_notifier.freezed.dart deleted file mode 100644 index 5d34b66b0f..0000000000 --- a/packages/stream_chat_flutter_core/lib/src/paged_value_notifier.freezed.dart +++ /dev/null @@ -1,594 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// 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 - -part of 'paged_value_notifier.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -/// @nodoc -mixin _$PagedValue { - @optionalTypeArgs - TResult when( - TResult Function( - List items, Key? nextPageKey, StreamChatError? error) - $default, { - required TResult Function() loading, - required TResult Function(StreamChatError error) error, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult? whenOrNull( - TResult? Function( - List items, Key? nextPageKey, StreamChatError? error)? - $default, { - TResult? Function()? loading, - TResult? Function(StreamChatError error)? error, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult maybeWhen( - TResult Function( - List items, Key? nextPageKey, StreamChatError? error)? - $default, { - TResult Function()? loading, - TResult Function(StreamChatError error)? error, - required TResult orElse(), - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult map( - TResult Function(Success value) $default, { - required TResult Function(Loading value) loading, - required TResult Function(Error value) error, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult? mapOrNull( - TResult? Function(Success value)? $default, { - TResult? Function(Loading value)? loading, - TResult? Function(Error value)? error, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult maybeMap( - TResult Function(Success value)? $default, { - TResult Function(Loading value)? loading, - TResult Function(Error value)? error, - required TResult orElse(), - }) => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $PagedValueCopyWith { - factory $PagedValueCopyWith(PagedValue value, - $Res Function(PagedValue) then) = - _$PagedValueCopyWithImpl>; -} - -/// @nodoc -class _$PagedValueCopyWithImpl> - implements $PagedValueCopyWith { - _$PagedValueCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of PagedValue - /// with the given fields replaced by the non-null parameter values. -} - -/// @nodoc -abstract class _$$SuccessImplCopyWith { - factory _$$SuccessImplCopyWith(_$SuccessImpl value, - $Res Function(_$SuccessImpl) then) = - __$$SuccessImplCopyWithImpl; - @useResult - $Res call({List items, Key? nextPageKey, StreamChatError? error}); -} - -/// @nodoc -class __$$SuccessImplCopyWithImpl - extends _$PagedValueCopyWithImpl> - implements _$$SuccessImplCopyWith { - __$$SuccessImplCopyWithImpl(_$SuccessImpl _value, - $Res Function(_$SuccessImpl) _then) - : super(_value, _then); - - /// Create a copy of PagedValue - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? items = null, - Object? nextPageKey = freezed, - Object? error = freezed, - }) { - return _then(_$SuccessImpl( - items: null == items - ? _value._items - : items // ignore: cast_nullable_to_non_nullable - as List, - nextPageKey: freezed == nextPageKey - ? _value.nextPageKey - : nextPageKey // ignore: cast_nullable_to_non_nullable - as Key?, - error: freezed == error - ? _value.error - : error // ignore: cast_nullable_to_non_nullable - as StreamChatError?, - )); - } -} - -/// @nodoc - -class _$SuccessImpl extends Success - with DiagnosticableTreeMixin { - const _$SuccessImpl( - {required final List items, this.nextPageKey, this.error}) - : _items = items, - super._(); - - /// List with all items loaded so far. - final List _items; - - /// List with all items loaded so far. - @override - List get items { - if (_items is EqualUnmodifiableListView) return _items; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_items); - } - - /// The key for the next page to be fetched. - @override - final Key? nextPageKey; - - /// The current error, if any. - @override - final StreamChatError? error; - - @override - String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { - return 'PagedValue<$Key, $Value>(items: $items, nextPageKey: $nextPageKey, error: $error)'; - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty('type', 'PagedValue<$Key, $Value>')) - ..add(DiagnosticsProperty('items', items)) - ..add(DiagnosticsProperty('nextPageKey', nextPageKey)) - ..add(DiagnosticsProperty('error', error)); - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$SuccessImpl && - const DeepCollectionEquality().equals(other._items, _items) && - const DeepCollectionEquality() - .equals(other.nextPageKey, nextPageKey) && - (identical(other.error, error) || other.error == error)); - } - - @override - int get hashCode => Object.hash( - runtimeType, - const DeepCollectionEquality().hash(_items), - const DeepCollectionEquality().hash(nextPageKey), - error); - - /// Create a copy of PagedValue - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$SuccessImplCopyWith> get copyWith => - __$$SuccessImplCopyWithImpl>( - this, _$identity); - - @override - @optionalTypeArgs - TResult when( - TResult Function( - List items, Key? nextPageKey, StreamChatError? error) - $default, { - required TResult Function() loading, - required TResult Function(StreamChatError error) error, - }) { - return $default(items, nextPageKey, this.error); - } - - @override - @optionalTypeArgs - TResult? whenOrNull( - TResult? Function( - List items, Key? nextPageKey, StreamChatError? error)? - $default, { - TResult? Function()? loading, - TResult? Function(StreamChatError error)? error, - }) { - return $default?.call(items, nextPageKey, this.error); - } - - @override - @optionalTypeArgs - TResult maybeWhen( - TResult Function( - List items, Key? nextPageKey, StreamChatError? error)? - $default, { - TResult Function()? loading, - TResult Function(StreamChatError error)? error, - required TResult orElse(), - }) { - if ($default != null) { - return $default(items, nextPageKey, this.error); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map( - TResult Function(Success value) $default, { - required TResult Function(Loading value) loading, - required TResult Function(Error value) error, - }) { - return $default(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull( - TResult? Function(Success value)? $default, { - TResult? Function(Loading value)? loading, - TResult? Function(Error value)? error, - }) { - return $default?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap( - TResult Function(Success value)? $default, { - TResult Function(Loading value)? loading, - TResult Function(Error value)? error, - required TResult orElse(), - }) { - if ($default != null) { - return $default(this); - } - return orElse(); - } -} - -abstract class Success extends PagedValue { - const factory Success( - {required final List items, - final Key? nextPageKey, - final StreamChatError? error}) = _$SuccessImpl; - const Success._() : super._(); - - /// List with all items loaded so far. - List get items; - - /// The key for the next page to be fetched. - Key? get nextPageKey; - - /// The current error, if any. - StreamChatError? get error; - - /// Create a copy of PagedValue - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - _$$SuccessImplCopyWith> get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class _$$LoadingImplCopyWith { - factory _$$LoadingImplCopyWith(_$LoadingImpl value, - $Res Function(_$LoadingImpl) then) = - __$$LoadingImplCopyWithImpl; -} - -/// @nodoc -class __$$LoadingImplCopyWithImpl - extends _$PagedValueCopyWithImpl> - implements _$$LoadingImplCopyWith { - __$$LoadingImplCopyWithImpl(_$LoadingImpl _value, - $Res Function(_$LoadingImpl) _then) - : super(_value, _then); - - /// Create a copy of PagedValue - /// with the given fields replaced by the non-null parameter values. -} - -/// @nodoc - -class _$LoadingImpl extends Loading - with DiagnosticableTreeMixin { - const _$LoadingImpl() : super._(); - - @override - String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { - return 'PagedValue<$Key, $Value>.loading()'; - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - .add(DiagnosticsProperty('type', 'PagedValue<$Key, $Value>.loading')); - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$LoadingImpl); - } - - @override - int get hashCode => runtimeType.hashCode; - - @override - @optionalTypeArgs - TResult when( - TResult Function( - List items, Key? nextPageKey, StreamChatError? error) - $default, { - required TResult Function() loading, - required TResult Function(StreamChatError error) error, - }) { - return loading(); - } - - @override - @optionalTypeArgs - TResult? whenOrNull( - TResult? Function( - List items, Key? nextPageKey, StreamChatError? error)? - $default, { - TResult? Function()? loading, - TResult? Function(StreamChatError error)? error, - }) { - return loading?.call(); - } - - @override - @optionalTypeArgs - TResult maybeWhen( - TResult Function( - List items, Key? nextPageKey, StreamChatError? error)? - $default, { - TResult Function()? loading, - TResult Function(StreamChatError error)? error, - required TResult orElse(), - }) { - if (loading != null) { - return loading(); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map( - TResult Function(Success value) $default, { - required TResult Function(Loading value) loading, - required TResult Function(Error value) error, - }) { - return loading(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull( - TResult? Function(Success value)? $default, { - TResult? Function(Loading value)? loading, - TResult? Function(Error value)? error, - }) { - return loading?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap( - TResult Function(Success value)? $default, { - TResult Function(Loading value)? loading, - TResult Function(Error value)? error, - required TResult orElse(), - }) { - if (loading != null) { - return loading(this); - } - return orElse(); - } -} - -abstract class Loading extends PagedValue { - const factory Loading() = _$LoadingImpl; - const Loading._() : super._(); -} - -/// @nodoc -abstract class _$$ErrorImplCopyWith { - factory _$$ErrorImplCopyWith(_$ErrorImpl value, - $Res Function(_$ErrorImpl) then) = - __$$ErrorImplCopyWithImpl; - @useResult - $Res call({StreamChatError error}); -} - -/// @nodoc -class __$$ErrorImplCopyWithImpl - extends _$PagedValueCopyWithImpl> - implements _$$ErrorImplCopyWith { - __$$ErrorImplCopyWithImpl(_$ErrorImpl _value, - $Res Function(_$ErrorImpl) _then) - : super(_value, _then); - - /// Create a copy of PagedValue - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? error = null, - }) { - return _then(_$ErrorImpl( - null == error - ? _value.error - : error // ignore: cast_nullable_to_non_nullable - as StreamChatError, - )); - } -} - -/// @nodoc - -class _$ErrorImpl extends Error - with DiagnosticableTreeMixin { - const _$ErrorImpl(this.error) : super._(); - - @override - final StreamChatError error; - - @override - String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { - return 'PagedValue<$Key, $Value>.error(error: $error)'; - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty('type', 'PagedValue<$Key, $Value>.error')) - ..add(DiagnosticsProperty('error', error)); - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$ErrorImpl && - (identical(other.error, error) || other.error == error)); - } - - @override - int get hashCode => Object.hash(runtimeType, error); - - /// Create a copy of PagedValue - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$ErrorImplCopyWith> get copyWith => - __$$ErrorImplCopyWithImpl>( - this, _$identity); - - @override - @optionalTypeArgs - TResult when( - TResult Function( - List items, Key? nextPageKey, StreamChatError? error) - $default, { - required TResult Function() loading, - required TResult Function(StreamChatError error) error, - }) { - return error(this.error); - } - - @override - @optionalTypeArgs - TResult? whenOrNull( - TResult? Function( - List items, Key? nextPageKey, StreamChatError? error)? - $default, { - TResult? Function()? loading, - TResult? Function(StreamChatError error)? error, - }) { - return error?.call(this.error); - } - - @override - @optionalTypeArgs - TResult maybeWhen( - TResult Function( - List items, Key? nextPageKey, StreamChatError? error)? - $default, { - TResult Function()? loading, - TResult Function(StreamChatError error)? error, - required TResult orElse(), - }) { - if (error != null) { - return error(this.error); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map( - TResult Function(Success value) $default, { - required TResult Function(Loading value) loading, - required TResult Function(Error value) error, - }) { - return error(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull( - TResult? Function(Success value)? $default, { - TResult? Function(Loading value)? loading, - TResult? Function(Error value)? error, - }) { - return error?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap( - TResult Function(Success value)? $default, { - TResult Function(Loading value)? loading, - TResult Function(Error value)? error, - required TResult orElse(), - }) { - if (error != null) { - return error(this); - } - return orElse(); - } -} - -abstract class Error extends PagedValue { - const factory Error(final StreamChatError error) = _$ErrorImpl; - const Error._() : super._(); - - StreamChatError get error; - - /// Create a copy of PagedValue - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - _$$ErrorImplCopyWith> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/packages/stream_chat_flutter_core/lib/src/paged_value_scroll_view.dart b/packages/stream_chat_flutter_core/lib/src/paged_value_scroll_view.dart deleted file mode 100644 index a078f471c7..0000000000 --- a/packages/stream_chat_flutter_core/lib/src/paged_value_scroll_view.dart +++ /dev/null @@ -1,706 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_flutter_core/src/paged_value_notifier.dart'; - -/// Signature for a function that creates a widget for a given index, e.g., in a -/// [PagedValueListView] and [PagedValueGridView]. -typedef PagedValueScrollViewIndexedWidgetBuilder = Widget Function( - BuildContext context, - List values, - int index, -); - -/// Signature for the item builder that creates the children of the -/// [PagedValueListView] and [PagedValueGridView]. -typedef PagedValueScrollViewLoadMoreErrorBuilder = Widget Function( - BuildContext context, - StreamChatError error, -); - -/// A [ListView] that loads more pages when the user scrolls to the end of the -/// list. -/// -/// Use [loadMoreTriggerIndex] to set the index of the item that triggers the -/// loading of the next page. -class PagedValueListView extends StatefulWidget { - /// Creates a new instance of [PagedValueListView] widget. - const PagedValueListView({ - super.key, - required this.controller, - required this.itemBuilder, - required this.separatorBuilder, - required this.emptyBuilder, - required this.loadMoreErrorBuilder, - required this.loadMoreIndicatorBuilder, - required this.loadingBuilder, - required this.errorBuilder, - this.loadMoreTriggerIndex = 3, - this.scrollDirection = Axis.vertical, - this.reverse = false, - this.scrollController, - this.primary, - this.physics, - this.shrinkWrap = false, - this.padding, - this.addAutomaticKeepAlives = true, - this.addRepaintBoundaries = true, - this.addSemanticIndexes = true, - this.cacheExtent, - this.dragStartBehavior = DragStartBehavior.start, - this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, - this.restorationId, - this.clipBehavior = Clip.hardEdge, - }); - - /// The [PagedValueNotifier] used to control the list of items. - final PagedValueNotifier controller; - - /// A builder that is called to build items in the [ListView]. - /// - /// The `value` parameter is the [V] at this position in the list. - final PagedValueScrollViewIndexedWidgetBuilder itemBuilder; - - /// A builder that is called to build the list separator. - final PagedValueScrollViewIndexedWidgetBuilder separatorBuilder; - - /// A builder that is called to build the empty state of the list. - final WidgetBuilder emptyBuilder; - - /// A builder that is called to build the load more error state of the list. - final PagedValueScrollViewLoadMoreErrorBuilder loadMoreErrorBuilder; - - /// A builder that is called to build the load more indicator of the list. - final WidgetBuilder loadMoreIndicatorBuilder; - - /// A builder that is called to build the loading state of the list. - final WidgetBuilder loadingBuilder; - - /// A builder that is called to build the error state of the list. - final Widget Function(BuildContext, StreamChatError) errorBuilder; - - /// The index to take into account when triggering [controller.loadMore]. - final int loadMoreTriggerIndex; - - /// {@template flutter.widgets.scroll_view.scrollDirection} - /// The axis along which the scroll view scrolls. - /// - /// Defaults to [Axis.vertical]. - /// {@endtemplate} - final Axis scrollDirection; - - /// The amount of space by which to inset the children. - final EdgeInsetsGeometry? padding; - - /// Whether to wrap each child in an [AutomaticKeepAlive]. - /// - /// Typically, children in lazy list are wrapped in [AutomaticKeepAlive] - /// widgets so that children can use [KeepAliveNotification]s to preserve - /// their state when they would otherwise be garbage collected off-screen. - /// - /// This feature (and [addRepaintBoundaries]) must be disabled if the children - /// are going to manually maintain their [KeepAlive] state. It may also be - /// more efficient to disable this feature if it is known ahead of time that - /// none of the children will ever try to keep themselves alive. - /// - /// Defaults to true. - final bool addAutomaticKeepAlives; - - /// Whether to wrap each child in a [RepaintBoundary]. - /// - /// Typically, children in a scrolling container are wrapped in repaint - /// boundaries so that they do not need to be repainted as the list scrolls. - /// If the children are easy to repaint (e.g., solid color blocks or a short - /// snippet of text), it might be more efficient to not add a repaint boundary - /// and simply repaint the children during scrolling. - /// - /// Defaults to true. - final bool addRepaintBoundaries; - - /// Whether to wrap each child in an [IndexedSemantics]. - /// - /// Typically, children in a scrolling container must be annotated with a - /// semantic index in order to generate the correct accessibility - /// announcements. This should only be set to false if the indexes have - /// already been provided by an [IndexedSemantics] widget. - /// - /// Defaults to true. - /// - /// See also: - /// - /// * [IndexedSemantics], for an explanation of how to manually - /// provide semantic indexes. - final bool addSemanticIndexes; - - /// {@template flutter.widgets.scroll_view.reverse} - /// Whether the scroll view scrolls in the reading direction. - /// - /// For example, if [scrollDirection] is [Axis.vertical], then the scroll view - /// scrolls from top to bottom when [reverse] is false and from bottom to top - /// when [reverse] is true. - /// - /// Defaults to false. - /// {@endtemplate} - final bool reverse; - - /// {@template flutter.widgets.scroll_view.controller} - /// An object that can be used to control the position to which this scroll - /// view is scrolled. - /// - /// Must be null if [primary] is true. - /// - /// A [ScrollController] serves several purposes. It can be used to control - /// the initial scroll position (see [ScrollController.initialScrollOffset]). - /// It can be used to control whether the scroll view should automatically - /// save and restore its scroll position in the [PageStorage] (see - /// [ScrollController.keepScrollOffset]). It can be used to read the current - /// scroll position (see [ScrollController.offset]), or change it (see - /// [ScrollController.animateTo]). - /// {@endtemplate} - final ScrollController? scrollController; - - /// {@template flutter.widgets.scroll_view.primary} - /// Whether this is the primary scroll view associated with the parent - /// [PrimaryScrollController]. - /// - /// When this is true, the scroll view is scrollable even if it does not have - /// sufficient content to actually scroll. Otherwise, by default the user can - /// only scroll the view if it has sufficient content. See [physics]. - /// - /// Also when true, the scroll view is used for default [ScrollAction]s. If a - /// ScrollAction is not handled by an otherwise focused part of the - /// application, the ScrollAction will be evaluated using this scroll view, - /// for example, when executing [Shortcuts] key events like page up and down. - /// - /// On iOS, this also identifies the scroll view that will scroll to top in - /// response to a tap in the status bar. - /// {@endtemplate} - /// - /// Defaults to true when [scrollController] is null. - final bool? primary; - - /// {@template flutter.widgets.scroll_view.shrinkWrap} - /// Whether the extent of the scroll view in the [scrollDirection] should be - /// determined by the contents being viewed. - /// - /// If the scroll view does not shrink wrap, then the scroll view will expand - /// to the maximum allowed size in the [scrollDirection]. If the scroll view - /// has unbounded constraints in the [scrollDirection], then [shrinkWrap] must - /// be true. - /// - /// Shrink wrapping the content of the scroll view is significantly more - /// expensive than expanding to the maximum allowed size because the content - /// can expand and contract during scrolling, which means the size of the - /// scroll view needs to be recomputed whenever the scroll position changes. - /// - /// Defaults to false. - /// {@endtemplate} - final bool shrinkWrap; - - /// {@template flutter.widgets.scroll_view.physics} - /// How the scroll view should respond to user input. - /// - /// For example, determines how the scroll view continues to animate after the - /// user stops dragging the scroll view. - /// - /// Defaults to matching platform conventions. Furthermore, if [primary] is - /// false, then the user cannot scroll if there is insufficient content to - /// scroll, while if [primary] is true, they can always attempt to scroll. - /// - /// To force the scroll view to always be scrollable even if there is - /// insufficient content, as if [primary] was true but without necessarily - /// setting it to true, provide an [AlwaysScrollableScrollPhysics] physics - /// object, as in: - /// - /// ```dart - /// physics: const AlwaysScrollableScrollPhysics(), - /// ``` - /// - /// To force the scroll view to use the default platform conventions and not - /// be scrollable if there is insufficient content, regardless of the value of - /// [primary], provide an explicit [ScrollPhysics] object, as in: - /// - /// ```dart - /// physics: const ScrollPhysics(), - /// ``` - /// - /// The physics can be changed dynamically (by providing a new object in a - /// subsequent build), but new physics will only take effect if the _class_ of - /// the provided object changes. Merely constructing a new instance with a - /// different configuration is insufficient to cause the physics to be - /// reapplied. (This is because the final object used is generated - /// dynamically, which can be relatively expensive, and it would be - /// inefficient to speculatively create this object each frame to see if the - /// physics should be updated.) - /// {@endtemplate} - /// - /// If an explicit [ScrollBehavior] is provided to [scrollBehavior], the - /// [ScrollPhysics] provided by that behavior will take precedence after - /// [physics]. - final ScrollPhysics? physics; - - /// {@macro flutter.rendering.RenderViewportBase.cacheExtent} - final double? cacheExtent; - - /// {@macro flutter.widgets.scrollable.dragStartBehavior} - final DragStartBehavior dragStartBehavior; - - /// {@template flutter.widgets.scroll_view.keyboardDismissBehavior} - /// [ScrollViewKeyboardDismissBehavior] the defines how this [ScrollView] will - /// dismiss the keyboard automatically. - /// {@endtemplate} - final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior; - - /// {@macro flutter.widgets.scrollable.restorationId} - final String? restorationId; - - /// {@macro flutter.material.Material.clipBehavior} - /// - /// Defaults to [Clip.hardEdge]. - final Clip clipBehavior; - - @override - State> createState() => - _PagedValueListViewState(); -} - -class _PagedValueListViewState extends State> { - PagedValueNotifier get _controller => widget.controller; - - // Avoids duplicate requests on rebuilds. - bool _hasRequestedNextPage = false; - - @override - void initState() { - super.initState(); - _controller.doInitialLoad(); - } - - @override - void didUpdateWidget(covariant PagedValueListView oldWidget) { - super.didUpdateWidget(oldWidget); - if (_controller != oldWidget.controller) { - // reset duplicate requests flag - _hasRequestedNextPage = false; - _controller.doInitialLoad(); - } - } - - @override - Widget build(BuildContext context) => PagedValueListenableBuilder( - valueListenable: _controller, - builder: (context, value, _) => value.when( - (items, nextPageKey, error) { - if (items.isEmpty) { - return widget.emptyBuilder(context); - } - - return ListView.separated( - scrollDirection: widget.scrollDirection, - padding: widget.padding, - physics: widget.physics, - reverse: widget.reverse, - controller: widget.scrollController, - primary: widget.primary, - shrinkWrap: widget.shrinkWrap, - addAutomaticKeepAlives: widget.addAutomaticKeepAlives, - addRepaintBoundaries: widget.addRepaintBoundaries, - addSemanticIndexes: widget.addSemanticIndexes, - keyboardDismissBehavior: widget.keyboardDismissBehavior, - restorationId: widget.restorationId, - dragStartBehavior: widget.dragStartBehavior, - cacheExtent: widget.cacheExtent, - clipBehavior: widget.clipBehavior, - itemCount: value.itemCount, - separatorBuilder: (context, index) => - widget.separatorBuilder(context, items, index), - itemBuilder: (context, index) { - if (!_hasRequestedNextPage) { - final newPageRequestTriggerIndex = - items.length - widget.loadMoreTriggerIndex; - final isBuildingTriggerIndexItem = - index == newPageRequestTriggerIndex; - if (nextPageKey != null && isBuildingTriggerIndexItem) { - // Schedules the request for the end of this frame. - WidgetsBinding.instance.addPostFrameCallback((_) async { - if (error == null) { - await _controller.loadMore(nextPageKey); - } - _hasRequestedNextPage = false; - }); - _hasRequestedNextPage = true; - } - } - - if (index == items.length) { - if (error != null) { - return widget.loadMoreErrorBuilder(context, error); - } - return widget.loadMoreIndicatorBuilder(context); - } - - return widget.itemBuilder(context, items, index); - }, - ); - }, - loading: () => widget.loadingBuilder(context), - error: (error) => widget.errorBuilder(context, error), - ), - ); -} - -/// A [GridView] that loads more pages when the user scrolls to the end of the -/// grid. -/// -/// Use [loadMoreTriggerIndex] to set the index of the item that triggers the -/// loading of the next page. -class PagedValueGridView extends StatefulWidget { - /// Creates a new instance of [PagedValueGridView] widget. - const PagedValueGridView({ - super.key, - required this.controller, - required this.gridDelegate, - required this.itemBuilder, - required this.emptyBuilder, - required this.loadMoreErrorBuilder, - required this.loadMoreIndicatorBuilder, - required this.loadingBuilder, - required this.errorBuilder, - this.loadMoreTriggerIndex = 3, - this.scrollDirection = Axis.vertical, - this.reverse = false, - this.scrollController, - this.primary, - this.physics, - this.shrinkWrap = false, - this.padding, - this.addAutomaticKeepAlives = true, - this.addRepaintBoundaries = true, - this.addSemanticIndexes = true, - this.cacheExtent, - this.semanticChildCount, - this.dragStartBehavior = DragStartBehavior.start, - this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, - this.restorationId, - this.clipBehavior = Clip.hardEdge, - }); - - /// The [PagedValueNotifier] used to control the list of items. - final PagedValueNotifier controller; - - /// A delegate that controls the layout of the children within - /// the [PagedValueGridView]. - final SliverGridDelegate gridDelegate; - - /// A builder that is called to build items in the [PagedValueGridView]. - /// - /// The `value` parameter is the [V] at this position in the list. - final PagedValueScrollViewIndexedWidgetBuilder itemBuilder; - - /// A builder that is called to build the empty state of the list. - final WidgetBuilder emptyBuilder; - - /// A builder that is called to build the load more error state of the list. - final PagedValueScrollViewLoadMoreErrorBuilder loadMoreErrorBuilder; - - /// A builder that is called to build the load more indicator of the list. - final WidgetBuilder loadMoreIndicatorBuilder; - - /// A builder that is called to build the loading state of the list. - final WidgetBuilder loadingBuilder; - - /// A builder that is called to build the error state of the list. - final Widget Function(BuildContext, StreamChatError) errorBuilder; - - /// The index to take into account when triggering [controller.loadMore]. - final int loadMoreTriggerIndex; - - /// {@template flutter.widgets.scroll_view.scrollDirection} - /// The axis along which the scroll view scrolls. - /// - /// Defaults to [Axis.vertical]. - /// {@endtemplate} - final Axis scrollDirection; - - /// {@template flutter.widgets.scroll_view.reverse} - /// Whether the scroll view scrolls in the reading direction. - /// - /// For example, if the reading direction is left-to-right and - /// [scrollDirection] is [Axis.horizontal], then the scroll view scrolls from - /// left to right when [reverse] is false and from right to left when - /// [reverse] is true. - /// - /// Similarly, if [scrollDirection] is [Axis.vertical], then the scroll view - /// scrolls from top to bottom when [reverse] is false and from bottom to top - /// when [reverse] is true. - /// - /// Defaults to false. - /// {@endtemplate} - final bool reverse; - - /// {@template flutter.widgets.scroll_view.controller} - /// An object that can be used to control the position to which this scroll - /// view is scrolled. - /// - /// Must be null if [primary] is true. - /// - /// A [ScrollController] serves several purposes. It can be used to control - /// the initial scroll position (see [ScrollController.initialScrollOffset]). - /// It can be used to control whether the scroll view should automatically - /// save and restore its scroll position in the [PageStorage] (see - /// [ScrollController.keepScrollOffset]). It can be used to read the current - /// scroll position (see [ScrollController.offset]), or change it (see - /// [ScrollController.animateTo]). - /// {@endtemplate} - final ScrollController? scrollController; - - /// {@template flutter.widgets.scroll_view.primary} - /// Whether this is the primary scroll view associated with the parent - /// [PrimaryScrollController]. - /// - /// When this is true, the scroll view is scrollable even if it does not have - /// sufficient content to actually scroll. Otherwise, by default the user can - /// only scroll the view if it has sufficient content. See [physics]. - /// - /// Also when true, the scroll view is used for default [ScrollAction]s. If a - /// ScrollAction is not handled by - /// an otherwise focused part of the application, - /// the ScrollAction will be evaluated using this scroll view, for example, - /// when executing [Shortcuts] key events like page up and down. - /// - /// On iOS, this also identifies the scroll view that will scroll to top in - /// response to a tap in the status bar. - /// {@endtemplate} - /// - /// Defaults to true when [scrollDirection] is [Axis.vertical] and - /// [controller] is null. - final bool? primary; - - /// {@template flutter.widgets.scroll_view.physics} - /// How the scroll view should respond to user input. - /// - /// For example, determines how the scroll view continues to animate after the - /// user stops dragging the scroll view. - /// - /// Defaults to matching platform conventions. Furthermore, if [primary] is - /// false, then the user cannot scroll if there is insufficient content to - /// scroll, while if [primary] is true, they can always attempt to scroll. - /// - /// To force the scroll view to always be scrollable even if there is - /// insufficient content, as if [primary] was true but without necessarily - /// setting it to true, provide an [AlwaysScrollableScrollPhysics] physics - /// object, as in: - /// - /// ```dart - /// physics: const AlwaysScrollableScrollPhysics(), - /// ``` - /// - /// To force the scroll view to use the default platform conventions and not - /// be scrollable if there is insufficient content, regardless of the value of - /// [primary], provide an explicit [ScrollPhysics] object, as in: - /// - /// ```dart - /// physics: const ScrollPhysics(), - /// ``` - /// - /// The physics can be changed dynamically (by providing a new object in a - /// subsequent build), but new physics will only take effect if the _class_ of - /// the provided object changes. Merely constructing a new instance with a - /// different configuration is insufficient to cause the physics to be - /// reapplied. (This is because the final object used is generated - /// dynamically, which can be relatively expensive, and it would be - /// inefficient to speculatively create this object each frame to see if the - /// physics should be updated.) - /// {@endtemplate} - /// - /// If an explicit [ScrollBehavior] is provided to [scrollBehavior], the - /// [ScrollPhysics] provided by that behavior will take precedence after - /// [physics]. - final ScrollPhysics? physics; - - /// {@template flutter.widgets.scroll_view.shrinkWrap} - /// Whether the extent of the scroll view in the [scrollDirection] should be - /// determined by the contents being viewed. - /// - /// If the scroll view does not shrink wrap, then the scroll view will expand - /// to the maximum allowed size in the [scrollDirection]. If the scroll view - /// has unbounded constraints in the [scrollDirection], then [shrinkWrap] must - /// be true. - /// - /// Shrink wrapping the content of the scroll view is significantly more - /// expensive than expanding to the maximum allowed size because the content - /// can expand and contract during scrolling, which means the size of the - /// scroll view needs to be recomputed whenever the scroll position changes. - /// - /// Defaults to false. - /// {@endtemplate} - final bool shrinkWrap; - - /// The amount of space by which to inset the children. - final EdgeInsetsGeometry? padding; - - /// Whether to wrap each child in an [AutomaticKeepAlive]. - /// - /// Typically, children in lazy list are wrapped in [AutomaticKeepAlive] - /// widgets so that children can use [KeepAliveNotification]s to preserve - /// their state when they would otherwise be garbage collected off-screen. - /// - /// This feature (and [addRepaintBoundaries]) must be disabled if the children - /// are going to manually maintain their [KeepAlive] state. It may also be - /// more efficient to disable this feature if it is known ahead of time that - /// none of the children will ever try to keep themselves alive. - /// - /// Defaults to true. - final bool addAutomaticKeepAlives; - - /// Whether to wrap each child in a [RepaintBoundary]. - /// - /// Typically, children in a scrolling container are wrapped in repaint - /// boundaries so that they do not need to be repainted as the list scrolls. - /// If the children are easy to repaint (e.g., solid color blocks or a short - /// snippet of text), it might be more efficient to not add a repaint boundary - /// and simply repaint the children during scrolling. - /// - /// Defaults to true. - final bool addRepaintBoundaries; - - /// Whether to wrap each child in an [IndexedSemantics]. - /// - /// Typically, children in a scrolling container must be annotated with a - /// semantic index in order to generate the correct accessibility - /// announcements. This should only be set to false if the indexes have - /// already been provided by an [IndexedSemantics] widget. - /// - /// Defaults to true. - /// - /// See also: - /// - /// * [IndexedSemantics], for an explanation of how to manually - /// provide semantic indexes. - final bool addSemanticIndexes; - - /// {@macro flutter.rendering.RenderViewportBase.cacheExtent} - final double? cacheExtent; - - /// The number of children that will contribute semantic information. - /// - /// Some subtypes of [ScrollView] can infer this value automatically. For - /// example [ListView] will use the number of widgets in the child list, - /// while the [ListView.separated] constructor will use half that amount. - /// - /// For [CustomScrollView] and other types which do not receive a builder - /// or list of widgets, the child count must be explicitly provided. If the - /// number is unknown or unbounded this should be left unset or set to null. - /// - /// See also: - /// - /// * [SemanticsConfiguration.scrollChildCount], the corresponding - /// semantics property. - final int? semanticChildCount; - - /// {@macro flutter.widgets.scrollable.dragStartBehavior} - final DragStartBehavior dragStartBehavior; - - /// {@template flutter.widgets.scroll_view.keyboardDismissBehavior} - /// [ScrollViewKeyboardDismissBehavior] the defines how this [ScrollView] will - /// dismiss the keyboard automatically. - /// {@endtemplate} - final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior; - - /// {@macro flutter.widgets.scrollable.restorationId} - final String? restorationId; - - /// {@macro flutter.material.Material.clipBehavior} - /// - /// Defaults to [Clip.hardEdge]. - final Clip clipBehavior; - - @override - State> createState() => - _PagedValueGridViewState(); -} - -class _PagedValueGridViewState extends State> { - PagedValueNotifier get _controller => widget.controller; - - // Avoids duplicate requests on rebuilds. - bool _hasRequestedNextPage = false; - - @override - void initState() { - super.initState(); - _controller.doInitialLoad(); - } - - @override - void didUpdateWidget(covariant PagedValueGridView oldWidget) { - super.didUpdateWidget(oldWidget); - if (_controller != oldWidget.controller) { - // reset duplicate requests flag - _hasRequestedNextPage = false; - _controller.doInitialLoad(); - } - } - - @override - Widget build(BuildContext context) => PagedValueListenableBuilder( - valueListenable: _controller, - builder: (context, value, _) => value.when( - (items, nextPageKey, error) { - if (items.isEmpty) { - return widget.emptyBuilder(context); - } - - return GridView.builder( - scrollDirection: widget.scrollDirection, - reverse: widget.reverse, - controller: widget.scrollController, - primary: widget.primary, - physics: widget.physics, - shrinkWrap: widget.shrinkWrap, - padding: widget.padding, - addAutomaticKeepAlives: widget.addAutomaticKeepAlives, - addRepaintBoundaries: widget.addRepaintBoundaries, - addSemanticIndexes: widget.addSemanticIndexes, - cacheExtent: widget.cacheExtent, - semanticChildCount: widget.semanticChildCount, - dragStartBehavior: widget.dragStartBehavior, - keyboardDismissBehavior: widget.keyboardDismissBehavior, - restorationId: widget.restorationId, - clipBehavior: widget.clipBehavior, - itemCount: value.itemCount, - gridDelegate: widget.gridDelegate, - itemBuilder: (context, index) { - if (!_hasRequestedNextPage) { - final newPageRequestTriggerIndex = - items.length - widget.loadMoreTriggerIndex; - final isBuildingTriggerIndexItem = - index == newPageRequestTriggerIndex; - if (nextPageKey != null && isBuildingTriggerIndexItem) { - // Schedules the request for the end of this frame. - WidgetsBinding.instance.addPostFrameCallback((_) async { - if (error == null) { - await _controller.loadMore(nextPageKey); - } - _hasRequestedNextPage = false; - }); - _hasRequestedNextPage = true; - } - } - - if (index == items.length) { - if (error != null) { - return widget.loadMoreErrorBuilder(context, error); - } - return widget.loadMoreIndicatorBuilder(context); - } - - return widget.itemBuilder(context, items, index); - }, - ); - }, - loading: () => widget.loadingBuilder(context), - error: (error) => widget.errorBuilder(context, error), - ), - ); -} diff --git a/packages/stream_chat_flutter_core/lib/src/stream_channel.dart b/packages/stream_chat_flutter_core/lib/src/stream_channel.dart deleted file mode 100644 index 7c65984eda..0000000000 --- a/packages/stream_chat_flutter_core/lib/src/stream_channel.dart +++ /dev/null @@ -1,501 +0,0 @@ -import 'dart:async'; - -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:rxdart/rxdart.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_flutter_core/src/stream_controller_extension.dart'; - -/// Specifies query direction for pagination -enum QueryDirection { - /// Query earlier messages - top, - - /// Query later messages - bottom, -} - -/// Signature used by [StreamChannel.errorBuilder] to create a replacement -/// widget for an error that occurs while asynchronously building the channel. -// TODO: Remove once ErrorBuilder supports passing stacktrace. -typedef ErrorWidgetBuilder = Widget Function( - BuildContext context, - Object error, - StackTrace? stackTrace, -); - -/// Widget used to provide information about the channel to the widget tree -/// -/// Use [StreamChannel.of] to get the current [StreamChannelState] instance. -class StreamChannel extends StatefulWidget { - /// Creates a new instance of [StreamChannel]. Both [child] and [channel] must - /// be supplied and not null. - const StreamChannel({ - super.key, - required this.child, - required this.channel, - this.showLoading = true, - this.initialMessageId, - this.errorBuilder = _defaultErrorBuilder, - this.loadingBuilder = _defaultLoadingBuilder, - }); - - /// The child of the widget - final Widget child; - - /// [channel] specifies the channel with which child should be wrapped - final Channel channel; - - /// Shows a loading indicator - final bool showLoading; - - /// If passed the channel will load from this particular message. - final String? initialMessageId; - - /// Widget builder used in case the channel is initialising. - final WidgetBuilder loadingBuilder; - - /// Widget builder used in case an error occurs while building the channel. - final ErrorWidgetBuilder errorBuilder; - - static Widget _defaultLoadingBuilder(BuildContext context) { - return const Center(child: CircularProgressIndicator.adaptive()); - } - - static Widget _defaultErrorBuilder( - BuildContext context, - Object error, - StackTrace? stackTrace, - ) { - if (error is DioException) { - if (error.type == DioExceptionType.badResponse) { - return Center(child: Text(error.message ?? 'Bad response')); - } - return const Center(child: Text('Check your connection and retry')); - } - - return Center(child: Text(error.toString())); - } - - /// Use this method to get the current [StreamChannelState] instance - static StreamChannelState of(BuildContext context) { - StreamChannelState? streamChannelState; - - streamChannelState = context.findAncestorStateOfType(); - - assert( - streamChannelState != null, - 'You must have a StreamChannel widget at the top of your widget tree', - ); - - return streamChannelState!; - } - - @override - StreamChannelState createState() => StreamChannelState(); -} - -// ignore: public_member_api_docs -class StreamChannelState extends State { - /// Current channel - Channel get channel => widget.channel; - - /// InitialMessageId - String? get initialMessageId => widget.initialMessageId; - - /// Current channel state stream - Stream? get channelStateStream => - widget.channel.state?.channelStateStream; - - final _queryTopMessagesController = BehaviorSubject.seeded(false); - final _queryBottomMessagesController = BehaviorSubject.seeded(false); - - /// The stream notifying the state of [_queryTopMessages] call - Stream get queryTopMessages => _queryTopMessagesController.stream; - - /// The stream notifying the state of [_queryBottomMessages] call - Stream get queryBottomMessages => _queryBottomMessagesController.stream; - - bool _topPaginationEnded = false; - bool _bottomPaginationEnded = false; - - Future _queryTopMessages({ - int limit = 20, - bool preferOffline = false, - }) async { - if (_topPaginationEnded || - _queryTopMessagesController.value || - channel.state == null) { - return; - } - _queryTopMessagesController.safeAdd(true); - - if (channel.state!.messages.isEmpty) { - return _queryTopMessagesController.safeAdd(false); - } - - final oldestMessage = channel.state!.messages.first; - - try { - final state = await queryBeforeMessage( - oldestMessage.id, - limit: limit, - preferOffline: preferOffline, - ); - if (state.messages == null || - state.messages!.isEmpty || - state.messages!.length < limit) { - _topPaginationEnded = true; - } - _queryTopMessagesController.safeAdd(false); - } catch (e, stk) { - _queryTopMessagesController.safeAddError(e, stk); - } - } - - Future _queryBottomMessages({ - int limit = 20, - bool preferOffline = false, - }) async { - if (_bottomPaginationEnded || - _queryBottomMessagesController.value || - channel.state == null || - channel.state!.isUpToDate) { - return; - } - _queryBottomMessagesController.safeAdd(true); - - if (channel.state!.messages.isEmpty) { - return _queryBottomMessagesController.safeAdd(false); - } - - final recentMessage = channel.state!.messages.last; - - try { - final state = await queryAfterMessage( - recentMessage.id, - limit: limit, - preferOffline: preferOffline, - ); - if (state.messages == null || - state.messages!.isEmpty || - state.messages!.length < limit) { - _bottomPaginationEnded = true; - } - _queryBottomMessagesController.safeAdd(false); - } catch (e, stk) { - _queryBottomMessagesController.safeAddError(e, stk); - } - } - - /// Calls [channel.query] updating [queryMessage] stream - Future queryMessages({ - QueryDirection? direction = QueryDirection.top, - int limit = 20, - }) { - if (direction == QueryDirection.top) { - return _queryTopMessages(limit: limit); - } - return _queryBottomMessages(limit: limit); - } - - /// Calls [channel.getReplies] updating [queryMessage] stream - Future getReplies( - String parentId, { - int limit = 50, - bool preferOffline = false, - }) async { - if (_topPaginationEnded || - _queryTopMessagesController.value || - channel.state == null) { - return; - } - _queryTopMessagesController.safeAdd(true); - - Message? message; - if (channel.state!.threads.containsKey(parentId)) { - final thread = channel.state!.threads[parentId]!; - if (thread.isNotEmpty) { - message = thread.first; - } - } - - try { - final response = await channel.getReplies( - parentId, - options: PaginationParams( - lessThan: message?.id, - limit: limit, - ), - preferOffline: preferOffline, - ); - if (response.messages.isEmpty || response.messages.length < limit) { - _topPaginationEnded = true; - } - _queryTopMessagesController.safeAdd(false); - } catch (e, stk) { - _queryTopMessagesController.safeAddError(e, stk); - } - } - - /// Query the channel members and watchers - Future queryMembersAndWatchers() async { - final _members = channel.state?.members; - if (_members != null) { - await widget.channel.query( - membersPagination: PaginationParams( - offset: _members.length, - limit: 100, - ), - watchersPagination: PaginationParams( - offset: _members.length, - limit: 100, - ), - ); - } else { - return; - } - } - - /// Loads channel at specific message - Future loadChannelAtMessage( - String? messageId, { - int limit = 20, - bool preferOffline = false, - }) => - _queryAtMessage( - messageId: messageId, - limit: limit, - preferOffline: preferOffline, - ); - - /// Loads channel at specific message - Future loadChannelAtTimestamp( - DateTime timestamp, { - int limit = 40, - bool preferOffline = false, - }) => - _queryAtTimestamp( - timestamp: timestamp, - limit: limit, - preferOffline: preferOffline, - ); - - Future _queryAtMessage({ - String? messageId, - int limit = 40, - bool preferOffline = false, - }) async { - if (channel.state == null) return null; - channel.state!.isUpToDate = false; - channel.state!.truncate(); - - if (messageId == null) { - final state = await channel.query( - messagesPagination: PaginationParams( - limit: limit, - ), - preferOffline: preferOffline, - ); - channel.state!.isUpToDate = true; - return state; - } - - return channel.query( - messagesPagination: PaginationParams( - idAround: messageId, - limit: limit, - ), - preferOffline: preferOffline, - ); - } - - Future _queryAtTimestamp({ - required DateTime timestamp, - int limit = 40, - bool preferOffline = false, - }) async { - if (channel.state == null) return null; - channel.state!.isUpToDate = false; - channel.state!.truncate(); - - return channel.query( - messagesPagination: PaginationParams( - createdAtAround: timestamp.toUtc(), - limit: limit, - ), - preferOffline: preferOffline, - ); - } - - /// - Future queryBeforeMessage( - String messageId, { - int limit = 20, - bool preferOffline = false, - }) => - channel.query( - messagesPagination: PaginationParams( - lessThan: messageId, - limit: limit, - ), - preferOffline: preferOffline, - ); - - /// - Future queryAfterMessage( - String messageId, { - int limit = 20, - bool preferOffline = false, - }) async { - final state = await channel.query( - messagesPagination: PaginationParams( - greaterThanOrEqual: messageId, - limit: limit, - ), - preferOffline: preferOffline, - ); - if (state.messages == null || - state.messages!.isEmpty || - state.messages!.length < limit) { - channel.state?.isUpToDate = true; - } - return state; - } - - /// - Future getMessage(String messageId) async { - var message = channel.state?.messages.firstWhereOrNull( - (it) => it.id == messageId, - ); - if (message == null) { - final response = await channel.getMessagesById([messageId]); - message = response.messages.first; - } - return message; - } - - /// Query channel members. - Future> queryMembers({ - Filter? filter, - List? sort, - PaginationParams? pagination, - }) async { - final response = await channel.queryMembers( - filter: filter, - sort: sort, - pagination: pagination, - ); - return response.members; - } - - /// Reloads the channel with latest message - Future reloadChannel() => _queryAtMessage(limit: 30); - - late List> _futures; - - Future get _loadChannelAtMessage async { - try { - await loadChannelAtMessage(initialMessageId); - return true; - } catch (_) { - rethrow; - } - } - - Future _loadChannelAtTimestamp(DateTime timestamp) async { - try { - await loadChannelAtTimestamp(timestamp); - return true; - } catch (_) { - rethrow; - } - } - - @override - void initState() { - super.initState(); - _populateFutures(); - - // Start watching the channel if it's not yet initialized. - if (channel.state == null) channel.watch(); - } - - void _populateFutures() { - _futures = [channel.initialized]; - if (initialMessageId != null) { - _futures.add(_loadChannelAtMessage); - } else if (channel.state != null && channel.state!.unreadCount > 0) { - final read = channel.state!.read.firstWhereOrNull( - (it) => it.user.id == channel.client.state.currentUser?.id, - ); - - if (read == null) return; - - final messages = channel.state!.messages; - final lastRead = read.lastRead; - - final hasNewMessages = - messages.any((it) => it.createdAt.isAfter(lastRead)); - final hasOldMessages = - messages.any((it) => it.createdAt.isBeforeOrEqualTo(lastRead)); - - // Only load messages if the unread message is in-between the messages. - // Otherwise, we can just load the channel normally. - if (hasNewMessages && hasOldMessages) { - _futures.add(_loadChannelAtTimestamp(lastRead)); - } - } - } - - @override - void didUpdateWidget(covariant StreamChannel oldWidget) { - if (oldWidget.channel != channel || - oldWidget.initialMessageId != initialMessageId) { - _populateFutures(); - - // Start watching the channel if it's not yet initialized. - if (channel.state == null) channel.watch(); - } - super.didUpdateWidget(oldWidget); - } - - @override - void dispose() { - _queryTopMessagesController.close(); - _queryBottomMessagesController.close(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Material( - child: FutureBuilder>( - future: Future.wait(_futures), - initialData: [ - channel.state != null, - _futures.length == 1, - ], - builder: (context, snapshot) { - if (snapshot.hasError) { - final error = snapshot.error!; - final stackTrace = snapshot.stackTrace; - return widget.errorBuilder(context, error, stackTrace); - } - - final dataLoaded = snapshot.data?.every((it) => it) == true; - if (widget.showLoading && !dataLoaded) { - return widget.loadingBuilder(context); - } - return widget.child; - }, - ), - ); - } -} - -extension on DateTime { - bool isBeforeOrEqualTo(DateTime other) { - return isBefore(other) || isAtSameMomentAs(other); - } -} diff --git a/packages/stream_chat_flutter_core/lib/src/stream_channel_list_controller.dart b/packages/stream_chat_flutter_core/lib/src/stream_channel_list_controller.dart deleted file mode 100644 index 0d61e06d2b..0000000000 --- a/packages/stream_chat_flutter_core/lib/src/stream_channel_list_controller.dart +++ /dev/null @@ -1,280 +0,0 @@ -import 'dart:async'; -import 'dart:math'; - -import 'package:stream_chat/stream_chat.dart' hide Success; -import 'package:stream_chat_flutter_core/src/paged_value_notifier.dart'; -import 'package:stream_chat_flutter_core/src/stream_channel_list_event_handler.dart'; - -/// The default channel page limit to load. -const defaultChannelPagedLimit = 10; - -const _kDefaultBackendPaginationLimit = 30; - -/// A controller for a Channel list. -/// -/// This class lets you perform tasks such as: -/// * Load initial data. -/// * Use channel events handlers. -/// * Load more data using [loadMore]. -/// * Replace the previously loaded channels. -/// * Return/Create a new channel and start watching it. -/// * Pause and Resume all subscriptions added to this composite. -class StreamChannelListController extends PagedValueNotifier { - /// Creates a Stream channel list controller. - /// - /// * `client` is the Stream chat client to use for the channels list. - /// - /// * `channelEventHandlers` is the channel events to use for the channels - /// list. This class can be mixed in or extended to create custom overrides. - /// See [StreamChannelListEventHandler] for advice. - /// - /// * `filter` is the query filters to use. - /// - /// * `sort` is the sorting used for the channels matching the filters. - /// - /// * `presence` sets whether you'll receive user presence updates via the - /// websocket events. - /// - /// * `limit` is the limit to apply to the channel list. - /// - /// * `messageLimit` is the number of messages to fetch in each channel. - /// - /// * `memberLimit` is the number of members to fetch in each channel. - StreamChannelListController({ - required this.client, - StreamChannelListEventHandler? eventHandler, - this.filter, - this.channelStateSort, - this.presence = true, - this.limit = defaultChannelPagedLimit, - this.messageLimit, - this.memberLimit, - }) : _eventHandler = eventHandler ?? StreamChannelListEventHandler(), - super(const PagedValue.loading()); - - /// Creates a [StreamChannelListController] from the passed [value]. - StreamChannelListController.fromValue( - super.value, { - required this.client, - StreamChannelListEventHandler? eventHandler, - this.filter, - this.channelStateSort, - this.presence = true, - this.limit = defaultChannelPagedLimit, - this.messageLimit, - this.memberLimit, - }) : _eventHandler = eventHandler ?? StreamChannelListEventHandler(); - - /// The client to use for the channels list. - final StreamChatClient client; - - /// The channel event handlers to use for the channels list. - final StreamChannelListEventHandler _eventHandler; - - /// The query filters to use. - /// - /// You can query on any of the custom fields you've defined on the [Channel]. - /// - /// You can also filter other built-in channel fields. - final Filter? filter; - - /// The sorting used for the channels matching the filters. - /// - /// Sorting is based on field and direction, multiple sorting options - /// can be provided. - /// - /// You can sort based on last_updated, last_message_at, updated_at, - /// created_at or member_count. - /// - /// Direction can be ascending or descending. - final List>? channelStateSort; - - /// If true you’ll receive user presence updates via the websocket events - final bool presence; - - /// The limit to apply to the channel list. The default is set to - /// [defaultChannelPagedLimit]. - final int limit; - - /// Number of messages to fetch in each channel. - final int? messageLimit; - - /// Number of members to fetch in each channel. - final int? memberLimit; - - @override - Future doInitialLoad() async { - final limit = min( - this.limit * defaultInitialPagedLimitMultiplier, - _kDefaultBackendPaginationLimit, - ); - try { - await for (final channels in client.queryChannels( - filter: filter, - channelStateSort: channelStateSort, - memberLimit: memberLimit, - messageLimit: messageLimit, - presence: presence, - paginationParams: PaginationParams(limit: limit), - )) { - final nextKey = channels.length < limit ? null : channels.length; - value = PagedValue( - items: channels, - nextPageKey: nextKey, - ); - } - // start listening to events - _subscribeToChannelListEvents(); - } on StreamChatError catch (error) { - value = PagedValue.error(error); - } catch (error) { - final chatError = StreamChatError(error.toString()); - value = PagedValue.error(chatError); - } - } - - @override - Future loadMore(int nextPageKey) async { - final previousValue = value.asSuccess; - - try { - await for (final channels in client.queryChannels( - filter: filter, - channelStateSort: channelStateSort, - memberLimit: memberLimit, - messageLimit: messageLimit, - presence: presence, - paginationParams: PaginationParams(limit: limit, offset: nextPageKey), - )) { - final previousItems = previousValue.items; - final newItems = previousItems + channels; - final nextKey = channels.length < limit ? null : newItems.length; - value = PagedValue( - items: newItems, - nextPageKey: nextKey, - ); - } - } on StreamChatError catch (error) { - value = previousValue.copyWith(error: error); - } catch (error) { - final chatError = StreamChatError(error.toString()); - value = previousValue.copyWith(error: chatError); - } - } - - /// Replaces the previously loaded channels with the passed [channels]. - set channels(List channels) { - if (value.isSuccess) { - final currentValue = value.asSuccess; - value = currentValue.copyWith(items: channels); - } else { - value = PagedValue(items: channels); - } - } - - /// Returns/Creates a new Channel and starts watching it. - Future getChannel({ - required String id, - required String type, - }) async { - final channel = client.channel(type, id: id); - await channel.watch(); - return channel; - } - - /// Leaves the [channel] and updates the list. - Future leaveChannel(Channel channel) async { - final user = client.state.currentUser; - assert(user != null, 'You must be logged in to leave a channel.'); - await channel.removeMembers([user!.id]); - } - - /// Deletes the [channel] and updates the list. - Future deleteChannel(Channel channel) async { - await channel.delete(); - } - - /// Mutes the [channel] and updates the list. - Future muteChannel(Channel channel) async { - await channel.mute(); - } - - /// Un-mutes the [channel] and updates the list. - Future unmuteChannel(Channel channel) async { - await channel.unmute(); - } - - /// Event listener, which can be set in order to listen - /// [client] web-socket events. - /// - /// Return `true` if the event is handled. Return `false` to - /// allow the event to be handled internally. - bool Function(Event event)? eventListener; - - StreamSubscription? _channelEventSubscription; - - // Subscribes to the channel list events. - void _subscribeToChannelListEvents() { - if (_channelEventSubscription != null) { - _unsubscribeFromChannelListEvents(); - } - - _channelEventSubscription = client.on().listen((event) { - // Only handle the event if the value is in success state. - if (value.isNotSuccess) return; - - // Returns early if the event is already handled by the listener. - if (eventListener?.call(event) ?? false) return; - - final eventType = event.type; - if (eventType == EventType.channelDeleted) { - _eventHandler.onChannelDeleted(event, this); - } else if (eventType == EventType.channelHidden) { - _eventHandler.onChannelHidden(event, this); - } else if (eventType == EventType.channelTruncated) { - _eventHandler.onChannelTruncated(event, this); - } else if (eventType == EventType.channelUpdated) { - _eventHandler.onChannelUpdated(event, this); - } else if (eventType == EventType.channelVisible) { - _eventHandler.onChannelVisible(event, this); - } else if (eventType == EventType.connectionRecovered) { - _eventHandler.onConnectionRecovered(event, this); - } else if (eventType == EventType.messageNew) { - _eventHandler.onMessageNew(event, this); - } else if (eventType == EventType.notificationAddedToChannel) { - _eventHandler.onNotificationAddedToChannel(event, this); - } else if (eventType == EventType.notificationMessageNew) { - _eventHandler.onNotificationMessageNew(event, this); - } else if (eventType == EventType.notificationRemovedFromChannel) { - _eventHandler.onNotificationRemovedFromChannel(event, this); - } else if (eventType == 'user.presence.changed' || - eventType == EventType.userUpdated) { - _eventHandler.onUserPresenceChanged(event, this); - } - }); - } - - // Unsubscribes from all channel list events. - void _unsubscribeFromChannelListEvents() { - if (_channelEventSubscription != null) { - _channelEventSubscription!.cancel(); - _channelEventSubscription = null; - } - } - - /// Pauses all subscriptions added to this composite. - void pauseEventsSubscription([Future? resumeSignal]) { - _channelEventSubscription?.pause(resumeSignal); - } - - /// Resumes all subscriptions added to this composite. - void resumeEventsSubscription() { - _channelEventSubscription?.resume(); - } - - @override - void dispose() { - _unsubscribeFromChannelListEvents(); - super.dispose(); - } -} diff --git a/packages/stream_chat_flutter_core/lib/src/stream_channel_list_event_handler.dart b/packages/stream_chat_flutter_core/lib/src/stream_channel_list_event_handler.dart deleted file mode 100644 index 1437bddb7b..0000000000 --- a/packages/stream_chat_flutter_core/lib/src/stream_channel_list_event_handler.dart +++ /dev/null @@ -1,204 +0,0 @@ -import 'package:stream_chat/stream_chat.dart' show ChannelState, Event; -import 'package:stream_chat_flutter_core/src/stream_channel_list_controller.dart'; - -/// Contains handlers that are called from [StreamChannelListController] for -/// certain [Event]s. -/// -/// This class can be mixed in or extended to create custom overrides. -mixin class StreamChannelListEventHandler { - /// Function which gets called for the event - /// [EventType.channelDeleted]. - /// - /// This event is fired when a channel is deleted. - /// - /// By default, this removes the channel from the list of channels. - void onChannelDeleted(Event event, StreamChannelListController controller) { - final channels = [...controller.currentItems]; - - final updatedChannels = channels - ..removeWhere( - (it) => it.cid == (event.cid ?? event.channel?.cid), - ); - - controller.channels = updatedChannels; - } - - /// Function which gets called for the event - /// [EventType.channelHidden]. - /// - /// This event is fired when a channel is hidden. - /// - /// By default, this removes the channel from the list of channels. - void onChannelHidden(Event event, StreamChannelListController controller) { - onChannelDeleted(event, controller); - } - - /// Function which gets called for the event - /// [EventType.channelTruncated]. - /// - /// This event is fired when a channel is truncated. - /// - /// By default, this refreshes the whole channel list. - void onChannelTruncated(Event event, StreamChannelListController controller) { - controller.refresh(); - } - - /// Function which gets called for the event - /// [EventType.channelUpdated]. - /// - /// This event is fired when a channel is updated. - /// - /// By default, this updates the channel received in the event. - // ignore: no-empty-block - void onChannelUpdated(Event event, StreamChannelListController controller) {} - - /// Function which gets called for the event - /// [EventType.channelVisible]. - /// - /// This event is fired when a channel is made visible. - /// - /// By default, this adds the channel to the list of channels. - void onChannelVisible( - Event event, - StreamChannelListController controller, - ) async { - final channelId = event.channelId; - final channelType = event.channelType; - - if (channelId == null || channelType == null) return; - - final channel = await controller.getChannel( - id: channelId, - type: channelType, - ); - - final currentChannels = [...controller.currentItems]; - - final updatedChannels = [ - channel, - ...currentChannels..removeWhere((it) => it.cid == channel.cid), - ]; - - controller.channels = updatedChannels; - } - - /// Function which gets called for the event - /// [EventType.connectionRecovered]. - /// - /// This event is fired when the client web-socket connection recovers. - /// - /// By default, this refreshes the whole channel list. - void onConnectionRecovered( - Event event, - StreamChannelListController controller, - ) { - controller.refresh(); - } - - /// Function which gets called for the event [EventType.messageNew]. - /// - /// This event is fired when a new message is created in one of the channels - /// we are currently watching. - /// - /// By default, this moves the channel to the top of the list. - void onMessageNew(Event event, StreamChannelListController controller) async { - final channelCid = event.cid; - if (channelCid == null) return; - - final channels = [...controller.currentItems]; - - final channelIndex = channels.indexWhere((it) => it.cid == channelCid); - if (channelIndex < 0) { - // If the channel is not in the list, It might be hidden. - // So, we just refresh the list. - await controller.refresh(resetValue: false); - return; - } - - final channel = channels.removeAt(channelIndex); - channels.insert(0, channel); - - controller.channels = [...channels]; - } - - /// Function which gets called for the event - /// [EventType.notificationAddedToChannel]. - /// - /// This event is fired when a channel is added which we are not watching. - /// - /// By default, this adds the channel and moves it to the top of list. - void onNotificationAddedToChannel( - Event event, - StreamChannelListController controller, - ) { - onChannelVisible(event, controller); - } - - /// Function which gets called for the event - /// [EventType.notificationMessageNew]. - /// - /// This event is fired when a new message is created in a channel - /// which we are not currently watching. - /// - /// By default, this adds the channel and moves it to the top of list. - void onNotificationMessageNew( - Event event, - StreamChannelListController controller, - ) { - onChannelVisible(event, controller); - } - - /// Function which gets called for the event - /// [EventType.notificationRemovedFromChannel]. - /// - /// This event is fired when a user is removed from a channel which we are - /// not currently watching. - /// - /// By default, this removes the event channel from the list. - void onNotificationRemovedFromChannel( - Event event, - StreamChannelListController controller, - ) { - final channels = [...controller.currentItems]; - final updatedChannels = - channels.where((it) => it.cid != event.channel?.cid); - final listChanged = channels.length != updatedChannels.length; - - if (!listChanged) return; - - controller.channels = [...updatedChannels]; - } - - /// Function which gets called for the event - /// 'user.presence.changed' and [EventType.userUpdated]. - /// - /// This event is fired when a user's presence changes or gets updated. - /// - /// By default, this updates the channel member with the event user. - void onUserPresenceChanged( - Event event, - StreamChannelListController controller, - ) { - final user = event.user; - if (user == null) return; - - final channels = [...controller.currentItems]; - - final updatedChannels = channels.map((channel) { - final members = [...channel.state!.members]; - final memberIndex = members.indexWhere( - (it) => user.id == (it.userId ?? it.user?.id), - ); - - if (memberIndex < 0) return channel; - - members[memberIndex] = members[memberIndex].copyWith(user: user); - final updatedState = ChannelState(members: [...members]); - channel.state!.updateChannelState(updatedState); - - return channel; - }); - - controller.channels = [...updatedChannels]; - } -} diff --git a/packages/stream_chat_flutter_core/lib/src/stream_chat_core.dart b/packages/stream_chat_flutter_core/lib/src/stream_chat_core.dart deleted file mode 100644 index 33004696b0..0000000000 --- a/packages/stream_chat_flutter_core/lib/src/stream_chat_core.dart +++ /dev/null @@ -1,219 +0,0 @@ -import 'dart:async'; - -import 'package:connectivity_plus/connectivity_plus.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_flutter_core/src/typedef.dart'; - -/// Widget used to provide information about the chat to the widget tree. -/// This Widget is used to react to life cycle changes and system updates. -/// When the app goes into the background, the websocket connection is kept -/// alive for two minutes before being terminated. -/// -/// Conversely, when app is resumed or restarted, a new connection is initiated. -/// -/// ```dart -/// class MyApp extends StatelessWidget { -/// final StreamChatClient client; -/// -/// MyApp(this.client); -/// -/// @override -/// Widget build(BuildContext context) { -/// return MaterialApp( -/// home: Container( -/// child: StreamChatCore( -/// client: client, -/// child: ChannelListPage(), -/// ), -/// ), -/// ); -/// } -/// } -/// ``` -/// -class StreamChatCore extends StatefulWidget { - /// Constructor used for creating a new instance of [StreamChatCore]. - /// - /// [StreamChatCore] is a stateful widget which reacts to system events and - /// updates Stream's connection status accordingly. - const StreamChatCore({ - super.key, - required this.client, - required this.child, - this.onBackgroundEventReceived, - this.backgroundKeepAlive = const Duration(minutes: 1), - this.connectivityStream, - }); - - /// Instance of Stream Chat Client containing information about the current - /// application. - final StreamChatClient client; - - /// Widget descendant. - final Widget child; - - /// The amount of time that will pass before disconnecting the client in - /// the background - final Duration backgroundKeepAlive; - - /// Handler called whenever the [client] receives a new [Event] while the app - /// is in background. Can be used to display various notifications depending - /// upon the [Event.type] - final EventHandler? onBackgroundEventReceived; - - /// Stream of connectivity result - /// Visible for testing - @visibleForTesting - final Stream>? connectivityStream; - - @override - StreamChatCoreState createState() => StreamChatCoreState(); - - /// Use this method to get the current [StreamChatCoreState] instance - static StreamChatCoreState of(BuildContext context) { - StreamChatCoreState? streamChatState; - - streamChatState = context.findAncestorStateOfType(); - - assert( - streamChatState != null, - 'You must have a StreamChat widget at the top of your widget tree', - ); - - return streamChatState!; - } -} - -/// State class associated with [StreamChatCore]. -class StreamChatCoreState extends State - with WidgetsBindingObserver { - /// Initialized client used throughout the application. - StreamChatClient get client => widget.client; - - Timer? _disconnectTimer; - - @override - Widget build(BuildContext context) { - StreamChatClient.additionalHeaders = { - 'X-Stream-Client': '${StreamChatClient.defaultUserAgent}-' - 'core-${StreamChatClient.packageVersion}', - }; - return widget.child; - } - - /// The current user - User? get currentUser => client.state.currentUser; - - /// The current user as a stream - Stream get currentUserStream => client.state.currentUserStream; - - StreamSubscription>? _connectivitySubscription; - - var _isInForeground = true; - var _isConnectionAvailable = true; - - @override - void initState() { - super.initState(); - WidgetsBinding.instance.addObserver(this); - _subscribeToConnectivityChange(widget.connectivityStream); - } - - void _subscribeToConnectivityChange([ - Stream>? connectivityStream, - ]) { - if (_connectivitySubscription == null) { - connectivityStream ??= Connectivity().onConnectivityChanged; - _connectivitySubscription = - connectivityStream.distinct().listen((result) { - _isConnectionAvailable = !result.contains(ConnectivityResult.none); - if (!_isInForeground) return; - if (_isConnectionAvailable) { - if (client.wsConnectionStatus == ConnectionStatus.disconnected && - currentUser != null) { - client.openConnection(); - } - } else { - if (client.wsConnectionStatus == ConnectionStatus.connected) { - client.closeConnection(); - } - } - }); - } - } - - void _unsubscribeFromConnectivityChange() { - if (_connectivitySubscription != null) { - _connectivitySubscription?.cancel(); - _connectivitySubscription = null; - } - } - - @override - void didUpdateWidget(StreamChatCore oldWidget) { - super.didUpdateWidget(oldWidget); - final connectivityStream = widget.connectivityStream; - if (connectivityStream != oldWidget.connectivityStream) { - _unsubscribeFromConnectivityChange(); - _subscribeToConnectivityChange(connectivityStream); - } - } - - StreamSubscription? _eventSubscription; - - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - _isInForeground = [ - AppLifecycleState.resumed, - AppLifecycleState.inactive, - ].contains(state); - if (currentUser != null) { - if (_isInForeground) { - _onForeground(); - } else { - _onBackground(); - } - } - } - - void _onForeground() { - if (_disconnectTimer?.isActive == true) { - _eventSubscription?.cancel(); - _disconnectTimer?.cancel(); - } else if (client.wsConnectionStatus == ConnectionStatus.disconnected && - _isConnectionAvailable) { - client.openConnection(); - } - } - - void _onBackground() { - if (widget.onBackgroundEventReceived == null) { - if (client.wsConnectionStatus != ConnectionStatus.disconnected) { - client.closeConnection(); - } - return; - } - - _eventSubscription?.cancel(); - _eventSubscription = client.on().listen(widget.onBackgroundEventReceived); - - void onTimerComplete() { - _eventSubscription?.cancel(); - client.closeConnection(); - } - - _disconnectTimer?.cancel(); - _disconnectTimer = Timer(widget.backgroundKeepAlive, onTimerComplete); - return; - } - - @override - void dispose() { - WidgetsBinding.instance.removeObserver(this); - _unsubscribeFromConnectivityChange(); - _eventSubscription?.cancel(); - _disconnectTimer?.cancel(); - super.dispose(); - } -} diff --git a/packages/stream_chat_flutter_core/lib/src/stream_controller_extension.dart b/packages/stream_chat_flutter_core/lib/src/stream_controller_extension.dart deleted file mode 100644 index 3720b752b9..0000000000 --- a/packages/stream_chat_flutter_core/lib/src/stream_controller_extension.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'dart:async'; - -/// Extension on [StreamController] to safely add events and errors. -extension StreamControllerX on StreamController { - /// Safely adds the event to the controller, - /// Returns early if the controller is closed. - void safeAdd(T event) { - if (isClosed) return; - add(event); - } - - /// Safely adds the error to the controller, - /// Returns early if the controller is closed. - void safeAddError(Object error, [StackTrace? stackTrace]) { - if (isClosed) return; - addError(error, stackTrace); - } -} diff --git a/packages/stream_chat_flutter_core/lib/src/stream_member_list_controller.dart b/packages/stream_chat_flutter_core/lib/src/stream_member_list_controller.dart deleted file mode 100644 index 989aa87786..0000000000 --- a/packages/stream_chat_flutter_core/lib/src/stream_member_list_controller.dart +++ /dev/null @@ -1,154 +0,0 @@ -import 'dart:async'; -import 'dart:math'; - -import 'package:stream_chat/stream_chat.dart' hide Success; -import 'package:stream_chat_flutter_core/src/paged_value_notifier.dart'; - -/// The default channel page limit to load. -const defaultMemberPagedLimit = 10; - -const _kDefaultBackendPaginationLimit = 30; - -/// A controller for a member list. -/// -/// This class lets you perform tasks such as: -/// * Load initial data. -/// * Load more data using [loadMore]. -/// * Replace the previously loaded members. -class StreamMemberListController extends PagedValueNotifier { - /// Creates a Stream member list controller. - /// - /// * `client` is the Stream chat client to use for the channels list. - /// - /// * `filter` is the query filters to use. - /// - /// * `sort` is the sorting used for the members matching the filters. - /// - /// * `limit` is the limit to apply to the member list. - StreamMemberListController({ - required this.channel, - this.filter, - this.sort, - this.limit = defaultMemberPagedLimit, - }) : _activeFilter = filter, - _activeSort = sort, - super(const PagedValue.loading()); - - /// Creates a [StreamMemberListController] from the passed [value]. - StreamMemberListController.fromValue( - super.value, { - required this.channel, - this.filter, - this.sort, - this.limit = defaultMemberPagedLimit, - }) : _activeFilter = filter, - _activeSort = sort; - - /// The client to use for the channels list. - final Channel channel; - - /// The query filters to use. - /// - /// You can query on any of the custom fields you've defined on the [Member]. - /// - /// You can also filter other built-in channel fields. - final Filter? filter; - Filter? _activeFilter; - - /// The sorting used for the members matching the filters. - /// - /// Sorting is based on field and direction, multiple sorting options - /// can be provided. - /// - /// Direction can be ascending or descending. - final List? sort; - List? _activeSort; - - /// The limit to apply to the member list. The default is set to - /// [defaultMemberPagedLimit]. - final int limit; - - /// Allows for the change of filters used for member queries. - /// - /// Use this if you need to support runtime filter changes, - /// through custom filters UI. - set filter(Filter? value) => _activeFilter = value; - - /// Allows for the change of the query sort used for member queries. - /// - /// Use this if you need to support runtime sort changes, - /// through custom sort UI. - set sort(List? value) => _activeSort = value; - - @override - Future doInitialLoad() async { - final limit = min( - this.limit * defaultInitialPagedLimitMultiplier, - _kDefaultBackendPaginationLimit, - ); - try { - final memberResponse = await channel.queryMembers( - filter: _activeFilter, - sort: _activeSort, - pagination: PaginationParams(limit: limit), - ); - - final members = memberResponse.members; - final nextKey = members.length < limit ? null : members.length; - value = PagedValue( - items: members.where((it) => it.user != null).toList(), - nextPageKey: nextKey, - ); - } on StreamChatError catch (error) { - value = PagedValue.error(error); - } catch (error) { - final chatError = StreamChatError(error.toString()); - value = PagedValue.error(chatError); - } - } - - @override - Future loadMore(int nextPageKey) async { - final previousValue = value.asSuccess; - - try { - final memberResponse = await channel.queryMembers( - filter: _activeFilter, - sort: _activeSort, - pagination: PaginationParams(limit: limit, offset: nextPageKey), - ); - - final members = memberResponse.members; - final previousItems = previousValue.items; - final newItems = previousItems + members; - final nextKey = members.length < limit ? null : newItems.length; - value = PagedValue( - items: newItems.where((it) => it.user != null).toList(), - nextPageKey: nextKey, - ); - } on StreamChatError catch (error) { - value = previousValue.copyWith(error: error); - } catch (error) { - final chatError = StreamChatError(error.toString()); - value = previousValue.copyWith(error: chatError); - } - } - - @override - Future refresh({bool resetValue = true}) { - if (resetValue) { - _activeFilter = filter; - _activeSort = sort; - } - return super.refresh(resetValue: resetValue); - } - - /// Replaces the previously loaded members with [members] and updates - /// the nextPageKey. - set members(List members) { - value = PagedValue( - items: members, - nextPageKey: members.length, - ); - } -} diff --git a/packages/stream_chat_flutter_core/lib/src/stream_message_input_controller.dart b/packages/stream_chat_flutter_core/lib/src/stream_message_input_controller.dart deleted file mode 100644 index 874311a4aa..0000000000 --- a/packages/stream_chat_flutter_core/lib/src/stream_message_input_controller.dart +++ /dev/null @@ -1,331 +0,0 @@ -import 'dart:convert'; - -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat/stream_chat.dart'; - -import 'package:stream_chat_flutter_core/src/message_text_field_controller.dart'; - -/// A value listenable builder related to a [Message]. -/// -/// Pass in a [StreamMessageInputController] as the `valueListenable`. -typedef StreamMessageValueListenableBuilder = ValueListenableBuilder; - -/// {@template stream_chat_flutter.StreamMessageInputController} -/// Controller for storing and mutating a [Message] value. -/// {@endtemplate} -class StreamMessageInputController extends ValueNotifier { - /// Creates a controller for an editable text field. - /// - /// This constructor treats a null [message] argument as if it were the empty - /// message. - factory StreamMessageInputController({ - Message? message, - Map? textPatternStyle, - }) => - StreamMessageInputController._( - initialMessage: message ?? Message(), - textPatternStyle: textPatternStyle, - ); - - /// Creates a controller for an editable text field from an initial [text]. - factory StreamMessageInputController.fromText( - String? text, { - Map? textPatternStyle, - }) => - StreamMessageInputController._( - initialMessage: Message(text: text), - textPatternStyle: textPatternStyle, - ); - - /// Creates a controller for an editable text field from initial - /// [attachments]. - factory StreamMessageInputController.fromAttachments( - List attachments, { - Map? textPatternStyle, - }) => - StreamMessageInputController._( - initialMessage: Message(attachments: attachments), - textPatternStyle: textPatternStyle, - ); - - StreamMessageInputController._({ - required Message initialMessage, - Map? textPatternStyle, - }) : _initialMessage = initialMessage, - _textFieldController = MessageTextFieldController.fromValue( - _textEditingValueFromMessage(initialMessage), - textPatternStyle: textPatternStyle, - ), - super(initialMessage) { - _textFieldController.addListener(_textFieldListener); - } - - /// Returns the controller of the text field linked to this controller. - MessageTextFieldController get textFieldController => _textFieldController; - MessageTextFieldController _textFieldController; - - Message _initialMessage; - - static TextEditingValue _textEditingValueFromMessage(Message message) { - final messageText = message.text; - var textEditingValue = TextEditingValue.empty; - if (messageText != null) { - textEditingValue = TextEditingValue( - text: messageText, - selection: TextSelection.collapsed(offset: messageText.length), - ); - } - return textEditingValue; - } - - void _textFieldListener() { - final text = _textFieldController.text; - message = message.copyWith(text: text); - } - - /// Returns the current message associated with this controller. - Message get message => value; - - /// Sets the current message associated with this controller. - set message(Message message) => value = message; - - @override - set value(Message message) { - super.value = message; - - // Update text field controller only if message text has changed. - final messageText = message.text; - final textFieldText = _textFieldController.text; - if (messageText != textFieldText) { - textEditingValue = _textEditingValueFromMessage(message); - } - } - - /// Text of the message. - String get text => _textFieldController.text; - - /// Sets the text of the message. - set text(String text) { - _textFieldController.text = text; - } - - /// The currently selected [text]. - /// - /// If the selection is collapsed, then this property gives the offset of the - /// cursor within the text. - TextSelection get selection => _textFieldController.selection; - - set selection(TextSelection newSelection) { - _textFieldController.selection = selection; - } - - /// Returns the textEditingValue associated with this controller. - TextEditingValue get textEditingValue => _textFieldController.value; - - set textEditingValue(TextEditingValue value) { - _textFieldController.value = value; - } - - set quotedMessage(Message quotedMessage) { - message = message.copyWith( - quotedMessage: quotedMessage, - quotedMessageId: quotedMessage.id, - ); - } - - /// Clears the quoted message. - void clearQuotedMessage() { - message = message.copyWith( - quotedMessageId: null, - quotedMessage: null, - ); - } - - /// Sets a command for the message. - set command(String command) { - // Setting the command should also clear the text and attachments. - message = message.copyWith( - text: '', - attachments: [], - command: command, - ); - } - - /// Sets the [showInChannel] flag of the message. - set showInChannel(bool newValue) { - message = message.copyWith(showInChannel: newValue); - } - - /// Returns true if the message is in a thread and - /// should be shown in the main channel as well. - bool get showInChannel => message.showInChannel ?? false; - - /// Returns the attachments of the message. - List get attachments => message.attachments; - - /// Sets the list of [attachments] for the message. - set attachments(List attachments) { - message = message.copyWith(attachments: attachments); - } - - /// Adds a new attachment to the message. - void addAttachment(Attachment attachment) { - attachments = [...attachments, attachment]; - } - - /// Adds a new attachment at the specified [index]. - void addAttachmentAt(int index, Attachment attachment) { - attachments = [...attachments]..insert(index, attachment); - } - - /// Removes the specified [attachment] from the message. - void removeAttachment(Attachment attachment) { - attachments = [...attachments]..remove(attachment); - } - - /// Remove the attachment with the given [attachmentId]. - void removeAttachmentById(String attachmentId) { - attachments = [...attachments]..removeWhere((it) => it.id == attachmentId); - } - - /// Removes the attachment at the given [index]. - void removeAttachmentAt(int index) { - attachments = [...attachments]..removeAt(index); - } - - /// Clears the message attachments. - void clearAttachments() { - attachments = []; - } - - // Only used to store the value locally in order to remove it if we call - // [clearOGAttachment] or [setOGAttachment] again. - Attachment? _ogAttachment; - - /// Returns the og attachment of the message if set - Attachment? get ogAttachment => - attachments.firstWhereOrNull((it) => it.id == _ogAttachment?.id); - - /// Sets the og attachment in the message. - void setOGAttachment(Attachment attachment) { - attachments = [...attachments] - ..remove(_ogAttachment) - ..insert(0, attachment); - _ogAttachment = attachment; - } - - /// Removes the og attachment. - void clearOGAttachment() { - if (_ogAttachment != null) { - removeAttachment(_ogAttachment!); - } - _ogAttachment = null; - } - - /// Returns the poll in the message. - Poll? get poll => message.poll; - - /// Sets the poll in the message. - set poll(Poll? poll) { - message = message.copyWith(pollId: poll?.id, poll: poll); - } - - /// Returns the list of mentioned users in the message. - List get mentionedUsers => message.mentionedUsers; - - /// Sets the mentioned users. - set mentionedUsers(List users) { - message = message.copyWith(mentionedUsers: users); - } - - /// Adds a user to the list of mentioned users. - void addMentionedUser(User user) { - mentionedUsers = [...mentionedUsers, user]; - } - - /// Removes the specified [user] from the mentioned users list. - void removeMentionedUser(User user) { - mentionedUsers = [...mentionedUsers]..remove(user); - } - - /// Removes the mentioned user with the given [userId]. - void removeMentionedUserById(String userId) { - mentionedUsers = [...mentionedUsers]..removeWhere((it) => it.id == userId); - } - - /// Removes all mentioned users from the message. - void clearMentionedUsers() { - mentionedUsers = []; - } - - /// Sets the [message], to empty. - /// - /// After calling this function, [text], [attachments] and [mentionedUsers] - /// will all be empty. - /// - /// Calling this will notify all the listeners of this - /// [StreamMessageInputController] that they need to update - /// (calls [notifyListeners]). For this reason, - /// this method should only be called between frames, e.g. in response to user - /// actions, not during the build, layout, or paint phases. - void clear() { - message = Message(); - } - - /// Sets the [message] to the initial [Message] value. - void reset({bool resetId = true}) { - if (resetId) { - final newId = const Uuid().v4(); - _initialMessage = _initialMessage.copyWith(id: newId); - } - // Reset the message to the initial value. - message = _initialMessage; - } - - @override - void dispose() { - _textFieldController - ..removeListener(_textFieldListener) - ..dispose(); - super.dispose(); - } -} - -/// A [RestorableProperty] that knows how to store and restore a -/// [StreamMessageInputController]. -/// -/// The [StreamMessageInputController] is accessible via the [value] getter. -/// During state restoration, -/// the property will restore [StreamMessageInputController.message] -/// to the value it had when the restoration data it is getting restored from -/// was collected. -class StreamRestorableMessageInputController - extends RestorableChangeNotifier { - /// Creates a [StreamRestorableMessageInputController]. - /// - /// This constructor creates a default [Message] when no `message` argument - /// is supplied. - StreamRestorableMessageInputController({Message? message}) - : _initialValue = message ?? Message(); - - /// Creates a [StreamRestorableMessageInputController] from an initial - /// [text] value. - factory StreamRestorableMessageInputController.fromText(String? text) => - StreamRestorableMessageInputController(message: Message(text: text)); - - final Message _initialValue; - - @override - StreamMessageInputController createDefaultValue() => - StreamMessageInputController(message: _initialValue); - - @override - StreamMessageInputController fromPrimitives(Object? data) { - final message = Message.fromJson(json.decode(data! as String)); - return StreamMessageInputController(message: message); - } - - @override - String toPrimitives() => json.encode(value.message); -} diff --git a/packages/stream_chat_flutter_core/lib/src/stream_message_search_list_controller.dart b/packages/stream_chat_flutter_core/lib/src/stream_message_search_list_controller.dart deleted file mode 100644 index b41a99f041..0000000000 --- a/packages/stream_chat_flutter_core/lib/src/stream_message_search_list_controller.dart +++ /dev/null @@ -1,204 +0,0 @@ -import 'dart:async'; -import 'dart:math'; - -import 'package:stream_chat/stream_chat.dart' hide Success; -import 'package:stream_chat_flutter_core/src/paged_value_notifier.dart'; - -/// The default channel page limit to load. -const defaultMessageSearchPagedLimit = 10; - -const _kDefaultBackendPaginationLimit = 30; - -/// A controller for a user list. -/// -/// This class lets you perform tasks such as: -/// * Load initial data. -/// * Load more data using [loadMore]. -/// * Replace the previously loaded users. -class StreamMessageSearchListController - extends PagedValueNotifier { - /// Creates a Stream user list controller. - /// - /// * `client` is the Stream chat client to use for the channels list. - /// - /// * `filter` is the query filters to use. - /// - /// * `sort` is the sorting used for the users matching the filters. - /// - /// * `presence` sets whether you'll receive user presence updates via the - /// websocket events. - /// - /// * `limit` is the limit to apply to the user list. - StreamMessageSearchListController({ - required this.client, - required this.filter, - this.messageFilter, - this.searchQuery, - this.sort, - this.limit = defaultMessageSearchPagedLimit, - }) : assert( - messageFilter != null || searchQuery != null, - 'Either messageFilter or searchQuery must be provided', - ), - assert( - messageFilter == null || searchQuery == null, - 'Only one of messageFilter or searchQuery can be provided', - ), - _activeFilter = filter, - _activeMessageFilter = messageFilter, - _activeSearchQuery = searchQuery, - _activeSort = sort, - super(const PagedValue.loading()); - - /// Creates a [StreamUserListController] from the passed [value]. - StreamMessageSearchListController.fromValue( - super.value, { - required this.client, - required this.filter, - this.messageFilter, - this.searchQuery, - this.sort, - this.limit = defaultMessageSearchPagedLimit, - }) : assert( - messageFilter != null || searchQuery != null, - 'Either messageFilter or searchQuery must be provided', - ), - assert( - messageFilter == null || searchQuery == null, - 'Only one of messageFilter or searchQuery can be provided', - ), - _activeFilter = filter, - _activeMessageFilter = messageFilter, - _activeSearchQuery = searchQuery, - _activeSort = sort; - - /// The client to use for the channels list. - final StreamChatClient client; - - /// The query filters to use. - /// - /// You can query on any of the custom fields you've defined on the [User]. - /// - /// You can also filter other built-in channel fields. - final Filter filter; - Filter _activeFilter; - - /// The message query filters to use. - /// - /// You can query on any of the custom fields you've defined on the [Channel]. - /// - /// You can also filter other built-in channel fields. - final Filter? messageFilter; - Filter? _activeMessageFilter; - - /// Message String to search on. - final String? searchQuery; - String? _activeSearchQuery; - - /// The sorting used for the users matching the filters. - /// - /// Sorting is based on field and direction, multiple sorting options - /// can be provided. - /// - /// Direction can be ascending or descending. - final List? sort; - List? _activeSort; - - /// The limit to apply to the user list. The default is set to - /// [defaultUserPagedLimit]. - final int limit; - - /// Allows for the change of filters used for user queries. - /// - /// Use this if you need to support runtime filter changes, - /// through custom filters UI. - set filter(Filter value) => _activeFilter = value; - - /// Allows for the change of message filters used for user queries. - /// - /// Use this if you need to support runtime filter changes, - /// through custom filters UI. - set messageFilter(Filter? value) => _activeMessageFilter = value; - - /// Allows for the change of filters used for user queries. - /// - /// Use this if you need to support runtime filter changes, - /// through custom filters UI. - set searchQuery(String? value) => _activeSearchQuery = value; - - /// Allows for the change of the query sort used for user queries. - /// - /// Use this if you need to support runtime sort changes, - /// through custom sort UI. - set sort(List? value) => _activeSort = value; - - @override - Future doInitialLoad() async { - final limit = min( - this.limit * defaultInitialPagedLimitMultiplier, - _kDefaultBackendPaginationLimit, - ); - try { - final response = await client.search( - _activeFilter, - sort: _activeSort, - query: _activeSearchQuery, - messageFilters: _activeMessageFilter, - paginationParams: PaginationParams(limit: limit), - ); - - final results = response.results; - final nextKey = response.next; - value = PagedValue( - items: results, - nextPageKey: nextKey, - ); - } on StreamChatError catch (error) { - value = PagedValue.error(error); - } catch (error) { - final chatError = StreamChatError(error.toString()); - value = PagedValue.error(chatError); - } - } - - @override - Future loadMore(String nextPageKey) async { - final previousValue = value.asSuccess; - - try { - final response = await client.search( - _activeFilter, - sort: _activeSort, - query: _activeSearchQuery, - messageFilters: _activeMessageFilter, - paginationParams: PaginationParams(limit: limit, next: nextPageKey), - ); - - final results = response.results; - final previousItems = previousValue.items; - final newItems = previousItems + results; - final next = response.next; - final nextKey = next != null && next.isNotEmpty ? next : null; - value = PagedValue( - items: newItems, - nextPageKey: nextKey, - ); - } on StreamChatError catch (error) { - value = previousValue.copyWith(error: error); - } catch (error) { - final chatError = StreamChatError(error.toString()); - value = previousValue.copyWith(error: chatError); - } - } - - @override - Future refresh({bool resetValue = true}) { - if (resetValue) { - _activeFilter = filter; - _activeMessageFilter = messageFilter; - _activeSearchQuery = searchQuery; - _activeSort = sort; - } - return super.refresh(resetValue: resetValue); - } -} diff --git a/packages/stream_chat_flutter_core/lib/src/stream_poll_controller.dart b/packages/stream_chat_flutter_core/lib/src/stream_poll_controller.dart deleted file mode 100644 index 6fccd13007..0000000000 --- a/packages/stream_chat_flutter_core/lib/src/stream_poll_controller.dart +++ /dev/null @@ -1,284 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:stream_chat/stream_chat.dart'; - -part 'stream_poll_controller.freezed.dart'; - -/// {@template minMax} -/// A generic type representing a minimum and maximum value. -/// {@endtemplate} -typedef Range = ({T? min, T? max}); - -/// {@template pollConfig} -/// Configurations used while validating a poll. -/// {@endtemplate} -class PollConfig { - /// {@macro pollConfig} - const PollConfig({ - this.nameRange = const (min: 1, max: 80), - this.optionsRange = const (min: 1, max: 10), - this.allowDuplicateOptions = false, - this.allowedVotesRange = const (min: 2, max: 10), - }); - - /// The minimum and maximum length of the poll question. - /// if `null`, there is no limit to the length of the question. - /// - /// Defaults to `1` and `80`. - final Range? nameRange; - - /// The minimum and maximum length of the poll options. - /// if `null`, there is no limit to the length of the options. - /// - /// Defaults to `2` and `10`. - final Range? optionsRange; - - /// Whether the poll allows duplicate options. - /// - /// Defaults to `false`. - final bool allowDuplicateOptions; - - /// The minimum and maximum number of votes allowed. - /// if `null`, there is no limit to the number of votes allowed. - /// - /// Defaults to `2` and `10`. - final Range? allowedVotesRange; -} - -/// {@template streamPollController} -/// Controller used to manage the state of a poll. -/// {@endtemplate} -class StreamPollController extends ValueNotifier { - /// {@macro streamPollController} - factory StreamPollController({ - Poll? poll, - PollConfig? config, - }) => - StreamPollController._( - config ?? const PollConfig(), - poll ?? Poll(name: '', options: const [PollOption(text: '')]), - ); - - StreamPollController._(this.config, super.poll) : _initialValue = poll; - - // Initial poll passed to the controller, or modified with a new id when the - // controller is marked reset. - Poll _initialValue; - - /// The configuration used to validate the poll. - final PollConfig config; - - /// Returns a sanitized version of the poll. - /// - /// The sanitized poll is a copy of the current poll with the following - /// modifications: - /// - The id of the new options is set to `null`. This is because the id is - /// a reserved field and should not be set by the user. - Poll get sanitizedPoll { - final initialOptions = _initialValue.options.map((it) => it.id); - return value.copyWith( - options: [ - ...value.options.map((option) { - // Skip if the option is already present in the initial poll. - if (initialOptions.contains(option.id)) return option; - - // Remove the id from the new added options. - return option.copyWith(id: null); - }) - ], - ); - } - - /// Resets the poll to the initial value. - void reset({bool resetId = true}) { - if (resetId) { - final newId = const Uuid().v4(); - _initialValue = _initialValue.copyWith(id: newId); - } - value = _initialValue; - } - - /// Returns `true` if the poll is valid. - /// - /// The poll is considered valid if it passes all the validations specified - /// in the [config]. - /// - /// See also: - /// * [validateGranularly], which returns a [Set] of [PollValidationError] if - /// there are any errors. - bool validate() => validateGranularly().isEmpty; - - /// Validates the poll with the validation specified in the [config], and - /// returns a [Set] of [PollValidationError] only, if any. - /// - /// See also: - /// * [validate], which also validates the poll and returns true if there are - /// no errors. - Set validateGranularly() { - final invalidErrors = {}; - - // Validate the name length - if (config.nameRange case final nameRange?) { - final name = value.name; - final (:min, :max) = nameRange; - - if (min != null && name.length < min || - max != null && name.length > max) { - invalidErrors.add( - PollValidationError.nameRange(name, range: nameRange), - ); - } - } - - // Validate if the poll options are unique. - if (config.allowDuplicateOptions case false) { - final options = value.options; - final uniqueOptions = options.map((it) => it.text).toSet(); - if (uniqueOptions.length != options.length) { - invalidErrors.add( - PollValidationError.duplicateOptions(options), - ); - } - } - - // Validate the poll options count - if (config.optionsRange case final optionsRange?) { - final options = value.options; - final nonEmptyOptions = [...options.where((it) => it.text.isNotEmpty)]; - final (:min, :max) = optionsRange; - - if (min != null && nonEmptyOptions.length < min || - max != null && nonEmptyOptions.length > max) { - invalidErrors.add( - PollValidationError.optionsRange(options, range: optionsRange), - ); - } - } - - // Validate the max number of votes allowed if enforceUniqueVote is false. - if (value.enforceUniqueVote case false) { - if (value.maxVotesAllowed case final maxVotesAllowed?) { - if (config.allowedVotesRange case final allowedVotesRange?) { - final (:min, :max) = allowedVotesRange; - - if (min != null && maxVotesAllowed < min || - max != null && maxVotesAllowed > max) { - invalidErrors.add( - PollValidationError.maxVotesAllowed( - maxVotesAllowed, - range: allowedVotesRange, - ), - ); - } - } - } - } - - return invalidErrors; - } - - /// Adds a new option with the provided [text] and [extraData]. - /// - /// The new option will be added to the end of the list of options. - void addOption( - String text, { - Map extraData = const {}, - }) { - final options = [...value.options]; - final newOption = PollOption(text: text, extraData: extraData); - value = value.copyWith(options: [...options, newOption]); - } - - /// Updates the option at the provided [index] with the provided [text] and - /// [extraData]. - void updateOption( - String text, { - required int index, - Map extraData = const {}, - }) { - final options = [...value.options]; - options[index] = options[index].copyWith( - text: text, - extraData: extraData, - ); - - value = value.copyWith(options: options); - } - - /// Removes the option at the provided [index]. - PollOption removeOption(int index) { - final options = [...value.options]; - final removed = options.removeAt(index); - value = value.copyWith(options: options); - - return removed; - } - - /// Sets the poll question. - set question(String question) { - value = value.copyWith(name: question); - } - - /// Sets the poll options. - set options(List options) { - value = value.copyWith(options: options); - } - - /// Sets the poll enforce unique vote. - set enforceUniqueVote(bool enforceUniqueVote) { - value = value.copyWith(enforceUniqueVote: enforceUniqueVote); - } - - /// Sets the poll max votes allowed. - /// - /// If `null`, there is no limit to the number of votes allowed. - set maxVotesAllowed(int? maxVotesAllowed) { - value = value.copyWith(maxVotesAllowed: maxVotesAllowed); - } - - set allowSuggestions(bool allowSuggestions) { - value = value.copyWith(allowUserSuggestedOptions: allowSuggestions); - } - - /// Sets the poll voting visibility. - set votingVisibility(VotingVisibility visibility) { - value = value.copyWith(votingVisibility: visibility); - } - - /// Sets whether the poll allows comments. - set allowComments(bool allowComments) { - value = value.copyWith(allowAnswers: allowComments); - } -} - -/// {@template pollValidationError} -/// Union representing the possible validation errors while creating a poll. -/// -/// The errors are used to provide feedback to the user about what went wrong -/// while creating a poll. -/// {@endtemplate} -@freezed -sealed class PollValidationError with _$PollValidationError { - /// Occurs when the poll contains duplicate options. - const factory PollValidationError.duplicateOptions( - List options, - ) = _PollValidationErrorDuplicateOptions; - - /// Occurs when the poll question length is not within the allowed range. - const factory PollValidationError.nameRange( - String name, { - required Range range, - }) = _PollValidationErrorNameRange; - - /// Occurs when the poll options count is not within the allowed range. - const factory PollValidationError.optionsRange( - List options, { - required Range range, - }) = _PollValidationErrorOptionsRange; - - /// Occurs when the poll max votes allowed is not within the allowed range. - const factory PollValidationError.maxVotesAllowed( - int maxVotesAllowed, { - required Range range, - }) = _PollValidationErrorMaxVotesAllowed; -} diff --git a/packages/stream_chat_flutter_core/lib/src/stream_poll_controller.freezed.dart b/packages/stream_chat_flutter_core/lib/src/stream_poll_controller.freezed.dart deleted file mode 100644 index 5bacc5ef02..0000000000 --- a/packages/stream_chat_flutter_core/lib/src/stream_poll_controller.freezed.dart +++ /dev/null @@ -1,857 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// 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 - -part of 'stream_poll_controller.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); - -/// @nodoc -mixin _$PollValidationError { - @optionalTypeArgs - TResult when({ - required TResult Function(List options) duplicateOptions, - required TResult Function(String name, ({int? max, int? min}) range) - nameRange, - required TResult Function( - List options, ({int? max, int? min}) range) - optionsRange, - required TResult Function(int maxVotesAllowed, ({int? max, int? min}) range) - maxVotesAllowed, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function(List options)? duplicateOptions, - TResult? Function(String name, ({int? max, int? min}) range)? nameRange, - TResult? Function(List options, ({int? max, int? min}) range)? - optionsRange, - TResult? Function(int maxVotesAllowed, ({int? max, int? min}) range)? - maxVotesAllowed, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult maybeWhen({ - TResult Function(List options)? duplicateOptions, - TResult Function(String name, ({int? max, int? min}) range)? nameRange, - TResult Function(List options, ({int? max, int? min}) range)? - optionsRange, - TResult Function(int maxVotesAllowed, ({int? max, int? min}) range)? - maxVotesAllowed, - required TResult orElse(), - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult map({ - required TResult Function(_PollValidationErrorDuplicateOptions value) - duplicateOptions, - required TResult Function(_PollValidationErrorNameRange value) nameRange, - required TResult Function(_PollValidationErrorOptionsRange value) - optionsRange, - required TResult Function(_PollValidationErrorMaxVotesAllowed value) - maxVotesAllowed, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(_PollValidationErrorDuplicateOptions value)? - duplicateOptions, - TResult? Function(_PollValidationErrorNameRange value)? nameRange, - TResult? Function(_PollValidationErrorOptionsRange value)? optionsRange, - TResult? Function(_PollValidationErrorMaxVotesAllowed value)? - maxVotesAllowed, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult maybeMap({ - TResult Function(_PollValidationErrorDuplicateOptions value)? - duplicateOptions, - TResult Function(_PollValidationErrorNameRange value)? nameRange, - TResult Function(_PollValidationErrorOptionsRange value)? optionsRange, - TResult Function(_PollValidationErrorMaxVotesAllowed value)? - maxVotesAllowed, - required TResult orElse(), - }) => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $PollValidationErrorCopyWith<$Res> { - factory $PollValidationErrorCopyWith( - PollValidationError value, $Res Function(PollValidationError) then) = - _$PollValidationErrorCopyWithImpl<$Res, PollValidationError>; -} - -/// @nodoc -class _$PollValidationErrorCopyWithImpl<$Res, $Val extends PollValidationError> - implements $PollValidationErrorCopyWith<$Res> { - _$PollValidationErrorCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of PollValidationError - /// with the given fields replaced by the non-null parameter values. -} - -/// @nodoc -abstract class _$$PollValidationErrorDuplicateOptionsImplCopyWith<$Res> { - factory _$$PollValidationErrorDuplicateOptionsImplCopyWith( - _$PollValidationErrorDuplicateOptionsImpl value, - $Res Function(_$PollValidationErrorDuplicateOptionsImpl) then) = - __$$PollValidationErrorDuplicateOptionsImplCopyWithImpl<$Res>; - @useResult - $Res call({List options}); -} - -/// @nodoc -class __$$PollValidationErrorDuplicateOptionsImplCopyWithImpl<$Res> - extends _$PollValidationErrorCopyWithImpl<$Res, - _$PollValidationErrorDuplicateOptionsImpl> - implements _$$PollValidationErrorDuplicateOptionsImplCopyWith<$Res> { - __$$PollValidationErrorDuplicateOptionsImplCopyWithImpl( - _$PollValidationErrorDuplicateOptionsImpl _value, - $Res Function(_$PollValidationErrorDuplicateOptionsImpl) _then) - : super(_value, _then); - - /// Create a copy of PollValidationError - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? options = null, - }) { - return _then(_$PollValidationErrorDuplicateOptionsImpl( - null == options - ? _value._options - : options // ignore: cast_nullable_to_non_nullable - as List, - )); - } -} - -/// @nodoc - -class _$PollValidationErrorDuplicateOptionsImpl - implements _PollValidationErrorDuplicateOptions { - const _$PollValidationErrorDuplicateOptionsImpl( - final List options) - : _options = options; - - final List _options; - @override - List get options { - if (_options is EqualUnmodifiableListView) return _options; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_options); - } - - @override - String toString() { - return 'PollValidationError.duplicateOptions(options: $options)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$PollValidationErrorDuplicateOptionsImpl && - const DeepCollectionEquality().equals(other._options, _options)); - } - - @override - int get hashCode => - Object.hash(runtimeType, const DeepCollectionEquality().hash(_options)); - - /// Create a copy of PollValidationError - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$PollValidationErrorDuplicateOptionsImplCopyWith< - _$PollValidationErrorDuplicateOptionsImpl> - get copyWith => __$$PollValidationErrorDuplicateOptionsImplCopyWithImpl< - _$PollValidationErrorDuplicateOptionsImpl>(this, _$identity); - - @override - @optionalTypeArgs - TResult when({ - required TResult Function(List options) duplicateOptions, - required TResult Function(String name, ({int? max, int? min}) range) - nameRange, - required TResult Function( - List options, ({int? max, int? min}) range) - optionsRange, - required TResult Function(int maxVotesAllowed, ({int? max, int? min}) range) - maxVotesAllowed, - }) { - return duplicateOptions(options); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function(List options)? duplicateOptions, - TResult? Function(String name, ({int? max, int? min}) range)? nameRange, - TResult? Function(List options, ({int? max, int? min}) range)? - optionsRange, - TResult? Function(int maxVotesAllowed, ({int? max, int? min}) range)? - maxVotesAllowed, - }) { - return duplicateOptions?.call(options); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function(List options)? duplicateOptions, - TResult Function(String name, ({int? max, int? min}) range)? nameRange, - TResult Function(List options, ({int? max, int? min}) range)? - optionsRange, - TResult Function(int maxVotesAllowed, ({int? max, int? min}) range)? - maxVotesAllowed, - required TResult orElse(), - }) { - if (duplicateOptions != null) { - return duplicateOptions(options); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(_PollValidationErrorDuplicateOptions value) - duplicateOptions, - required TResult Function(_PollValidationErrorNameRange value) nameRange, - required TResult Function(_PollValidationErrorOptionsRange value) - optionsRange, - required TResult Function(_PollValidationErrorMaxVotesAllowed value) - maxVotesAllowed, - }) { - return duplicateOptions(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(_PollValidationErrorDuplicateOptions value)? - duplicateOptions, - TResult? Function(_PollValidationErrorNameRange value)? nameRange, - TResult? Function(_PollValidationErrorOptionsRange value)? optionsRange, - TResult? Function(_PollValidationErrorMaxVotesAllowed value)? - maxVotesAllowed, - }) { - return duplicateOptions?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(_PollValidationErrorDuplicateOptions value)? - duplicateOptions, - TResult Function(_PollValidationErrorNameRange value)? nameRange, - TResult Function(_PollValidationErrorOptionsRange value)? optionsRange, - TResult Function(_PollValidationErrorMaxVotesAllowed value)? - maxVotesAllowed, - required TResult orElse(), - }) { - if (duplicateOptions != null) { - return duplicateOptions(this); - } - return orElse(); - } -} - -abstract class _PollValidationErrorDuplicateOptions - implements PollValidationError { - const factory _PollValidationErrorDuplicateOptions( - final List options) = - _$PollValidationErrorDuplicateOptionsImpl; - - List get options; - - /// Create a copy of PollValidationError - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - _$$PollValidationErrorDuplicateOptionsImplCopyWith< - _$PollValidationErrorDuplicateOptionsImpl> - get copyWith => throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class _$$PollValidationErrorNameRangeImplCopyWith<$Res> { - factory _$$PollValidationErrorNameRangeImplCopyWith( - _$PollValidationErrorNameRangeImpl value, - $Res Function(_$PollValidationErrorNameRangeImpl) then) = - __$$PollValidationErrorNameRangeImplCopyWithImpl<$Res>; - @useResult - $Res call({String name, ({int? max, int? min}) range}); -} - -/// @nodoc -class __$$PollValidationErrorNameRangeImplCopyWithImpl<$Res> - extends _$PollValidationErrorCopyWithImpl<$Res, - _$PollValidationErrorNameRangeImpl> - implements _$$PollValidationErrorNameRangeImplCopyWith<$Res> { - __$$PollValidationErrorNameRangeImplCopyWithImpl( - _$PollValidationErrorNameRangeImpl _value, - $Res Function(_$PollValidationErrorNameRangeImpl) _then) - : super(_value, _then); - - /// Create a copy of PollValidationError - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? name = null, - Object? range = null, - }) { - return _then(_$PollValidationErrorNameRangeImpl( - null == name - ? _value.name - : name // ignore: cast_nullable_to_non_nullable - as String, - range: null == range - ? _value.range - : range // ignore: cast_nullable_to_non_nullable - as ({int? max, int? min}), - )); - } -} - -/// @nodoc - -class _$PollValidationErrorNameRangeImpl - implements _PollValidationErrorNameRange { - const _$PollValidationErrorNameRangeImpl(this.name, {required this.range}); - - @override - final String name; - @override - final ({int? max, int? min}) range; - - @override - String toString() { - return 'PollValidationError.nameRange(name: $name, range: $range)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$PollValidationErrorNameRangeImpl && - (identical(other.name, name) || other.name == name) && - (identical(other.range, range) || other.range == range)); - } - - @override - int get hashCode => Object.hash(runtimeType, name, range); - - /// Create a copy of PollValidationError - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$PollValidationErrorNameRangeImplCopyWith< - _$PollValidationErrorNameRangeImpl> - get copyWith => __$$PollValidationErrorNameRangeImplCopyWithImpl< - _$PollValidationErrorNameRangeImpl>(this, _$identity); - - @override - @optionalTypeArgs - TResult when({ - required TResult Function(List options) duplicateOptions, - required TResult Function(String name, ({int? max, int? min}) range) - nameRange, - required TResult Function( - List options, ({int? max, int? min}) range) - optionsRange, - required TResult Function(int maxVotesAllowed, ({int? max, int? min}) range) - maxVotesAllowed, - }) { - return nameRange(name, range); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function(List options)? duplicateOptions, - TResult? Function(String name, ({int? max, int? min}) range)? nameRange, - TResult? Function(List options, ({int? max, int? min}) range)? - optionsRange, - TResult? Function(int maxVotesAllowed, ({int? max, int? min}) range)? - maxVotesAllowed, - }) { - return nameRange?.call(name, range); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function(List options)? duplicateOptions, - TResult Function(String name, ({int? max, int? min}) range)? nameRange, - TResult Function(List options, ({int? max, int? min}) range)? - optionsRange, - TResult Function(int maxVotesAllowed, ({int? max, int? min}) range)? - maxVotesAllowed, - required TResult orElse(), - }) { - if (nameRange != null) { - return nameRange(name, range); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(_PollValidationErrorDuplicateOptions value) - duplicateOptions, - required TResult Function(_PollValidationErrorNameRange value) nameRange, - required TResult Function(_PollValidationErrorOptionsRange value) - optionsRange, - required TResult Function(_PollValidationErrorMaxVotesAllowed value) - maxVotesAllowed, - }) { - return nameRange(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(_PollValidationErrorDuplicateOptions value)? - duplicateOptions, - TResult? Function(_PollValidationErrorNameRange value)? nameRange, - TResult? Function(_PollValidationErrorOptionsRange value)? optionsRange, - TResult? Function(_PollValidationErrorMaxVotesAllowed value)? - maxVotesAllowed, - }) { - return nameRange?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(_PollValidationErrorDuplicateOptions value)? - duplicateOptions, - TResult Function(_PollValidationErrorNameRange value)? nameRange, - TResult Function(_PollValidationErrorOptionsRange value)? optionsRange, - TResult Function(_PollValidationErrorMaxVotesAllowed value)? - maxVotesAllowed, - required TResult orElse(), - }) { - if (nameRange != null) { - return nameRange(this); - } - return orElse(); - } -} - -abstract class _PollValidationErrorNameRange implements PollValidationError { - const factory _PollValidationErrorNameRange(final String name, - {required final ({int? max, int? min}) range}) = - _$PollValidationErrorNameRangeImpl; - - String get name; - ({int? max, int? min}) get range; - - /// Create a copy of PollValidationError - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - _$$PollValidationErrorNameRangeImplCopyWith< - _$PollValidationErrorNameRangeImpl> - get copyWith => throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class _$$PollValidationErrorOptionsRangeImplCopyWith<$Res> { - factory _$$PollValidationErrorOptionsRangeImplCopyWith( - _$PollValidationErrorOptionsRangeImpl value, - $Res Function(_$PollValidationErrorOptionsRangeImpl) then) = - __$$PollValidationErrorOptionsRangeImplCopyWithImpl<$Res>; - @useResult - $Res call({List options, ({int? max, int? min}) range}); -} - -/// @nodoc -class __$$PollValidationErrorOptionsRangeImplCopyWithImpl<$Res> - extends _$PollValidationErrorCopyWithImpl<$Res, - _$PollValidationErrorOptionsRangeImpl> - implements _$$PollValidationErrorOptionsRangeImplCopyWith<$Res> { - __$$PollValidationErrorOptionsRangeImplCopyWithImpl( - _$PollValidationErrorOptionsRangeImpl _value, - $Res Function(_$PollValidationErrorOptionsRangeImpl) _then) - : super(_value, _then); - - /// Create a copy of PollValidationError - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? options = null, - Object? range = null, - }) { - return _then(_$PollValidationErrorOptionsRangeImpl( - null == options - ? _value._options - : options // ignore: cast_nullable_to_non_nullable - as List, - range: null == range - ? _value.range - : range // ignore: cast_nullable_to_non_nullable - as ({int? max, int? min}), - )); - } -} - -/// @nodoc - -class _$PollValidationErrorOptionsRangeImpl - implements _PollValidationErrorOptionsRange { - const _$PollValidationErrorOptionsRangeImpl(final List options, - {required this.range}) - : _options = options; - - final List _options; - @override - List get options { - if (_options is EqualUnmodifiableListView) return _options; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_options); - } - - @override - final ({int? max, int? min}) range; - - @override - String toString() { - return 'PollValidationError.optionsRange(options: $options, range: $range)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$PollValidationErrorOptionsRangeImpl && - const DeepCollectionEquality().equals(other._options, _options) && - (identical(other.range, range) || other.range == range)); - } - - @override - int get hashCode => Object.hash( - runtimeType, const DeepCollectionEquality().hash(_options), range); - - /// Create a copy of PollValidationError - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$PollValidationErrorOptionsRangeImplCopyWith< - _$PollValidationErrorOptionsRangeImpl> - get copyWith => __$$PollValidationErrorOptionsRangeImplCopyWithImpl< - _$PollValidationErrorOptionsRangeImpl>(this, _$identity); - - @override - @optionalTypeArgs - TResult when({ - required TResult Function(List options) duplicateOptions, - required TResult Function(String name, ({int? max, int? min}) range) - nameRange, - required TResult Function( - List options, ({int? max, int? min}) range) - optionsRange, - required TResult Function(int maxVotesAllowed, ({int? max, int? min}) range) - maxVotesAllowed, - }) { - return optionsRange(options, range); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function(List options)? duplicateOptions, - TResult? Function(String name, ({int? max, int? min}) range)? nameRange, - TResult? Function(List options, ({int? max, int? min}) range)? - optionsRange, - TResult? Function(int maxVotesAllowed, ({int? max, int? min}) range)? - maxVotesAllowed, - }) { - return optionsRange?.call(options, range); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function(List options)? duplicateOptions, - TResult Function(String name, ({int? max, int? min}) range)? nameRange, - TResult Function(List options, ({int? max, int? min}) range)? - optionsRange, - TResult Function(int maxVotesAllowed, ({int? max, int? min}) range)? - maxVotesAllowed, - required TResult orElse(), - }) { - if (optionsRange != null) { - return optionsRange(options, range); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(_PollValidationErrorDuplicateOptions value) - duplicateOptions, - required TResult Function(_PollValidationErrorNameRange value) nameRange, - required TResult Function(_PollValidationErrorOptionsRange value) - optionsRange, - required TResult Function(_PollValidationErrorMaxVotesAllowed value) - maxVotesAllowed, - }) { - return optionsRange(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(_PollValidationErrorDuplicateOptions value)? - duplicateOptions, - TResult? Function(_PollValidationErrorNameRange value)? nameRange, - TResult? Function(_PollValidationErrorOptionsRange value)? optionsRange, - TResult? Function(_PollValidationErrorMaxVotesAllowed value)? - maxVotesAllowed, - }) { - return optionsRange?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(_PollValidationErrorDuplicateOptions value)? - duplicateOptions, - TResult Function(_PollValidationErrorNameRange value)? nameRange, - TResult Function(_PollValidationErrorOptionsRange value)? optionsRange, - TResult Function(_PollValidationErrorMaxVotesAllowed value)? - maxVotesAllowed, - required TResult orElse(), - }) { - if (optionsRange != null) { - return optionsRange(this); - } - return orElse(); - } -} - -abstract class _PollValidationErrorOptionsRange implements PollValidationError { - const factory _PollValidationErrorOptionsRange(final List options, - {required final ({int? max, int? min}) range}) = - _$PollValidationErrorOptionsRangeImpl; - - List get options; - ({int? max, int? min}) get range; - - /// Create a copy of PollValidationError - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - _$$PollValidationErrorOptionsRangeImplCopyWith< - _$PollValidationErrorOptionsRangeImpl> - get copyWith => throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class _$$PollValidationErrorMaxVotesAllowedImplCopyWith<$Res> { - factory _$$PollValidationErrorMaxVotesAllowedImplCopyWith( - _$PollValidationErrorMaxVotesAllowedImpl value, - $Res Function(_$PollValidationErrorMaxVotesAllowedImpl) then) = - __$$PollValidationErrorMaxVotesAllowedImplCopyWithImpl<$Res>; - @useResult - $Res call({int maxVotesAllowed, ({int? max, int? min}) range}); -} - -/// @nodoc -class __$$PollValidationErrorMaxVotesAllowedImplCopyWithImpl<$Res> - extends _$PollValidationErrorCopyWithImpl<$Res, - _$PollValidationErrorMaxVotesAllowedImpl> - implements _$$PollValidationErrorMaxVotesAllowedImplCopyWith<$Res> { - __$$PollValidationErrorMaxVotesAllowedImplCopyWithImpl( - _$PollValidationErrorMaxVotesAllowedImpl _value, - $Res Function(_$PollValidationErrorMaxVotesAllowedImpl) _then) - : super(_value, _then); - - /// Create a copy of PollValidationError - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? maxVotesAllowed = null, - Object? range = null, - }) { - return _then(_$PollValidationErrorMaxVotesAllowedImpl( - null == maxVotesAllowed - ? _value.maxVotesAllowed - : maxVotesAllowed // ignore: cast_nullable_to_non_nullable - as int, - range: null == range - ? _value.range - : range // ignore: cast_nullable_to_non_nullable - as ({int? max, int? min}), - )); - } -} - -/// @nodoc - -class _$PollValidationErrorMaxVotesAllowedImpl - implements _PollValidationErrorMaxVotesAllowed { - const _$PollValidationErrorMaxVotesAllowedImpl(this.maxVotesAllowed, - {required this.range}); - - @override - final int maxVotesAllowed; - @override - final ({int? max, int? min}) range; - - @override - String toString() { - return 'PollValidationError.maxVotesAllowed(maxVotesAllowed: $maxVotesAllowed, range: $range)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$PollValidationErrorMaxVotesAllowedImpl && - (identical(other.maxVotesAllowed, maxVotesAllowed) || - other.maxVotesAllowed == maxVotesAllowed) && - (identical(other.range, range) || other.range == range)); - } - - @override - int get hashCode => Object.hash(runtimeType, maxVotesAllowed, range); - - /// Create a copy of PollValidationError - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$PollValidationErrorMaxVotesAllowedImplCopyWith< - _$PollValidationErrorMaxVotesAllowedImpl> - get copyWith => __$$PollValidationErrorMaxVotesAllowedImplCopyWithImpl< - _$PollValidationErrorMaxVotesAllowedImpl>(this, _$identity); - - @override - @optionalTypeArgs - TResult when({ - required TResult Function(List options) duplicateOptions, - required TResult Function(String name, ({int? max, int? min}) range) - nameRange, - required TResult Function( - List options, ({int? max, int? min}) range) - optionsRange, - required TResult Function(int maxVotesAllowed, ({int? max, int? min}) range) - maxVotesAllowed, - }) { - return maxVotesAllowed(this.maxVotesAllowed, range); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function(List options)? duplicateOptions, - TResult? Function(String name, ({int? max, int? min}) range)? nameRange, - TResult? Function(List options, ({int? max, int? min}) range)? - optionsRange, - TResult? Function(int maxVotesAllowed, ({int? max, int? min}) range)? - maxVotesAllowed, - }) { - return maxVotesAllowed?.call(this.maxVotesAllowed, range); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function(List options)? duplicateOptions, - TResult Function(String name, ({int? max, int? min}) range)? nameRange, - TResult Function(List options, ({int? max, int? min}) range)? - optionsRange, - TResult Function(int maxVotesAllowed, ({int? max, int? min}) range)? - maxVotesAllowed, - required TResult orElse(), - }) { - if (maxVotesAllowed != null) { - return maxVotesAllowed(this.maxVotesAllowed, range); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(_PollValidationErrorDuplicateOptions value) - duplicateOptions, - required TResult Function(_PollValidationErrorNameRange value) nameRange, - required TResult Function(_PollValidationErrorOptionsRange value) - optionsRange, - required TResult Function(_PollValidationErrorMaxVotesAllowed value) - maxVotesAllowed, - }) { - return maxVotesAllowed(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(_PollValidationErrorDuplicateOptions value)? - duplicateOptions, - TResult? Function(_PollValidationErrorNameRange value)? nameRange, - TResult? Function(_PollValidationErrorOptionsRange value)? optionsRange, - TResult? Function(_PollValidationErrorMaxVotesAllowed value)? - maxVotesAllowed, - }) { - return maxVotesAllowed?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(_PollValidationErrorDuplicateOptions value)? - duplicateOptions, - TResult Function(_PollValidationErrorNameRange value)? nameRange, - TResult Function(_PollValidationErrorOptionsRange value)? optionsRange, - TResult Function(_PollValidationErrorMaxVotesAllowed value)? - maxVotesAllowed, - required TResult orElse(), - }) { - if (maxVotesAllowed != null) { - return maxVotesAllowed(this); - } - return orElse(); - } -} - -abstract class _PollValidationErrorMaxVotesAllowed - implements PollValidationError { - const factory _PollValidationErrorMaxVotesAllowed(final int maxVotesAllowed, - {required final ({int? max, int? min}) range}) = - _$PollValidationErrorMaxVotesAllowedImpl; - - int get maxVotesAllowed; - ({int? max, int? min}) get range; - - /// Create a copy of PollValidationError - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - _$$PollValidationErrorMaxVotesAllowedImplCopyWith< - _$PollValidationErrorMaxVotesAllowedImpl> - get copyWith => throw _privateConstructorUsedError; -} diff --git a/packages/stream_chat_flutter_core/lib/src/stream_poll_vote_list_controller.dart b/packages/stream_chat_flutter_core/lib/src/stream_poll_vote_list_controller.dart deleted file mode 100644 index 3ac1cfd209..0000000000 --- a/packages/stream_chat_flutter_core/lib/src/stream_poll_vote_list_controller.dart +++ /dev/null @@ -1,297 +0,0 @@ -import 'dart:async'; -import 'dart:math'; - -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_flutter_core/src/paged_value_notifier.dart'; - -/// The default channel page limit to load. -const defaultPollVotePagedLimit = 10; - -const _kDefaultBackendPaginationLimit = 30; - -/// A controller for a poll vote list. -/// -/// This class lets you perform tasks such as: -/// * Load initial data. -/// * Load more data using [loadMore]. -/// * Replace the previously loaded poll votes. -class StreamPollVoteListController - extends PagedValueNotifier { - /// Creates a Stream poll vote list controller. - /// * `channel` is the Stream chat channel to use for the poll votes list. - /// * `pollId` is the poll id to use for the poll votes list. - /// * `filter` is the query filters to use. - /// * `sort` is the sorting used for the poll votes matching the filters. - /// * `limit` is the limit to apply to the poll vote list. - StreamPollVoteListController({ - required this.channel, - required this.pollId, - StreamPollVoteEventHandler? eventHandler, - this.filter, - this.sort, - this.limit = defaultPollVotePagedLimit, - }) : _eventHandler = eventHandler ?? StreamPollVoteEventHandler(), - _activeFilter = filter, - _activeSort = sort, - super(const PagedValue.loading()); - - /// Creates a [StreamPollVoteListController] from the passed [value]. - StreamPollVoteListController.fromValue( - super.value, { - required this.channel, - required this.pollId, - StreamPollVoteEventHandler? eventHandler, - this.filter, - this.sort, - this.limit = defaultPollVotePagedLimit, - }) : _eventHandler = eventHandler ?? StreamPollVoteEventHandler(), - _activeFilter = filter, - _activeSort = sort; - - /// The channel to use for the poll votes list. - final Channel channel; - - /// The poll id to use for the poll votes list. - final String pollId; - - /// The poll vote event handlers to use for the poll votes list. - final StreamPollVoteEventHandler _eventHandler; - - /// The query filters to use. - /// - /// You can query on any of the custom fields you've defined on the - /// [PollVote]. - final Filter? filter; - Filter? _activeFilter; - - /// The sorting used for the poll votes matching the filters. - /// - /// Sorting is based on field and direction, multiple sorting options - /// can be provided. - /// - /// Direction can be ascending or descending. - final List? sort; - List? _activeSort; - - /// The limit to apply to the poll vote list. The default is set to - /// [defaultPollVotePagedLimit]. - final int limit; - - /// Allows for the change of filters used for poll vote queries. - /// - /// Use this if you need to support runtime filter changes, - /// through custom filters UI. - set filter(Filter? value) => _activeFilter = value; - - /// Allows for the change of the query sort used for poll vote queries. - /// - /// Use this if you need to support runtime sort changes, - /// through custom sort UI. - set sort(List? value) => _activeSort = value; - - @override - Future doInitialLoad() async { - final limit = min( - this.limit * defaultInitialPagedLimitMultiplier, - _kDefaultBackendPaginationLimit, - ); - try { - final response = await channel.queryPollVotes( - pollId, - sort: _activeSort, - filter: _activeFilter, - pagination: PaginationParams(limit: limit), - ); - - final results = response.votes; - final nextKey = response.next; - value = PagedValue( - items: results, - nextPageKey: nextKey, - ); - - // start listening to events - _subscribeToPollVoteEvents(); - } on StreamChatError catch (error) { - value = PagedValue.error(error); - } catch (error) { - final chatError = StreamChatError(error.toString()); - value = PagedValue.error(chatError); - } - } - - @override - Future loadMore(String nextPageKey) async { - final previousValue = value.asSuccess; - - try { - final response = await channel.queryPollVotes( - pollId, - sort: _activeSort, - filter: _activeFilter, - pagination: PaginationParams(limit: limit, next: nextPageKey), - ); - - final results = response.votes; - final previousItems = previousValue.items; - final newItems = previousItems + results; - final next = response.next; - final nextKey = next != null && next.isNotEmpty ? next : null; - value = PagedValue( - items: newItems, - nextPageKey: nextKey, - ); - } on StreamChatError catch (error) { - value = previousValue.copyWith(error: error); - } catch (error) { - final chatError = StreamChatError(error.toString()); - value = previousValue.copyWith(error: chatError); - } - } - - @override - Future refresh({bool resetValue = true}) { - if (resetValue) { - _activeFilter = filter; - _activeSort = sort; - } - return super.refresh(resetValue: resetValue); - } - - /// Replaces the previously loaded poll votes with the passed [pollVotes]. - set pollVotes(List pollVotes) { - if (value.isSuccess) { - final currentValue = value.asSuccess; - value = currentValue.copyWith(items: pollVotes); - } else { - value = PagedValue(items: pollVotes); - } - } - - /// Event listener, which can be set in order to listen - /// [client] web-socket events. - /// - /// Return `true` if the event is handled. Return `false` to - /// allow the event to be handled internally. - bool Function(Event event)? eventListener; - - StreamSubscription? _pollVoteListEventSubscription; - - // Subscribes to the poll vote list events. - void _subscribeToPollVoteEvents() { - if (_pollVoteListEventSubscription != null) { - _unsubscribeFromPolVoteListEvents(); - } - - _pollVoteListEventSubscription = channel.on().listen((event) { - // Only handle the event if the value is in success state. - if (value.isNotSuccess) return; - - // Returns early if the event is already handled by the listener. - if (eventListener?.call(event) ?? false) return; - - final eventType = event.type; - if (eventType == EventType.pollVoteCasted || - eventType == EventType.pollAnswerCasted) { - _eventHandler.onPollVoteCasted(event, this); - } else if (eventType == EventType.pollVoteChanged) { - _eventHandler.onPollVoteChanged(event, this); - } else if (eventType == EventType.pollVoteRemoved || - eventType == EventType.pollAnswerRemoved) { - _eventHandler.onPollVoteRemoved(event, this); - } - }); - } - - // Unsubscribes from all poll vote list events. - void _unsubscribeFromPolVoteListEvents() { - if (_pollVoteListEventSubscription != null) { - _pollVoteListEventSubscription!.cancel(); - _pollVoteListEventSubscription = null; - } - } - - /// Pauses all subscriptions added to this composite. - void pauseEventsSubscription([Future? resumeSignal]) { - _pollVoteListEventSubscription?.pause(resumeSignal); - } - - /// Resumes all subscriptions added to this composite. - void resumeEventsSubscription() { - _pollVoteListEventSubscription?.resume(); - } - - @override - void dispose() { - _unsubscribeFromPolVoteListEvents(); - super.dispose(); - } -} - -/// Contains handlers that are called from [StreamPollVoteListController] for -/// certain [Event]s. -/// -/// This class can be mixed in or extended to create custom overrides. -mixin class StreamPollVoteEventHandler { - /// Function which gets called for the event - /// [EventType.pollVoteCasted] and [EventType.pollAnswerCasted]. - /// - /// This event is fired when a new poll vote is casted in a poll. - /// - /// By default, this adds the poll vote and moves it to the top of list. - void onPollVoteCasted( - Event event, - StreamPollVoteListController controller, - ) { - final pollVote = event.pollVote; - if (pollVote == null) return; - - final updatedPollVotes = { - for (final vote in controller.currentItems) vote.id!: vote, - pollVote.id!: pollVote, - }; - - controller.pollVotes = [...updatedPollVotes.values]; - } - - /// Function which gets called for the event [EventType.pollVoteChanged]. - /// - /// This event is fired when a poll vote is changed in a poll. - /// - /// By default, this updates the poll vote with the event poll vote. - void onPollVoteChanged( - Event event, - StreamPollVoteListController controller, - ) { - final pollVote = event.pollVote; - if (pollVote == null) return; - - final updatedPollVotes = { - for (final vote in controller.currentItems) vote.id!: vote, - // Update the poll vote if it exists otherwise add it. - pollVote.id!: pollVote, - }; - - controller.pollVotes = [...updatedPollVotes.values]; - } - - /// Function which gets called for the event [EventType.pollVoteRemoved] and - /// [EventType.pollAnswerRemoved]. - /// - /// This event is fired when a poll vote is removed from a poll. - /// - /// By default, this removes the poll vote from the list. - void onPollVoteRemoved( - Event event, - StreamPollVoteListController controller, - ) { - final pollVote = event.pollVote; - if (pollVote == null) return; - - final ownVotesAndAnswers = { - for (final vote in controller.currentItems) vote.id!: vote, - }..remove(pollVote.id); - - controller.pollVotes = [...ownVotesAndAnswers.values]; - } -} diff --git a/packages/stream_chat_flutter_core/lib/src/stream_thread_list_controller.dart b/packages/stream_chat_flutter_core/lib/src/stream_thread_list_controller.dart deleted file mode 100644 index d5d5902aab..0000000000 --- a/packages/stream_chat_flutter_core/lib/src/stream_thread_list_controller.dart +++ /dev/null @@ -1,443 +0,0 @@ -import 'dart:async'; -import 'dart:math'; - -import 'package:collection/collection.dart'; -import 'package:flutter/foundation.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_flutter_core/src/paged_value_notifier.dart'; -import 'package:stream_chat_flutter_core/src/stream_thread_list_event_handler.dart'; - -/// The default thread list page limit to load. -const defaultThreadsPagedLimit = 10; - -const _kDefaultBackendPaginationLimit = 30; - -/// {@template streamThreadListController} -/// A controller for a thread list. -/// -/// This class lets you perform tasks such as: -/// * Load initial data. -/// * Load more data using [loadMore]. -/// * Replace the previously loaded threads. -/// {@endtemplate} -class StreamThreadListController extends PagedValueNotifier { - /// {@macro streamThreadListController} - StreamThreadListController({ - required this.client, - StreamThreadListEventHandler? eventHandler, - this.options = const ThreadOptions(), - this.limit = defaultThreadsPagedLimit, - }) : _activeOptions = options, - _eventHandler = eventHandler ?? StreamThreadListEventHandler(), - super(const PagedValue.loading()); - - /// Creates a [StreamThreadListController] from the passed [value]. - StreamThreadListController.fromValue( - super.value, { - required this.client, - StreamThreadListEventHandler? eventHandler, - this.options = const ThreadOptions(), - this.limit = defaultThreadsPagedLimit, - }) : _activeOptions = options, - _eventHandler = eventHandler ?? StreamThreadListEventHandler(); - - /// The Stream client used to perform the queries. - final StreamChatClient client; - - /// The channel event handlers to use for the channels list. - final StreamThreadListEventHandler _eventHandler; - - /// The limit to apply to the thread list. - /// - /// The default is set to [defaultUserPagedLimit]. - final int limit; - - /// The options used to filter the threads. - /// - /// The default is set to [ThreadOptions]. - final ThreadOptions options; - ThreadOptions _activeOptions; - - /// Allows for the change of the [options] at runtime. - /// - /// Use this if you need to support runtime option changes, - /// through custom filters UI. - set options(ThreadOptions options) => _activeOptions = options; - - /// The ids of the threads that have unseen messages. - ValueListenable> get unseenThreadIds => _unseenThreadIds; - final _unseenThreadIds = ValueNotifier>(const {}); - - /// Adds a new thread to the set of unseen thread IDs. - void addUnseenThreadId(String? threadId) { - if (threadId == null) return; - - final currentUnseenThreadIds = {..._unseenThreadIds.value}; - final updatedUnseenThreadIds = {...currentUnseenThreadIds, threadId}; - _unseenThreadIds.value = updatedUnseenThreadIds; - } - - /// Clears the set of unseen thread IDs. - void clearUnseenThreadIds() => _unseenThreadIds.value = const {}; - - @override - Future doInitialLoad() async { - final limit = min( - this.limit * defaultInitialPagedLimitMultiplier, - _kDefaultBackendPaginationLimit, - ); - try { - final response = await client.queryThreads( - options: _activeOptions, - pagination: PaginationParams(limit: limit), - ); - - final results = response.threads; - final nextKey = response.next; - value = PagedValue( - items: results, - nextPageKey: nextKey, - ); - // Start listening to events - _subscribeToThreadListEvents(); - } on StreamChatError catch (error) { - value = PagedValue.error(error); - } catch (error) { - final chatError = StreamChatError(error.toString()); - value = PagedValue.error(chatError); - } - } - - @override - Future loadMore(String nextPageKey) async { - final previousValue = value.asSuccess; - - try { - final response = await client.queryThreads( - options: _activeOptions, - pagination: PaginationParams(limit: limit, next: nextPageKey), - ); - - final results = response.threads; - final previousItems = previousValue.items; - final newItems = previousItems + results; - final next = response.next; - final nextKey = next != null && next.isNotEmpty ? next : null; - value = PagedValue( - items: newItems, - nextPageKey: nextKey, - ); - } on StreamChatError catch (error) { - value = previousValue.copyWith(error: error); - } catch (error) { - final chatError = StreamChatError(error.toString()); - value = previousValue.copyWith(error: chatError); - } - } - - @override - Future refresh({bool resetValue = true}) { - if (resetValue) { - _activeOptions = options; - } - return super.refresh(resetValue: resetValue); - } - - /// Replaces the previously loaded threads with the passed [threads]. - set threads(List threads) { - if (value.isSuccess) { - final currentValue = value.asSuccess; - value = currentValue.copyWith(items: threads); - } else { - value = PagedValue(items: threads); - } - } - - /// Returns the thread with the given [parentMessageId] from the list. - /// - /// Returns `null` if no thread is found. - Thread? getThread({required String? parentMessageId}) { - final currentThreads = [...currentItems]; - final thread = currentThreads.firstWhereOrNull( - (it) => it.parentMessageId == parentMessageId, - ); - - return thread; - } - - /// Updates the given [thread] in the list and moves it to the top if - /// [moveThreadToTop] is `true`. - /// - /// Returns `true` if the thread is updated successfully. Otherwise, `false`. - bool updateThread( - Thread thread, { - bool moveThreadToTop = false, - }) { - final currentThreads = [...currentItems]; - final updateIndex = currentThreads.indexWhere( - (it) => it.parentMessageId == thread.parentMessageId, - ); - - if (updateIndex < 0) return false; - currentThreads[updateIndex] = thread; - - if (moveThreadToTop) { - final updatedThread = currentThreads.removeAt(updateIndex); - currentThreads.insert(0, updatedThread); - } - - threads = currentThreads; - return true; - } - - /// Deletes the thread with the given [parentMessageId] from the list. - /// - /// Returns `true` if the thread is deleted successfully. Otherwise, `false`. - bool deleteThread({required String? parentMessageId}) { - final currentThreads = [...currentItems]; - final removeIndex = currentThreads.indexWhere( - (it) => it.parentMessageId == parentMessageId, - ); - - if (removeIndex < 0) return false; - currentThreads.removeAt(removeIndex); - - threads = currentThreads; - return true; - } - - /// Removes all the threads with the given [channelCid] from the list. - /// - /// This is useful when you want to remove all the threads from a channel - /// when the channel is deleted. - void deleteThreadByChannelCid({required String channelCid}) { - final oldThreads = [...currentItems]; - final newThreads = [ - ...oldThreads.where((it) => it.channelCid != channelCid), - ]; - - threads = newThreads; - } - - /// Updates the parent message of a thread. - /// - /// Returns `true` if matching parent message was found and was updated, - /// `false` otherwise. - bool updateParent(Message parent) { - final thread = getThread(parentMessageId: parent.id); - if (thread == null) return false; // No thread found for the message. - - final updatedThread = thread.updateParent(parent); - - return updateThread(updatedThread); - } - - /// Deletes the given [reply] from the appropriate thread. - bool deleteReply(Message reply) { - final thread = getThread(parentMessageId: reply.parentId); - if (thread == null) return false; // No thread found for the message. - - final updatedThread = thread.deleteReply(reply); - - return updateThread(updatedThread); - } - - /// Inserts/updates the given [reply] into the appropriate thread. - bool upsertReply(Message reply) { - final thread = getThread(parentMessageId: reply.parentId); - if (thread == null) return false; // No thread found for the message. - - final updatedThread = thread.upsertReply(reply); - - return updateThread(updatedThread); - } - - /// Event listener, which can be set in order to listen - /// [client] web-socket events. - /// - /// Return `true` if the event is handled. Return `false` to - /// allow the event to be handled internally. - bool Function(Event event)? eventListener; - - StreamSubscription? _threadEventSubscription; - - // Subscribes to the thread list events. - void _subscribeToThreadListEvents() { - if (_threadEventSubscription != null) { - _unsubscribeFromThreadListEvents(); - } - - _threadEventSubscription = client.on().listen((event) { - // Only handle the event if the value is in success state. - if (value.isNotSuccess) return; - - // Returns early if the event is already handled by the listener. - if (eventListener?.call(event) ?? false) return; - - final handlerFunc = switch (event.type) { - EventType.threadUpdated => _eventHandler.onThreadUpdated, - EventType.connectionRecovered => _eventHandler.onConnectionRecovered, - EventType.notificationThreadMessageNew => - _eventHandler.onNotificationThreadMessageNew, - EventType.messageRead => _eventHandler.onMessageRead, - EventType.notificationMarkUnread => - _eventHandler.onNotificationMarkUnread, - EventType.channelDeleted => _eventHandler.onChannelDeleted, - EventType.channelTruncated => _eventHandler.onChannelTruncated, - EventType.messageNew => _eventHandler.onMessageNew, - EventType.messageUpdated => _eventHandler.onMessageUpdated, - EventType.messageDeleted => _eventHandler.onMessageDeleted, - EventType.reactionNew => _eventHandler.onReactionNew, - EventType.reactionUpdated => _eventHandler.onReactionUpdated, - EventType.reactionDeleted => _eventHandler.onReactionDeleted, - _ => null, - }; - - return handlerFunc?.call(event, this); - }); - } - - // Unsubscribes from all channel list events. - void _unsubscribeFromThreadListEvents() { - if (_threadEventSubscription != null) { - _threadEventSubscription!.cancel(); - _threadEventSubscription = null; - } - } - - /// Pauses all subscriptions added to this composite. - void pauseEventsSubscription([Future? resumeSignal]) { - _threadEventSubscription?.pause(resumeSignal); - } - - /// Resumes all subscriptions added to this composite. - void resumeEventsSubscription() { - _threadEventSubscription?.resume(); - } - - @override - void dispose() { - _unsubscribeFromThreadListEvents(); - super.dispose(); - } -} - -/// Helper extension on [Thread] to update the parent message and replies. -extension ThreadExtension on Thread { - /// Updates the parent message of a Thread. - Thread updateParent(Message parent) { - // Skip update if [parent] is not related to this Thread. - if (parentMessageId != parent.id) return this; - - return copyWith( - parentMessage: parent, - deletedAt: parent.deletedAt, - updatedAt: parent.updatedAt, - ); - } - - /// Inserts a new [reply] (or updates and existing one) into the Thread. - Thread upsertReply(Message reply) { - // Skip update if [reply] is not related to this Thread. - if (parentMessageId != reply.parentId) return this; - - final updatedReplies = _upsertMessageInList(reply, latestReplies); - final isInsert = updatedReplies.length > latestReplies.length; - final sortedUpdatedReplies = updatedReplies.sortedBy( - (it) => it.localCreatedAt ?? it.createdAt, - ); - - final lastMessage = sortedUpdatedReplies.lastOrNull; - final lastMessageAt = lastMessage?.localCreatedAt ?? lastMessage?.createdAt; - - // Update read counts (+1 for each non-sender of the message). - final updatedRead = isInsert ? _updateReadCounts(read, reply) : read; - - return copyWith( - updatedAt: lastMessageAt, - lastMessageAt: lastMessageAt, - latestReplies: sortedUpdatedReplies, - read: updatedRead, - ); - } - - /// Deletes the given [reply] from the Thread. - Thread deleteReply(Message reply) { - // Skip update if [reply] is not related to this Thread. - if (parentMessageId != reply.parentId) return this; - - final updatedReplies = latestReplies.where((it) => it.id != reply.id); - final sortedUpdatedReplies = updatedReplies.sortedBy( - (it) => it.localCreatedAt ?? it.createdAt, - ); - - final lastMessage = sortedUpdatedReplies.lastOrNull; - final lastMessageAt = lastMessage?.localCreatedAt ?? lastMessage?.createdAt; - - return copyWith( - updatedAt: lastMessageAt, - lastMessageAt: lastMessageAt, - latestReplies: sortedUpdatedReplies, - ); - } - - /// Marks the given thread as read by the given [user]. - Thread markAsReadByUser(User user, DateTime createdAt) { - final updatedRead = read?.map((read) { - if (read.user.id == user.id) { - return read.copyWith( - user: user, - unreadMessages: 0, - lastRead: createdAt, - ); - } - return read; - }).toList(); - - return copyWith(read: updatedRead); - } - - /// Marks the given thread as unread by the given [user]. - Thread markAsUnreadByUser(User user, DateTime createdAt) { - final updatedRead = read?.map((read) { - if (read.user.id == user.id) { - return read.copyWith( - user: user, - // Update this value to what the backend returns (when implemented) - unreadMessages: read.unreadMessages + 1, - lastRead: createdAt, - ); - } - return read; - }).toList(); - - return copyWith(read: updatedRead); - } - - List _upsertMessageInList( - Message newMessage, - List messages, - ) { - // Insert if message is not present in the list. - if (messages.none((it) => it.id == newMessage.id)) { - return [...messages, newMessage]; - } - - // Otherwise, update the message. - return [ - ...messages.map((message) { - if (message.id == newMessage.id) return newMessage; - return message; - }), - ]; - } - - List? _updateReadCounts(List? read, Message reply) { - return read?.map((userRead) { - // Skip the sender of the message. - if (userRead.user.id == reply.user?.id) return userRead; - // Increment the unread count for the non-sender. - return userRead.copyWith(unreadMessages: userRead.unreadMessages + 1); - }).toList(); - } -} diff --git a/packages/stream_chat_flutter_core/lib/src/stream_thread_list_event_handler.dart b/packages/stream_chat_flutter_core/lib/src/stream_thread_list_event_handler.dart deleted file mode 100644 index d01279665c..0000000000 --- a/packages/stream_chat_flutter_core/lib/src/stream_thread_list_event_handler.dart +++ /dev/null @@ -1,277 +0,0 @@ -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_flutter_core/src/stream_thread_list_controller.dart'; - -/// Contains handlers that are called from [StreamThreadListController] for -/// certain [Event]s. -/// -/// This class can be mixed in or extended to create custom overrides. -mixin class StreamThreadListEventHandler { - /// Function which gets called for the event [EventType.threadUpdated]. - /// - /// This event is fired when a thread is updated. - /// - /// By default, this does nothing. Override this method to handle this event. - void onThreadUpdated(Event event, StreamThreadListController controller) { - // no-op - } - - /// Function which gets called for the event [EventType.connectionRecovered]. - /// - /// This event is fired when the client web-socket connection recovers. - /// - /// By default, this refreshes the whole thread list. - void onConnectionRecovered( - Event event, - StreamThreadListController controller, - ) { - controller.refresh(); - } - - /// Function which gets called for the event [EventType.reactionNew]. - /// - /// - /// This event is fired when a new reaction is added to a message. - /// - /// By default, this updates the parent or reply message in the thread list. - void onReactionNew( - Event event, - StreamThreadListController controller, - ) { - final message = event.message; - if (message == null) return; - - _updateParentOrReply(message, controller); - } - - /// Function which gets called for the event [EventType.reactionUpdated]. - /// - /// This event is fired when a reaction is updated. - /// - /// By default, this updates the parent or reply message in the thread list. - void onReactionUpdated( - Event event, - StreamThreadListController controller, - ) { - final message = event.message; - if (message == null) return; - - _updateParentOrReply(message, controller); - } - - /// Function which gets called for the event [EventType.reactionDeleted]. - /// - /// This event is fired when a reaction is deleted. - /// - /// By default, this updates the parent or reply message in the thread list. - void onReactionDeleted( - Event event, - StreamThreadListController controller, - ) { - final message = event.message; - if (message == null) return; - - _updateParentOrReply(message, controller); - } - - /// Function which gets called for the event - /// [EventType.notificationThreadMessageNew]. - /// - /// This event is fired when a new message is added to a thread. - /// - /// By default, this updates the unread count of the channel. - void onNotificationThreadMessageNew( - Event event, - StreamThreadListController controller, - ) { - final message = event.message; - if (message == null) return; - - final parentMessageId = message.parentId; - final thread = controller.getThread(parentMessageId: parentMessageId); - - // Thread is not (yet) loaded, just update the state of unseenThreadIds. - if (thread == null) return controller.addUnseenThreadId(parentMessageId); - - // Loaded thread is already being handled by the [onMessageNew] and - // [onMessageUpdated] handlers. - return; - } - - /// Function which gets called for the event [EventType.messageNew]. - /// - /// This event is fired when a new message is added. - /// - /// By default, this updates the parent or reply message in the thread list. - void onMessageNew( - Event event, - StreamThreadListController controller, - ) { - final message = event.message; - if (message == null) return; - - _updateParentOrReply(message, controller); - } - - /// Function which gets called for the event [EventType.messageUpdated]. - /// - /// This event is fired when a message is updated. - /// - /// By default, this updates the parent or reply message in the thread list. - void onMessageUpdated( - Event event, - StreamThreadListController controller, - ) { - final message = event.message; - if (message == null) return; - - _updateParentOrReply(message, controller); - } - - /// Function which gets called for the event [EventType.messageDeleted]. - /// - /// This event is fired when a message is deleted. - /// - /// By default, this updates or deletes the parent/reply message in the - /// thread list based on the [Event.hardDelete] value. - void onMessageDeleted( - Event event, - StreamThreadListController controller, - ) { - final message = event.message; - if (message == null) return; - - _deleteParentOrReply( - message, - controller, - isHardDelete: event.hardDelete ?? false, - ); - } - - /// Function which gets called for the event [EventType.channelDeleted]. - /// - /// This event is fired when a channel is deleted. - /// - /// By default, this deletes all threads associated with the channel. - void onChannelDeleted( - Event event, - StreamThreadListController controller, - ) { - final channelCid = event.cid ?? event.channel?.cid; - if (channelCid == null) return; - - return controller.deleteThreadByChannelCid(channelCid: channelCid); - } - - /// Function which gets called for the event [EventType.channelTruncated]. - /// - /// This event is fired when a channel is truncated. - /// - /// By default, this deletes all threads associated with the channel. - void onChannelTruncated( - Event event, - StreamThreadListController controller, - ) { - final channelCid = event.cid ?? event.channel?.cid; - if (channelCid == null) return; - - return controller.deleteThreadByChannelCid(channelCid: channelCid); - } - - /// Function which gets called for the event [EventType.messageRead]. - /// - /// This event is fired when a message is marked as read. - /// - /// By default, this updates the read state of the thread. - void onMessageRead( - Event event, - StreamThreadListController controller, - ) { - final thread = event.thread; - if (thread == null) return; - - final user = event.user; - if (user == null) return; - - final createdAt = event.createdAt; - - return _markThreadAsRead(thread, user, createdAt, controller); - } - - /// Function which gets called for the event - /// [EventType.notificationMarkUnread]. - /// - /// This event is fired when a message is marked as unread. - /// - /// By default, this updates the read state of the thread. - void onNotificationMarkUnread( - Event event, - StreamThreadListController controller, - ) { - final thread = event.thread; - if (thread == null) return; - - final user = event.user; - if (user == null) return; - - final createdAt = event.createdAt; - - return _markThreadAsUnread(thread, user, createdAt, controller); - } - - void _markThreadAsRead( - Thread threadInfo, - User user, - DateTime createdAt, - StreamThreadListController controller, - ) { - final parentMessageId = threadInfo.parentMessageId; - final thread = controller.getThread(parentMessageId: parentMessageId); - if (thread == null) return; - - final updatedThread = thread.markAsReadByUser(user, createdAt); - - controller.updateThread(updatedThread); - } - - void _markThreadAsUnread( - Thread threadInfo, - User user, - DateTime createdAt, - StreamThreadListController controller, - ) { - final parentMessageId = threadInfo.parentMessageId; - final thread = controller.getThread(parentMessageId: parentMessageId); - if (thread == null) return; - - final updatedThread = thread.markAsUnreadByUser(user, createdAt); - - controller.updateThread(updatedThread); - } - - bool _updateParentOrReply( - Message message, - StreamThreadListController controller, - ) { - return controller.updateParent(message) || controller.upsertReply(message); - } - - bool _deleteParentOrReply( - Message message, - StreamThreadListController controller, { - bool isHardDelete = false, - }) { - // If the message is hard deleted, and it is a parent message, delete the - // thread. Otherwise, remove the message from the thread replies. - if (isHardDelete) { - final parentMessageId = message.parentId; - if (parentMessageId == null) { - return controller.deleteThread(parentMessageId: message.id); - } - - return controller.deleteReply(message); - } - - // Otherwise, update the parent or reply message. - return _updateParentOrReply(message, controller); - } -} diff --git a/packages/stream_chat_flutter_core/lib/src/stream_user_list_controller.dart b/packages/stream_chat_flutter_core/lib/src/stream_user_list_controller.dart deleted file mode 100644 index 466b61776d..0000000000 --- a/packages/stream_chat_flutter_core/lib/src/stream_user_list_controller.dart +++ /dev/null @@ -1,165 +0,0 @@ -import 'dart:async'; -import 'dart:math'; - -import 'package:stream_chat/stream_chat.dart' hide Success; -import 'package:stream_chat_flutter_core/src/paged_value_notifier.dart'; - -/// The default channel page limit to load. -const defaultUserPagedLimit = 10; - -const _kDefaultBackendPaginationLimit = 30; - -/// A controller for a user list. -/// -/// This class lets you perform tasks such as: -/// * Load initial data. -/// * Load more data using [loadMore]. -/// * Replace the previously loaded users. -class StreamUserListController extends PagedValueNotifier { - /// Creates a Stream user list controller. - /// - /// * `client` is the Stream chat client to use for the channels list. - /// - /// * `filter` is the query filters to use. - /// - /// * `sort` is the sorting used for the users matching the filters. - /// - /// * `presence` sets whether you'll receive user presence updates via the - /// websocket events. - /// - /// * `limit` is the limit to apply to the user list. - StreamUserListController({ - required this.client, - this.filter, - this.sort, - this.presence = true, - this.limit = defaultUserPagedLimit, - }) : _activeFilter = filter, - _activeSort = sort, - super(const PagedValue.loading()); - - /// Creates a [StreamUserListController] from the passed [value]. - StreamUserListController.fromValue( - super.value, { - required this.client, - this.filter, - this.sort, - this.presence = true, - this.limit = defaultUserPagedLimit, - }) : _activeFilter = filter, - _activeSort = sort; - - /// The client to use for the channels list. - final StreamChatClient client; - - /// The query filters to use. - /// - /// You can query on any of the custom fields you've defined on the [User]. - /// - /// You can also filter other built-in channel fields. - final Filter? filter; - Filter? _activeFilter; - - /// The sorting used for the users matching the filters. - /// - /// Sorting is based on field and direction, multiple sorting options - /// can be provided. - /// - /// Direction can be ascending or descending. - final List? sort; - List? _activeSort; - - /// If true you’ll receive user presence updates via the websocket events - final bool presence; - - /// The limit to apply to the user list. The default is set to - /// [defaultUserPagedLimit]. - final int limit; - - /// Allows for the change of filters used for user queries. - /// - /// Use this if you need to support runtime filter changes, - /// through custom filters UI. - set filter(Filter? value) => _activeFilter = value; - - /// Allows for the change of the query sort used for user queries. - /// - /// Use this if you need to support runtime sort changes, - /// through custom sort UI. - set sort(List? value) => _activeSort = value; - - @override - Future doInitialLoad() async { - final limit = min( - this.limit * defaultInitialPagedLimitMultiplier, - _kDefaultBackendPaginationLimit, - ); - try { - final userResponse = await client.queryUsers( - filter: _activeFilter, - sort: _activeSort, - presence: presence, - pagination: PaginationParams(limit: limit), - ); - - final users = userResponse.users; - final nextKey = users.length < limit ? null : users.length; - value = PagedValue( - items: users, - nextPageKey: nextKey, - ); - } on StreamChatError catch (error) { - value = PagedValue.error(error); - } catch (error) { - final chatError = StreamChatError(error.toString()); - value = PagedValue.error(chatError); - } - } - - @override - Future loadMore(int nextPageKey) async { - final previousValue = value.asSuccess; - - try { - final userResponse = await client.queryUsers( - filter: _activeFilter, - sort: _activeSort, - presence: presence, - pagination: PaginationParams(limit: limit, offset: nextPageKey), - ); - - final users = userResponse.users; - final previousItems = previousValue.items; - final newItems = previousItems + users; - final nextKey = users.length < limit ? null : newItems.length; - value = PagedValue( - items: newItems, - nextPageKey: nextKey, - ); - } on StreamChatError catch (error) { - value = previousValue.copyWith(error: error); - } catch (error) { - final chatError = StreamChatError(error.toString()); - value = previousValue.copyWith(error: chatError); - } - } - - @override - Future refresh({bool resetValue = true}) { - if (resetValue) { - _activeFilter = filter; - _activeSort = sort; - } - return super.refresh(resetValue: resetValue); - } - - /// Replaces the previously loaded users with the passed [users]. - set users(List users) { - if (value.isSuccess) { - final currentValue = value.asSuccess; - value = currentValue.copyWith(items: users); - } else { - value = PagedValue(items: users); - } - } -} diff --git a/packages/stream_chat_flutter_core/lib/src/typedef.dart b/packages/stream_chat_flutter_core/lib/src/typedef.dart deleted file mode 100644 index 4b69c79c55..0000000000 --- a/packages/stream_chat_flutter_core/lib/src/typedef.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:stream_chat/stream_chat.dart'; - -/// A signature for a callback which exposes an error and returns a function. -/// This Callback can be used in cases where an API failure occurs and the -/// widget is unable to render data. -// TODO: Add stacktrace as a parameter in v7.0.0 -typedef ErrorBuilder = Widget Function(BuildContext context, Object error); - -/// A Signature for a handler function which will expose a [event]. -typedef EventHandler = void Function(Event event); diff --git a/packages/stream_chat_flutter_core/lib/stream_chat_flutter_core.dart b/packages/stream_chat_flutter_core/lib/stream_chat_flutter_core.dart deleted file mode 100644 index cbdd0601c0..0000000000 --- a/packages/stream_chat_flutter_core/lib/stream_chat_flutter_core.dart +++ /dev/null @@ -1,24 +0,0 @@ -library stream_chat_flutter_core; - -export 'package:connectivity_plus/connectivity_plus.dart'; -export 'package:stream_chat/stream_chat.dart'; - -export 'src/better_stream_builder.dart'; -export 'src/lazy_load_scroll_view.dart'; -export 'src/message_list_core.dart' hide MessageListCoreState; -export 'src/message_text_field_controller.dart'; -export 'src/paged_value_notifier.dart' - show PagedValueListenableBuilder, PagedValue, PagedValueNotifier; -export 'src/paged_value_scroll_view.dart'; -export 'src/stream_channel.dart'; -export 'src/stream_channel_list_controller.dart'; -export 'src/stream_channel_list_event_handler.dart'; -export 'src/stream_chat_core.dart'; -export 'src/stream_member_list_controller.dart'; -export 'src/stream_message_input_controller.dart'; -export 'src/stream_message_search_list_controller.dart'; -export 'src/stream_poll_controller.dart'; -export 'src/stream_poll_vote_list_controller.dart'; -export 'src/stream_thread_list_controller.dart'; -export 'src/stream_user_list_controller.dart'; -export 'src/typedef.dart'; diff --git a/packages/stream_chat_flutter_core/pubspec.yaml b/packages/stream_chat_flutter_core/pubspec.yaml deleted file mode 100644 index 5563dbb913..0000000000 --- a/packages/stream_chat_flutter_core/pubspec.yaml +++ /dev/null @@ -1,41 +0,0 @@ -name: stream_chat_flutter_core -homepage: https://github.com/GetStream/stream-chat-flutter -description: Stream Chat official Flutter SDK Core. Build your own chat experience using Dart and Flutter. -version: 9.4.0 -repository: https://github.com/GetStream/stream-chat-flutter -issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues - -# Note: The environment configuration and dependency versions are managed by Melos. -# -# Do not edit them manually. -# -# Steps to update dependencies: -# 1. Modify the version in the melos.yaml file. -# 2. Run `melos bootstrap` to apply changes. -# -# Steps to add a new dependency: -# 1. Add the dependency to this list. -# 2. Add it to the melos.yaml file for future updates. - -environment: - sdk: ^3.6.2 - flutter: ">=3.27.4" - -dependencies: - collection: ^1.17.2 - connectivity_plus: ^6.0.3 - flutter: - sdk: flutter - freezed_annotation: ^2.4.1 - meta: ^1.9.1 - rxdart: ^0.28.0 - stream_chat: ^9.4.0 - -dev_dependencies: - build_runner: ^2.4.9 - fake_async: ^1.3.1 - flutter_test: - sdk: flutter - freezed: ^2.4.2 - mocktail: ^1.0.0 - diff --git a/packages/stream_chat_flutter_core/test/lazy_load_scroll_view_test.dart b/packages/stream_chat_flutter_core/test/lazy_load_scroll_view_test.dart deleted file mode 100644 index 86b39e2328..0000000000 --- a/packages/stream_chat_flutter_core/test/lazy_load_scroll_view_test.dart +++ /dev/null @@ -1,282 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter_core/src/lazy_load_scroll_view.dart'; - -void main() { - testWidgets( - 'should render LazyLoadScrollView if child is provided', - (tester) async { - const lazyLoadScrollViewKey = Key('lazyLoadScrollView'); - const lazyLoadScrollView = LazyLoadScrollView( - key: lazyLoadScrollViewKey, - child: Offstage(), - ); - - await tester.pumpWidget(lazyLoadScrollView); - - expect(find.byKey(lazyLoadScrollViewKey), findsOneWidget); - }, - ); - - testWidgets( - 'should render LazyLoadScrollView if child is provided', - (tester) async { - const lazyLoadScrollViewKey = Key('lazyLoadScrollView'); - const childKey = Key('childKey'); - const lazyLoadScrollView = LazyLoadScrollView( - key: lazyLoadScrollViewKey, - child: Offstage(key: childKey), - ); - - await tester.pumpWidget(lazyLoadScrollView); - - expect(find.byKey(lazyLoadScrollViewKey), findsOneWidget); - expect(find.byKey(childKey), findsOneWidget); - }, - ); - - testWidgets( - 'should call onPageScrollStart if child scrollView scrolls', - (tester) async { - const lazyLoadScrollViewKey = Key('lazyLoadScrollView'); - const childListViewKey = Key('childListView'); - - var onPageScrollStartCalled = false; - - final lazyLoadScrollView = LazyLoadScrollView( - key: lazyLoadScrollViewKey, - onPageScrollStart: () { - onPageScrollStartCalled = true; - }, - child: ListView( - key: childListViewKey, - children: List.generate( - 12, - (index) => SizedBox( - height: 100, - child: Text('Item #$index'), - ), - ), - ), - ); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: lazyLoadScrollView, - ), - ); - - expect(find.byKey(lazyLoadScrollViewKey), findsOneWidget); - expect(find.byKey(childListViewKey), findsOneWidget); - expect(onPageScrollStartCalled, isFalse); - - await tester.startGesture(const Offset(100, 100)); - await tester.pump(const Duration(seconds: 1)); - - expect(onPageScrollStartCalled, isTrue); - }, - ); - - testWidgets( - 'should call onPageScrollStart, onPageScrollEnd ' - 'if child scrollView starts scrolling and ends', - (tester) async { - const lazyLoadScrollViewKey = Key('lazyLoadScrollView'); - const childListViewKey = Key('childListView'); - - var onPageScrollStartCalled = false; - var onPageScrollEndCalled = false; - - final lazyLoadScrollView = LazyLoadScrollView( - key: lazyLoadScrollViewKey, - onPageScrollStart: () { - onPageScrollStartCalled = true; - }, - onPageScrollEnd: () { - onPageScrollEndCalled = true; - }, - child: ListView( - key: childListViewKey, - children: List.generate( - 12, - (index) => SizedBox( - height: 100, - child: Text('Item #$index'), - ), - ), - ), - ); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: lazyLoadScrollView, - ), - ); - - expect(find.byKey(lazyLoadScrollViewKey), findsOneWidget); - expect(find.byKey(childListViewKey), findsOneWidget); - expect(onPageScrollStartCalled, isFalse); - expect(onPageScrollEndCalled, isFalse); - - final gesture = await tester.createGesture(); - - await gesture.down(const Offset(100, 100)); - await tester.pump(const Duration(seconds: 1)); - - expect(onPageScrollStartCalled, isTrue); - - await gesture.up(); - await tester.pump(const Duration(seconds: 1)); - - expect(onPageScrollEndCalled, isTrue); - }, - ); - - testWidgets( - 'should call onInBetweenOfPage if child scrollView ' - 'scroll position is in-between of view', - (tester) async { - const lazyLoadScrollViewKey = Key('lazyLoadScrollView'); - const childListViewKey = Key('childListView'); - - var onInBetweenOfPageCalled = false; - - final lazyLoadScrollView = LazyLoadScrollView( - key: lazyLoadScrollViewKey, - onInBetweenOfPage: () async { - onInBetweenOfPageCalled = true; - }, - child: ListView( - key: childListViewKey, - children: List.generate( - 12, - (index) => SizedBox( - height: 100, - child: Text('Item #$index'), - ), - ), - ), - ); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: lazyLoadScrollView, - ), - ); - - expect(find.byKey(lazyLoadScrollViewKey), findsOneWidget); - expect(find.byKey(childListViewKey), findsOneWidget); - expect(onInBetweenOfPageCalled, isFalse); - - final gesture = await tester.createGesture(); - - await gesture.down(const Offset(100, 100)); - await tester.pump(const Duration(seconds: 1)); - await gesture.moveBy(const Offset(-200, -200)); - await tester.pump(const Duration(seconds: 1)); - - expect(onInBetweenOfPageCalled, isTrue); - }, - ); - - testWidgets( - 'should call onStartOfPage if child scrollView ' - 'tends to reach the start', - (tester) async { - const lazyLoadScrollViewKey = Key('lazyLoadScrollView'); - const childListViewKey = Key('childListView'); - - var onStartOfPageCalled = false; - - final lazyLoadScrollView = LazyLoadScrollView( - key: lazyLoadScrollViewKey, - onStartOfPage: () async { - onStartOfPageCalled = true; - }, - child: ListView( - key: childListViewKey, - children: List.generate( - 12, - (index) => SizedBox( - height: 100, - child: Text('Item #$index'), - ), - ), - ), - ); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: lazyLoadScrollView, - ), - ); - - expect(find.byKey(lazyLoadScrollViewKey), findsOneWidget); - expect(find.byKey(childListViewKey), findsOneWidget); - expect(onStartOfPageCalled, isFalse); - - final gesture = await tester.createGesture(); - - await gesture.down(const Offset(100, 100)); - await tester.pump(const Duration(seconds: 1)); - await gesture.moveBy(const Offset(-200, -200)); - await tester.pump(const Duration(seconds: 1)); - await gesture.moveBy(const Offset(201, 201)); - await tester.pump(const Duration(seconds: 1)); - - expect(onStartOfPageCalled, isTrue); - }, - ); - - testWidgets( - 'should call onEndOfPage if child scrollView ' - 'tends to reach the end', - (tester) async { - const lazyLoadScrollViewKey = Key('lazyLoadScrollView'); - const childListViewKey = Key('childListView'); - - var onEndOfPageCalled = false; - - final lazyLoadScrollView = LazyLoadScrollView( - key: lazyLoadScrollViewKey, - onEndOfPage: () async { - onEndOfPageCalled = true; - }, - child: ListView( - key: childListViewKey, - children: List.generate( - 12, - (index) => SizedBox( - height: 100, - child: Text('Item #$index'), - ), - ), - ), - ); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: lazyLoadScrollView, - ), - ); - - expect(find.byKey(lazyLoadScrollViewKey), findsOneWidget); - expect(find.byKey(childListViewKey), findsOneWidget); - expect(onEndOfPageCalled, isFalse); - - final gesture = await tester.createGesture(); - - await gesture.down(const Offset(100, 100)); - await tester.pump(const Duration(seconds: 1)); - await gesture.moveBy(const Offset(-601, -601)); - await tester.pump(const Duration(seconds: 1)); - - expect(onEndOfPageCalled, isTrue); - }, - ); -} diff --git a/packages/stream_chat_flutter_core/test/message_list_core_test.dart b/packages/stream_chat_flutter_core/test/message_list_core_test.dart deleted file mode 100644 index d4c92f760b..0000000000 --- a/packages/stream_chat_flutter_core/test/message_list_core_test.dart +++ /dev/null @@ -1,436 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/widgets.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter_core/src/message_list_core.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -import 'mocks.dart'; - -void main() { - List _generateMessages({ - int count = 3, - int offset = 0, - bool threads = false, - }) { - final users = List.generate(count, (index) { - index = count + offset; - return User(id: 'testUserId$index'); - }); - final messages = List.generate( - count, - (index) { - index = index + offset; - return Message( - id: 'testMessageId$index', - type: 'testType', - user: users[index], - createdAt: DateTime.now(), - replyCount: index, - updatedAt: DateTime.now(), - extraData: const {'extra_test_field': 'extraTestData'}, - text: 'Dummy text #$index', - pinned: true, - pinnedAt: DateTime.now(), - pinnedBy: User(id: 'testUserId$index'), - ); - }, - ); - final threadMessages = List.generate( - count, - (index) { - index = index + offset; - return Message( - id: 'testThreadMessageId$index', - type: 'testType', - user: users[index], - parentId: messages[0].id, - createdAt: DateTime.now(), - replyCount: index, - updatedAt: DateTime.now(), - extraData: const {'extra_test_field': 'extraTestData'}, - text: 'Dummy text #$index', - pinned: true, - pinnedAt: DateTime.now(), - pinnedBy: User(id: 'testUserId$index'), - ); - }, - ); - return threads ? threadMessages : messages; - } - - testWidgets( - '''should throw if MessageListCore is used where StreamChannel is not present in the widget tree''', - (tester) async { - const messageListCoreKey = Key('messageListCore'); - final messageListCore = MessageListCore( - key: messageListCoreKey, - messageListBuilder: (_, __) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - ); - - await tester.pumpWidget(messageListCore); - - expect(find.byKey(messageListCoreKey), findsNothing); - expect(tester.takeException(), isInstanceOf()); - }, - ); - - testWidgets( - 'should render MessageListCore if used with StreamChannel as an ancestor', - (tester) async { - const messageListCoreKey = Key('messageListCore'); - final messageListCore = MessageListCore( - key: messageListCoreKey, - messageListBuilder: (_, __) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - ); - - final mockChannel = MockChannel(); - when(() => mockChannel.initialized).thenAnswer((_) => Future.value(true)); - when(() => mockChannel.state.unreadCount).thenReturn(0); - when(() => mockChannel.state.isUpToDate).thenReturn(true); - when(() => mockChannel.state.messagesStream) - .thenAnswer((_) => Stream.value([])); - when(() => mockChannel.state.messages).thenReturn([]); - - await tester.pumpWidget( - StreamChannel( - channel: mockChannel, - child: messageListCore, - ), - ); - - expect(find.byKey(messageListCoreKey), findsOneWidget); - }, - ); - - testWidgets( - 'should assign paginateData callback to MessageListController if passed', - (tester) async { - const messageListCoreKey = Key('messageListCore'); - final controller = MessageListController(); - final messageListCore = MessageListCore( - key: messageListCoreKey, - messageListBuilder: (_, __) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - messageListController: controller, - ); - - expect(controller.paginateData, isNull); - - final mockChannel = MockChannel(); - - when(() => mockChannel.state.isUpToDate).thenReturn(true); - when(() => mockChannel.state.unreadCount).thenReturn(0); - when(() => mockChannel.state.messagesStream) - .thenAnswer((_) => Stream.value([])); - when(() => mockChannel.state.messages).thenReturn([]); - when(() => mockChannel.initialized).thenAnswer((_) => Future.value(true)); - - await tester.pumpWidget( - StreamChannel( - channel: mockChannel, - child: messageListCore, - ), - ); - - expect(find.byKey(messageListCoreKey), findsOneWidget); - expect(controller.paginateData, isNotNull); - }, - ); - - testWidgets( - '''should assign paginateData callback and paginate data correctly if a MessageListController is passed''', - (tester) async { - const messageListCoreKey = Key('messageListCore'); - final controller = MessageListController(); - const paginationLimit = 10; - final messageListCore = MessageListCore( - paginationLimit: paginationLimit, - key: messageListCoreKey, - messageListBuilder: (_, __) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - messageListController: controller, - ); - - expect(controller.paginateData, isNull); - - final mockChannel = MockChannel(); - - when(() => mockChannel.state.isUpToDate).thenReturn(true); - when(() => mockChannel.state.unreadCount).thenReturn(0); - final messages = _generateMessages(); - when(() => mockChannel.state.messages).thenReturn(messages); - when(() => mockChannel.state.messagesStream) - .thenAnswer((_) => Stream.value(messages)); - when(() => mockChannel.state.messages).thenReturn(messages); - when(() => mockChannel.initialized).thenAnswer((_) => Future.value(true)); - - await tester.pumpWidget( - StreamChannel( - channel: mockChannel, - child: messageListCore, - ), - ); - - final finder = find.byKey(messageListCoreKey); - final coreState = tester.firstState(finder); - expect(finder, findsOneWidget); - expect(controller.paginateData, isNotNull); - - await coreState.paginateData(); - - verify(() => mockChannel.query( - messagesPagination: any( - named: 'messagesPagination', - that: wrapMatcher((it) => it.limit == paginationLimit), - ), - preferOffline: any(named: 'preferOffline'), - )).called(1); - }, - ); - - testWidgets( - 'should build error widget if messagesStream emits error', - (tester) async { - const messageListCoreKey = Key('messageListCore'); - const errorWidgetKey = Key('errorWidget'); - final messageListCore = MessageListCore( - key: messageListCoreKey, - messageListBuilder: (_, __) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage( - key: errorWidgetKey, - ), - ); - - final mockChannel = MockChannel(); - - when(() => mockChannel.state.isUpToDate).thenReturn(true); - when(() => mockChannel.initialized).thenAnswer((_) async => true); - - const error = 'Error! Error! Error!'; - when(() => mockChannel.state.messagesStream) - .thenAnswer((_) => Stream.error(error)); - when(() => mockChannel.state.messages).thenReturn([]); - when(() => mockChannel.state.unreadCount).thenReturn(0); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: StreamChannel( - channel: mockChannel, - child: messageListCore, - ), - ), - ); - - await tester.pumpAndSettle(); - - expect(find.byKey(errorWidgetKey), findsOneWidget); - }, - ); - - testWidgets( - 'should build empty widget if the channel is upToDate and ' - 'messagesStream emits empty data', - (tester) async { - const messageListCoreKey = Key('messageListCore'); - const emptyWidgetKey = Key('emptyWidget'); - final messageListCore = MessageListCore( - key: messageListCoreKey, - messageListBuilder: (_, __) => const Offstage(), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => - const Offstage(key: emptyWidgetKey), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - ); - - final mockChannel = MockChannel(); - - when(() => mockChannel.state.isUpToDate).thenReturn(true); - when(() => mockChannel.initialized).thenAnswer((_) async => true); - - const messages = []; - when(() => mockChannel.state.messagesStream) - .thenAnswer((_) => Stream.value(messages)); - when(() => mockChannel.state.messages).thenReturn(messages); - when(() => mockChannel.state.unreadCount).thenReturn(0); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: StreamChannel( - channel: mockChannel, - child: messageListCore, - ), - ), - ); - - await tester.pumpAndSettle(); - - expect(find.byKey(emptyWidgetKey), findsOneWidget); - }, - ); - - testWidgets( - 'should build list widget if the channel is not upToDate and ' - 'messagesStream emits empty data', - (tester) async { - const messageListCoreKey = Key('messageListCore'); - const listWidgetKey = Key('listWidget'); - final messageListCore = MessageListCore( - key: messageListCoreKey, - messageListBuilder: (_, __) => const Offstage(key: listWidgetKey), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - ); - - final mockChannel = MockChannel(); - - when(() => mockChannel.state.isUpToDate).thenReturn(false); - when(() => mockChannel.initialized).thenAnswer((_) async => true); - when(() => mockChannel.query( - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - membersPagination: any(named: 'membersPagination'), - messagesPagination: any(named: 'messagesPagination'), - preferOffline: any(named: 'preferOffline'), - watchersPagination: any(named: 'watchersPagination'), - )).thenAnswer((_) async => ChannelState()); - - const messages = []; - when(() => mockChannel.state.messagesStream) - .thenAnswer((_) => Stream.value(messages)); - when(() => mockChannel.state.messages).thenReturn(messages); - when(() => mockChannel.state.unreadCount).thenReturn(0); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: StreamChannel( - channel: mockChannel, - child: messageListCore, - ), - ), - ); - - await tester.pumpAndSettle(); - - expect(find.byKey(listWidgetKey), findsOneWidget); - }, - ); - - testWidgets( - 'should build list widget and save it for future rebuilds ' - 'if messagesStream emits some data', - (tester) async { - const messageListCoreKey = Key('messageListCore'); - const listWidgetKey = Key('listWidget'); - final messageListCore = MessageListCore( - key: messageListCoreKey, - messageListBuilder: (_, messages) => Container( - key: listWidgetKey, - child: Text( - messages.reversed.map((it) => it.id).join(','), - ), - ), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - ); - - final mockChannel = MockChannel(); - - when(() => mockChannel.state.isUpToDate).thenReturn(true); - when(() => mockChannel.initialized).thenAnswer((_) async => true); - - final messages = _generateMessages(); - when(() => mockChannel.state.messagesStream) - .thenAnswer((_) => Stream.value(messages)); - when(() => mockChannel.state.messages).thenReturn(messages); - when(() => mockChannel.state.unreadCount).thenReturn(0); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: StreamChannel( - channel: mockChannel, - child: messageListCore, - ), - ), - ); - - await tester.pumpAndSettle(); - - expect(find.byKey(listWidgetKey), findsOneWidget); - expect(find.text(messages.map((it) => it.id).join(',')), findsOneWidget); - }, - ); - - testWidgets( - 'should build list widget with thread messages if parentMessage is passed', - (tester) async { - final messages = _generateMessages(threads: true); - final parentMessage = Message(id: messages.first.parentId); - const messageListCoreKey = Key('messageListCore'); - const listWidgetKey = Key('listWidget'); - final messageListCore = MessageListCore( - key: messageListCoreKey, - messageListBuilder: (_, messages) => Container( - key: listWidgetKey, - child: Text( - messages.reversed.map((it) => '${it.parentId}-${it.id}').join(','), - ), - ), - loadingBuilder: (BuildContext context) => const Offstage(), - emptyBuilder: (BuildContext context) => const Offstage(), - errorBuilder: (BuildContext context, Object error) => const Offstage(), - parentMessage: parentMessage, - ); - - final mockChannel = MockChannel(); - - when(() => mockChannel.state.isUpToDate).thenReturn(true); - when(() => mockChannel.initialized).thenAnswer((_) async => true); - - final threads = {parentMessage.id: messages}; - - when(() => mockChannel.state.threads).thenReturn(threads); - when(() => mockChannel.state.threadsStream) - .thenAnswer((_) => Stream.value(threads)); - when(() => mockChannel.state.unreadCount).thenReturn(0); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: StreamChannel( - channel: mockChannel, - child: messageListCore, - ), - ), - ); - - await tester.pumpAndSettle(); - - expect(find.byKey(listWidgetKey), findsOneWidget); - expect( - find.text(messages.map((it) => '${it.parentId}-${it.id}').join(',')), - findsOneWidget, - ); - }, - ); -} diff --git a/packages/stream_chat_flutter_core/test/mocks.dart b/packages/stream_chat_flutter_core/test/mocks.dart deleted file mode 100644 index 7013d4081d..0000000000 --- a/packages/stream_chat_flutter_core/test/mocks.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat/stream_chat.dart'; - -class MockLogger extends Mock implements Logger {} - -class MockClient extends Mock implements StreamChatClient { - MockClient() { - when(() => wsConnectionStatus).thenReturn(ConnectionStatus.connected); - } - - @override - final Logger logger = MockLogger(); - - ClientState? _state; - - @override - ClientState get state => _state ??= MockClientState(); -} - -class MockClientState extends Mock implements ClientState { - OwnUser? _currentUser; - - @override - OwnUser get currentUser => _currentUser ??= OwnUser( - id: 'testUserId', - role: 'admin', - createdAt: DateTime.now(), - updatedAt: DateTime.now(), - ); -} - -class MockChannel extends Mock implements Channel { - ChannelClientState? _state; - - @override - ChannelClientState get state => _state ??= MockChannelState(); - - StreamChatClient? _client; - - @override - StreamChatClient get client => _client ??= MockClient(); -} - -class MockChannelState extends Mock implements ChannelClientState {} diff --git a/packages/stream_chat_flutter_core/test/stream_channel_test.dart b/packages/stream_chat_flutter_core/test/stream_channel_test.dart deleted file mode 100644 index 5ba31042ea..0000000000 --- a/packages/stream_chat_flutter_core/test/stream_channel_test.dart +++ /dev/null @@ -1,288 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -import 'mocks.dart'; - -void main() { - List _generateMessages({ - int count = 3, - int offset = 0, - bool threads = false, - }) { - final users = List.generate(count, (index) { - index = count + offset; - return User(id: 'testUserId$index'); - }); - final messages = List.generate( - count, - (index) { - index = index + offset; - return Message( - id: 'testMessageId$index', - type: 'testType', - user: users[index], - createdAt: DateTime.now(), - replyCount: index, - updatedAt: DateTime.now(), - extraData: const {'extra_test_field': 'extraTestData'}, - text: 'Dummy text #$index', - pinned: true, - pinnedAt: DateTime.now(), - pinnedBy: User(id: 'testUserId$index'), - ); - }, - ); - final threadMessages = List.generate( - count, - (index) { - index = index + offset; - return Message( - id: 'testThreadMessageId$index', - type: 'testType', - user: users[index], - parentId: messages[0].id, - createdAt: DateTime.now(), - replyCount: index, - updatedAt: DateTime.now(), - extraData: const {'extra_test_field': 'extraTestData'}, - text: 'Dummy text #$index', - pinned: true, - pinnedAt: DateTime.now(), - pinnedBy: User(id: 'testUserId$index'), - ); - }, - ); - return threads ? threadMessages : messages; - } - - testWidgets( - 'should render StreamChannel if both channel and child is provided', - (tester) async { - final mockChannel = MockChannel(); - const streamChannelKey = Key('streamChannel'); - const childKey = Key('childKey'); - when(() => mockChannel.initialized).thenAnswer((_) => Future.value(true)); - when(() => mockChannel.state.unreadCount).thenReturn(0); - final streamChannel = StreamChannel( - key: streamChannelKey, - channel: mockChannel, - child: const Offstage(key: childKey), - ); - - await tester.pumpWidget(streamChannel); - - expect(find.byKey(streamChannelKey), findsOneWidget); - expect(find.byKey(childKey), findsOneWidget); - }, - ); - - testWidgets( - 'should build error widget if channel.initialized throws', - (tester) async { - final mockChannel = MockChannel(); - const streamChannelKey = Key('streamChannel'); - const childKey = Key('childKey'); - final streamChannel = StreamChannel( - key: streamChannelKey, - channel: mockChannel, - child: const Offstage(key: childKey), - ); - - const errorMessage = 'Error! Error! Error!'; - final error = DioException( - type: DioExceptionType.badResponse, - message: errorMessage, - requestOptions: RequestOptions(), - ); - when(() => mockChannel.initialized) - .thenAnswer((_) => Future.error(error)); - when(() => mockChannel.state.unreadCount).thenReturn(0); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: streamChannel, - ), - ); - - await tester.pumpAndSettle(); - - expect(find.text(errorMessage), findsOneWidget); - - verify(() => mockChannel.initialized).called(1); - }, - ); - - testWidgets( - 'should show circular progress indicator showLoading is true ' - 'and channel is still not initialized', - (tester) async { - final mockChannel = MockChannel(); - const streamChannelKey = Key('streamChannel'); - const childKey = Key('childKey'); - final streamChannel = StreamChannel( - key: streamChannelKey, - channel: mockChannel, - child: const Offstage(key: childKey), - ); - - when(() => mockChannel.initialized).thenAnswer((_) async => false); - when(() => mockChannel.state.unreadCount).thenReturn(0); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: streamChannel, - ), - ); - - await tester.pump(const Duration(seconds: 1)); - - expect(find.byType(CircularProgressIndicator), findsOneWidget); - - verify(() => mockChannel.initialized).called(1); - }, - ); - - testWidgets( - 'should preload extra messages if initialMessageId is provided', - (tester) async { - final mockChannel = MockChannel(); - const streamChannelKey = Key('streamChannel'); - const childKey = Key('childKey'); - final streamChannel = StreamChannel( - key: streamChannelKey, - channel: mockChannel, - initialMessageId: 'testInitialMessageId', - child: const Offstage(key: childKey), - ); - - when(() => mockChannel.initialized).thenAnswer((_) async => true); - final messages = _generateMessages(); - when(() => mockChannel.query( - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - messagesPagination: any(named: 'messagesPagination'), - membersPagination: any(named: 'membersPagination'), - watchersPagination: any(named: 'watchersPagination'), - preferOffline: any(named: 'preferOffline'), - )).thenAnswer((_) async => ChannelState(messages: messages)); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: streamChannel, - ), - ); - - await tester.pumpAndSettle(); - - verify(() => mockChannel.initialized).called(1); - verify(() => mockChannel.query( - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - messagesPagination: any(named: 'messagesPagination'), - membersPagination: any(named: 'membersPagination'), - watchersPagination: any(named: 'watchersPagination'), - preferOffline: any(named: 'preferOffline'), - )).called(1); - }, - ); - - testWidgets( - 'should rebuild StreamChannel with updated widget data ' - 'on calling setState()', - (tester) async { - StateSetter? _stateSetter; - - var initialMessageId = 'testInitialMessageId'; - - final mockChannel = MockChannel(); - const streamChannelKey = Key('streamChannel'); - const childKey = Key('childKey'); - StreamChannel streamChannelBuilder(String initialMessageId) => - StreamChannel( - key: streamChannelKey, - channel: mockChannel, - initialMessageId: initialMessageId, - child: const Offstage(key: childKey), - ); - - final paginationParams = PaginationParams( - idAround: initialMessageId, - limit: 20, - ); - - when(() => mockChannel.initialized).thenAnswer((_) async => true); - - final messages = _generateMessages(); - - when(() => mockChannel.query( - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - messagesPagination: paginationParams, - membersPagination: any(named: 'membersPagination'), - watchersPagination: any(named: 'watchersPagination'), - preferOffline: any(named: 'preferOffline'), - )).thenAnswer((_) async => ChannelState(messages: messages)); - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: StatefulBuilder( - builder: (context, stateSetter) { - // Assigning stateSetter for rebuilding StreamChannel - _stateSetter = stateSetter; - return streamChannelBuilder(initialMessageId); - }, - ), - ), - ); - - await tester.pumpAndSettle(); - - verify(() => mockChannel.query( - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - messagesPagination: paginationParams, - membersPagination: any(named: 'membersPagination'), - watchersPagination: any(named: 'watchersPagination'), - preferOffline: any(named: 'preferOffline'), - )).called(1); - - _stateSetter?.call(() => initialMessageId = 'testInitialMessageId2'); - - final updatedPaginationParams = paginationParams.copyWith( - idAround: initialMessageId, - ); - - when(() => mockChannel.query( - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - messagesPagination: updatedPaginationParams, - membersPagination: any(named: 'membersPagination'), - watchersPagination: any(named: 'watchersPagination'), - preferOffline: any(named: 'preferOffline'), - )).thenAnswer((_) async => ChannelState(messages: messages)); - - await tester.pumpAndSettle(); - - verify(() => mockChannel.query( - state: any(named: 'state'), - watch: any(named: 'watch'), - presence: any(named: 'presence'), - messagesPagination: updatedPaginationParams, - membersPagination: any(named: 'membersPagination'), - watchersPagination: any(named: 'watchersPagination'), - preferOffline: any(named: 'preferOffline'), - )).called(1); - }, - ); -} diff --git a/packages/stream_chat_flutter_core/test/stream_chat_core_test.dart b/packages/stream_chat_flutter_core/test/stream_chat_core_test.dart deleted file mode 100644 index 4944126165..0000000000 --- a/packages/stream_chat_flutter_core/test/stream_chat_core_test.dart +++ /dev/null @@ -1,616 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:rxdart/rxdart.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; - -import 'mocks.dart'; - -class MockOnBackgroundEventReceived extends Mock { - void call(Event event); -} - -void main() { - testWidgets( - 'should render StreamChatCore if both client and child is provided', - (tester) async { - final mockClient = MockClient(); - const streamChatCoreKey = Key('streamChatCore'); - const childKey = Key('child'); - final streamChatCore = StreamChatCore( - key: streamChatCoreKey, - client: mockClient, - child: const Offstage(key: childKey), - ); - - await tester.pumpWidget(streamChatCore); - - expect(find.byKey(streamChatCoreKey), findsOneWidget); - expect(find.byKey(childKey), findsOneWidget); - }, - ); - - testWidgets( - 'should render StreamChatCore if both client and child is provided', - (tester) async { - final mockClient = MockClient(); - const streamChatCoreKey = Key('streamChatCore'); - const childKey = Key('child'); - final streamChatCore = StreamChatCore( - key: streamChatCoreKey, - client: mockClient, - child: const Offstage(key: childKey), - ); - - await tester.pumpWidget(streamChatCore); - - expect(find.byKey(streamChatCoreKey), findsOneWidget); - expect(find.byKey(childKey), findsOneWidget); - }, - ); - - testWidgets( - 'didChangeAppLifecycleState should call client.closeConnection and return ' - 'if onBackgroundEventReceived is null and the widget lifestyle changes to ' - 'AppLifecycleState.paused', - (tester) async { - final mockClient = MockClient(); - const streamChatCoreKey = Key('streamChatCore'); - const childKey = Key('child'); - final streamChatCore = StreamChatCore( - key: streamChatCoreKey, - client: mockClient, - child: const Offstage(key: childKey), - ); - - await tester.pumpWidget(streamChatCore); - - expect(find.byKey(streamChatCoreKey), findsOneWidget); - expect(find.byKey(childKey), findsOneWidget); - - // ignore: prefer_expression_function_bodies - when(mockClient.closeConnection).thenAnswer((_) async { - return; - }); - - final streamChatCoreState = tester.state( - find.byKey(streamChatCoreKey), - ); - - // ignore: cascade_invocations - streamChatCoreState.didChangeAppLifecycleState(AppLifecycleState.paused); - - verify(mockClient.closeConnection).called(1); - }, - ); - - testWidgets( - 'didChangeAppLifecycleState should subscribe to client events for ' - 'backgroundKeepAlive duration if onBackgroundEventReceived is provided ' - 'and the widget lifestyle changes to AppLifecycleState.paused', - (tester) async { - await tester.runAsync(() async { - final mockClient = MockClient(); - final mockOnBackgroundEventReceived = MockOnBackgroundEventReceived(); - const backgroundKeepAlive = Duration(seconds: 3); - const streamChatCoreKey = Key('streamChatCore'); - const childKey = Key('child'); - final streamChatCore = StreamChatCore( - key: streamChatCoreKey, - client: mockClient, - onBackgroundEventReceived: mockOnBackgroundEventReceived, - backgroundKeepAlive: backgroundKeepAlive, - connectivityStream: Stream.value([ConnectivityResult.mobile]), - child: const Offstage(key: childKey), - ); - - await tester.pumpWidget(streamChatCore); - - expect(find.byKey(streamChatCoreKey), findsOneWidget); - expect(find.byKey(childKey), findsOneWidget); - - final event = Event(type: EventType.any); - when(mockClient.on).thenAnswer((_) => Stream.value(event)); - // ignore: prefer_expression_function_bodies - when(mockClient.closeConnection).thenAnswer((_) async { - return; - }); - - final streamChatCoreState = tester.state( - find.byKey(streamChatCoreKey), - ); - - // ignore: cascade_invocations - streamChatCoreState - .didChangeAppLifecycleState(AppLifecycleState.paused); - - await untilCalled(() => mockOnBackgroundEventReceived.call(event)); - - verify(() => mockOnBackgroundEventReceived.call(event)).called(1); - - await Future.delayed(backgroundKeepAlive); - - verify(mockClient.closeConnection).called(1); - verifyNever(() => mockOnBackgroundEventReceived.call(event)); - }); - }, - ); - - testWidgets( - 'didChangeAppLifecycleState should cancel the backgroundKeepAlive timer ' - 'if it is currently running in case the widget lifestyle changes to ' - 'AppLifecycleState.resume', - (tester) async { - await tester.runAsync(() async { - final mockClient = MockClient(); - final mockOnBackgroundEventReceived = MockOnBackgroundEventReceived(); - const backgroundKeepAlive = Duration(seconds: 3); - const streamChatCoreKey = Key('streamChatCore'); - const childKey = Key('child'); - final streamChatCore = StreamChatCore( - key: streamChatCoreKey, - client: mockClient, - onBackgroundEventReceived: mockOnBackgroundEventReceived, - backgroundKeepAlive: backgroundKeepAlive, - child: const Offstage(key: childKey), - ); - - await tester.pumpWidget(streamChatCore); - - expect(find.byKey(streamChatCoreKey), findsOneWidget); - expect(find.byKey(childKey), findsOneWidget); - - final event = Event(type: EventType.any); - when(mockClient.on).thenAnswer((_) => Stream.value(event)); - - final streamChatCoreState = tester.state( - find.byKey(streamChatCoreKey), - ); - - // ignore: cascade_invocations - streamChatCoreState - .didChangeAppLifecycleState(AppLifecycleState.paused); - - await untilCalled(() => mockOnBackgroundEventReceived.call(event)); - - verify(() => mockOnBackgroundEventReceived.call(event)).called(1); - - streamChatCoreState - .didChangeAppLifecycleState(AppLifecycleState.resumed); - - verifyNever(() => mockOnBackgroundEventReceived.call(event)); - }); - }, - ); - - testWidgets( - 'didChangeAppLifecycleState should call client.connect() ' - 'if the connectionStatus is ConnectionStatus.disconnected in case the ' - 'widget lifestyle changes to AppLifecycleState.resume', - (tester) async { - await tester.runAsync(() async { - final mockClient = MockClient(); - const streamChatCoreKey = Key('streamChatCore'); - const childKey = Key('child'); - final streamChatCore = StreamChatCore( - key: streamChatCoreKey, - client: mockClient, - connectivityStream: Stream.value([ConnectivityResult.mobile]), - child: const Offstage(key: childKey), - ); - - await tester.pumpWidget(streamChatCore); - - expect(find.byKey(streamChatCoreKey), findsOneWidget); - expect(find.byKey(childKey), findsOneWidget); - - final event = Event(type: EventType.any); - when(mockClient.on).thenAnswer((_) => Stream.value(event)); - when(mockClient.openConnection) - .thenAnswer((_) async => OwnUser(id: 'test')); - - // ignore: prefer_expression_function_bodies - when(mockClient.closeConnection).thenAnswer((_) async { - return; - }); - when(() => mockClient.wsConnectionStatus) - .thenReturn(ConnectionStatus.disconnected); - - final streamChatCoreState = tester.state( - find.byKey(streamChatCoreKey), - ); - - // ignore: cascade_invocations - streamChatCoreState - .didChangeAppLifecycleState(AppLifecycleState.paused); - - await Future.delayed(const Duration(seconds: 1)); - - streamChatCoreState - .didChangeAppLifecycleState(AppLifecycleState.resumed); - - verify(mockClient.openConnection).called(1); - }); - }, - ); - - testWidgets( - 'didChangeAppLifecycleState should not call client.openConnection() ' - 'if connection is not available in case the ' - 'widget lifestyle changes to AppLifecycleState.resume', - (tester) async { - await tester.runAsync(() async { - final mockClient = MockClient(); - const streamChatCoreKey = Key('streamChatCore'); - const childKey = Key('child'); - - final event = Event(); - when(mockClient.on).thenAnswer((_) => Stream.value(event)); - when(mockClient.openConnection) - .thenAnswer((_) async => OwnUser(id: 'test')); - - // ignore: prefer_expression_function_bodies - when(mockClient.closeConnection).thenAnswer((_) async { - return; - }); - when(() => mockClient.wsConnectionStatus) - .thenReturn(ConnectionStatus.disconnected); - - final streamChatCore = StreamChatCore( - key: streamChatCoreKey, - client: mockClient, - connectivityStream: Stream.value([ConnectivityResult.none]), - child: const Offstage(key: childKey), - ); - - await tester.pumpWidget(streamChatCore); - - expect(find.byKey(streamChatCoreKey), findsOneWidget); - expect(find.byKey(childKey), findsOneWidget); - - final streamChatCoreState = tester.state( - find.byKey(streamChatCoreKey), - ); - - // ignore: cascade_invocations - streamChatCoreState - .didChangeAppLifecycleState(AppLifecycleState.paused); - - await Future.delayed(const Duration(seconds: 1)); - - streamChatCoreState - .didChangeAppLifecycleState(AppLifecycleState.resumed); - - verifyNever(mockClient.openConnection); - }); - }, - ); - testWidgets( - 'didChangeAppLifecycleState should cancel the backgroundKeepAlive timer ' - 'if it is currently running in case the widget lifestyle changes to ' - 'AppLifecycleState.inactive', - (tester) async { - await tester.runAsync(() async { - final mockClient = MockClient(); - final mockOnBackgroundEventReceived = MockOnBackgroundEventReceived(); - const backgroundKeepAlive = Duration(seconds: 3); - const streamChatCoreKey = Key('streamChatCore'); - const childKey = Key('child'); - final streamChatCore = StreamChatCore( - key: streamChatCoreKey, - client: mockClient, - onBackgroundEventReceived: mockOnBackgroundEventReceived, - backgroundKeepAlive: backgroundKeepAlive, - child: const Offstage(key: childKey), - ); - - await tester.pumpWidget(streamChatCore); - - expect(find.byKey(streamChatCoreKey), findsOneWidget); - expect(find.byKey(childKey), findsOneWidget); - - final event = Event(type: EventType.any); - when(mockClient.on).thenAnswer((_) => Stream.value(event)); - - final streamChatCoreState = tester.state( - find.byKey(streamChatCoreKey), - ); - - // ignore: cascade_invocations - streamChatCoreState - .didChangeAppLifecycleState(AppLifecycleState.paused); - - await untilCalled(() => mockOnBackgroundEventReceived.call(event)); - - verify(() => mockOnBackgroundEventReceived.call(event)).called(1); - - streamChatCoreState - .didChangeAppLifecycleState(AppLifecycleState.inactive); - - verifyNever(() => mockOnBackgroundEventReceived.call(event)); - }); - }, - ); - - testWidgets( - 'didChangeAppLifecycleState should call client.connect() ' - 'if the connectionStatus is ConnectionStatus.disconnected in case the ' - 'widget lifestyle changes to AppLifecycleState.inactive', - (tester) async { - await tester.runAsync(() async { - final mockClient = MockClient(); - const streamChatCoreKey = Key('streamChatCore'); - const childKey = Key('child'); - final streamChatCore = StreamChatCore( - key: streamChatCoreKey, - client: mockClient, - connectivityStream: Stream.value([ConnectivityResult.mobile]), - child: const Offstage(key: childKey), - ); - - await tester.pumpWidget(streamChatCore); - - expect(find.byKey(streamChatCoreKey), findsOneWidget); - expect(find.byKey(childKey), findsOneWidget); - - final event = Event(type: EventType.any); - when(mockClient.on).thenAnswer((_) => Stream.value(event)); - when(mockClient.openConnection) - .thenAnswer((_) async => OwnUser(id: 'test')); - when(mockClient.closeConnection).thenAnswer((_) async {}); - when(() => mockClient.wsConnectionStatus) - .thenReturn(ConnectionStatus.disconnected); - - final streamChatCoreState = tester.state( - find.byKey(streamChatCoreKey), - ); - - // ignore: cascade_invocations - streamChatCoreState - .didChangeAppLifecycleState(AppLifecycleState.paused); - - await Future.delayed(const Duration(seconds: 1)); - - streamChatCoreState - .didChangeAppLifecycleState(AppLifecycleState.inactive); - - verify(mockClient.openConnection).called(1); - }); - }, - ); - - testWidgets( - 'didChangeAppLifecycleState should not call client.openConnection() ' - 'if connection is not available in case the ' - 'widget lifestyle changes to AppLifecycleState.inactive', - (tester) async { - await tester.runAsync(() async { - final mockClient = MockClient(); - const streamChatCoreKey = Key('streamChatCore'); - const childKey = Key('child'); - - final event = Event(); - when(mockClient.on).thenAnswer((_) => Stream.value(event)); - when(mockClient.openConnection) - .thenAnswer((_) async => OwnUser(id: 'test')); - when(mockClient.closeConnection).thenAnswer((_) async {}); - when(() => mockClient.wsConnectionStatus) - .thenReturn(ConnectionStatus.disconnected); - - final streamChatCore = StreamChatCore( - key: streamChatCoreKey, - client: mockClient, - connectivityStream: Stream.value([ConnectivityResult.none]), - child: const Offstage(key: childKey), - ); - - await tester.pumpWidget(streamChatCore); - - expect(find.byKey(streamChatCoreKey), findsOneWidget); - expect(find.byKey(childKey), findsOneWidget); - - final streamChatCoreState = tester.state( - find.byKey(streamChatCoreKey), - ); - - // ignore: cascade_invocations - streamChatCoreState - .didChangeAppLifecycleState(AppLifecycleState.paused); - - await Future.delayed(const Duration(seconds: 1)); - - streamChatCoreState - .didChangeAppLifecycleState(AppLifecycleState.inactive); - - verifyNever(mockClient.openConnection); - }); - }, - ); - - testWidgets( - 'streamChatCoreState.currentUserStream should emit all the user events ' - 'provided by client', - (tester) async { - await tester.runAsync(() async { - final userController = StreamController(); - - final mockClient = MockClient(); - const streamChatCoreKey = Key('streamChatCore'); - const childKey = Key('child'); - final streamChatCore = StreamChatCore( - key: streamChatCoreKey, - client: mockClient, - child: const Offstage(key: childKey), - ); - - await tester.pumpWidget(streamChatCore); - - expect(find.byKey(streamChatCoreKey), findsOneWidget); - expect(find.byKey(childKey), findsOneWidget); - - when(() => mockClient.state.currentUserStream) - .thenAnswer((_) => userController.stream); - - final streamChatCoreState = tester.state( - find.byKey(streamChatCoreKey), - ); - - final ownUser = OwnUser(id: 'testUserId'); - userController.add(ownUser); - - await expectLater( - streamChatCoreState.currentUserStream, - emits(ownUser), - ); - - addTearDown(userController.close); - }); - }, - ); - - testWidgets( - 'should call connect if in foreground and connection is back', - (tester) async { - await tester.runAsync(() async { - final mockClient = MockClient(); - const streamChatCoreKey = Key('streamChatCore'); - const childKey = Key('child'); - final _connectivityController = - BehaviorSubject.seeded([ConnectivityResult.none]); - - final event = Event(); - when(mockClient.on).thenAnswer((_) => Stream.value(event)); - when(mockClient.openConnection) - .thenAnswer((_) async => OwnUser(id: 'test')); - - // ignore: prefer_expression_function_bodies - when(mockClient.closeConnection).thenAnswer((_) async { - return; - }); - when(() => mockClient.wsConnectionStatus) - .thenReturn(ConnectionStatus.disconnected); - - final streamChatCore = StreamChatCore( - key: streamChatCoreKey, - client: mockClient, - connectivityStream: _connectivityController.stream, - child: const Offstage(key: childKey), - ); - - await tester.pumpWidget(streamChatCore); - - expect(find.byKey(streamChatCoreKey), findsOneWidget); - expect(find.byKey(childKey), findsOneWidget); - - _connectivityController.add([ConnectivityResult.mobile]); - - await Future.delayed(const Duration(seconds: 1)); - - verify(mockClient.openConnection).called(1); - - addTearDown(_connectivityController.close); - }); - }, - ); - - testWidgets( - 'should call disconnect if in foreground and connection goes away', - (tester) async { - await tester.runAsync(() async { - final mockClient = MockClient(); - const streamChatCoreKey = Key('streamChatCore'); - const childKey = Key('child'); - final _connectivityController = - BehaviorSubject.seeded([ConnectivityResult.mobile]); - final streamChatCore = StreamChatCore( - key: streamChatCoreKey, - client: mockClient, - connectivityStream: _connectivityController.stream, - child: const Offstage(key: childKey), - ); - - await tester.pumpWidget(streamChatCore); - - expect(find.byKey(streamChatCoreKey), findsOneWidget); - expect(find.byKey(childKey), findsOneWidget); - - final event = Event(); - when(mockClient.on).thenAnswer((_) => Stream.value(event)); - when(mockClient.openConnection) - .thenAnswer((_) async => OwnUser(id: 'test')); - - // ignore: prefer_expression_function_bodies - when(mockClient.closeConnection).thenAnswer((_) async { - return; - }); - when(() => mockClient.wsConnectionStatus) - .thenReturn(ConnectionStatus.connected); - - _connectivityController.add([ConnectivityResult.none]); - - await Future.delayed(const Duration(seconds: 1)); - - verify(mockClient.closeConnection).called(1); - - addTearDown(_connectivityController.close); - }); - }, - ); - - testWidgets( - 'should ignore connectivity in background', - (tester) async { - await tester.runAsync(() async { - final mockClient = MockClient(); - const streamChatCoreKey = Key('streamChatCore'); - const childKey = Key('child'); - final _connectivityController = - BehaviorSubject.seeded([ConnectivityResult.none]); - - final event = Event(); - when(mockClient.on).thenAnswer((_) => Stream.value(event)); - when(mockClient.openConnection) - .thenAnswer((_) async => OwnUser(id: 'test')); - - // ignore: prefer_expression_function_bodies - when(mockClient.closeConnection).thenAnswer((_) async { - return; - }); - when(() => mockClient.wsConnectionStatus) - .thenReturn(ConnectionStatus.disconnected); - - final streamChatCore = StreamChatCore( - key: streamChatCoreKey, - client: mockClient, - connectivityStream: _connectivityController.stream, - child: const Offstage(key: childKey), - ); - - await tester.pumpWidget(streamChatCore); - - expect(find.byKey(streamChatCoreKey), findsOneWidget); - expect(find.byKey(childKey), findsOneWidget); - - final streamChatCoreState = tester.state( - find.byKey(streamChatCoreKey), - ); - - // ignore: cascade_invocations - streamChatCoreState - .didChangeAppLifecycleState(AppLifecycleState.paused); - - await Future.delayed(const Duration(seconds: 1)); - - _connectivityController.add([ConnectivityResult.mobile]); - - await Future.delayed(const Duration(seconds: 1)); - - verifyNever(mockClient.closeConnection); - - addTearDown(_connectivityController.close); - }); - }, - ); -} diff --git a/packages/stream_chat_flutter_core/test/stream_poll_controller_test.dart b/packages/stream_chat_flutter_core/test/stream_poll_controller_test.dart deleted file mode 100644 index ed5282eb63..0000000000 --- a/packages/stream_chat_flutter_core/test/stream_poll_controller_test.dart +++ /dev/null @@ -1,281 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_flutter_core/src/stream_poll_controller.dart'; - -void main() { - group('Initialization Tests', () { - test('Default Initialization', () { - final pollController = StreamPollController(); - expect(pollController.value.name, ''); - expect(pollController.value.options.length, 1); - }); - - test('Initialization with Custom Poll and Config', () { - final poll = Poll(name: 'Initial Poll', options: const [ - PollOption(text: 'Option 1'), - PollOption(text: 'Option 2'), - ]); - const config = PollConfig(nameRange: (min: 2, max: 50)); - final pollController = StreamPollController(poll: poll, config: config); - - expect(pollController.value.name, 'Initial Poll'); - expect(pollController.config.nameRange?.min, 2); - expect(pollController.config.nameRange?.max, 50); - }); - }); - - group('Poll Property Setter Tests', () { - final pollController = StreamPollController(); - - test('Set Question', () { - pollController.question = 'New Question'; - expect(pollController.value.name, 'New Question'); - }); - - test('Set Options', () { - pollController.options = [const PollOption(text: 'Option A')]; - expect(pollController.value.options.length, 1); - }); - - test('Set enforceUniqueVote', () { - pollController.enforceUniqueVote = true; - expect(pollController.value.enforceUniqueVote, isTrue); - }); - - test('Set maxVotesAllowed', () { - pollController.maxVotesAllowed = 5; - expect(pollController.value.maxVotesAllowed, 5); - }); - - test('Set allowSuggestions', () { - pollController.allowSuggestions = true; - expect(pollController.value.allowUserSuggestedOptions, isTrue); - }); - - test('Set votingVisibility', () { - pollController.votingVisibility = VotingVisibility.anonymous; - expect(pollController.value.votingVisibility, VotingVisibility.anonymous); - }); - - test('Set allowComments', () { - pollController.allowComments = true; - expect(pollController.value.allowAnswers, isTrue); - }); - }); - - group('Add/Update/Remove Option Tests', () { - test('Add Option', () { - final pollController = StreamPollController()..addOption('Option 1'); - expect(pollController.value.options.length, 2); - expect(pollController.value.options.last.text, 'Option 1'); - }); - - test('Add Option with Extra Data', () { - final pollController = StreamPollController() - ..addOption( - 'Option 1', - extraData: {'key': 'value'}, - ); - - expect(pollController.value.options.length, 2); - expect(pollController.value.options.last.extraData['key'], 'value'); - }); - - test('Update Option', () { - final pollController = StreamPollController()..addOption('Option 1'); - expect(pollController.value.options.last.text, 'Option 1'); - - pollController.updateOption('Updated Option 1', index: 1); - expect(pollController.value.options.last.text, 'Updated Option 1'); - }); - - test('Remove Option', () { - final pollController = StreamPollController()..removeOption(0); - expect(pollController.value.options.length, 0); - }); - }); - - group('Validation Tests', () { - test('Validate Poll Name Length', () { - final pollController = StreamPollController()..question = 'A' * 100; - final errors = pollController.validateGranularly(); - expect(errors.isEmpty, isFalse); - - final containsNameRangeError = errors - .map((e) => e.mapOrNull(nameRange: (e) => e)) - .nonNulls - .isNotEmpty; - - expect(containsNameRangeError, isTrue); - }); - - test('Validate Unique Options', () { - final pollController = StreamPollController() - ..addOption('Option 1') - ..addOption('Option 1'); - - final errors = pollController.validateGranularly(); - expect(errors.isEmpty, isFalse); - - final containsDuplicateOptions = errors - .map((e) => e.mapOrNull(duplicateOptions: (e) => e)) - .nonNulls - .isNotEmpty; - - expect(containsDuplicateOptions, isTrue); - }); - - test('Validate Options Count', () { - final pollController = StreamPollController()..options = const []; - final errors = pollController.validateGranularly(); - expect(errors.isEmpty, isFalse); - - final containsOptionsRangeError = errors - .map((e) => e.mapOrNull(optionsRange: (e) => e)) - .nonNulls - .isNotEmpty; - - expect(containsOptionsRangeError, isTrue); - }); - - test('Validate Max Votes Allowed', () { - final pollController = StreamPollController() - ..enforceUniqueVote = false - ..maxVotesAllowed = 20; - - final errors = pollController.validateGranularly(); - expect(errors.isEmpty, isFalse); - - final containsMaxVotesAllowedError = errors - .map((e) => e.mapOrNull(maxVotesAllowed: (e) => e)) - .nonNulls - .isNotEmpty; - - expect(containsMaxVotesAllowedError, isTrue); - }); - }); - - group('Reset Tests', () { - test('Reset Poll Without Changing ID', () { - final poll = Poll( - name: 'Initial Poll', - options: const [PollOption(text: 'Option 1')], - ); - - final pollController = StreamPollController(poll: poll) - ..question = 'New Question' - ..addOption('New Option'); - - expect(pollController.value.name, 'New Question'); - expect(pollController.value.options.length, 2); - - pollController.reset(resetId: false); - - expect(pollController.value.name, 'Initial Poll'); - expect(pollController.value.options.length, 1); - expect(pollController.value.id, poll.id); - }); - - test('Reset Poll With New ID', () { - final poll = Poll( - name: 'Initial Poll', - options: const [PollOption(text: 'Option 1')], - ); - - final pollController = StreamPollController(poll: poll) - ..question = 'New Question' - ..addOption('New Option'); - - expect(pollController.value.name, 'New Question'); - expect(pollController.value.options.length, 2); - - pollController.reset(); - - expect(pollController.value.name, 'Initial Poll'); - expect(pollController.value.options.length, 1); - expect(pollController.value.id, isNot(poll.id)); - }); - }); - - group('Sanitization Tests', () { - test('Sanitized Poll Removes New Option IDs', () { - final pollController = StreamPollController() - ..options = [ - const PollOption(id: 'new_id', text: 'New Option'), - ]; - - final sanitizedPoll = pollController.sanitizedPoll; - expect(sanitizedPoll.options.last.id, isNull); - expect(sanitizedPoll.options.last.text, 'New Option'); - }); - - test('Sanitized Poll Preserves Existing Option IDs', () { - final poll = Poll( - name: 'Initial Poll', - options: const [ - PollOption(id: 'existing_id', text: 'Existing Option'), - ], - ); - - final pollController = StreamPollController(poll: poll) - ..options = [ - ...poll.options, - const PollOption(id: 'new_id', text: 'New Option'), - ]; - - final sanitizedPoll = pollController.sanitizedPoll; - expect(sanitizedPoll.options.first.text, 'Existing Option'); - expect(sanitizedPoll.options.first.id, isNotNull); - expect(sanitizedPoll.options.last.text, 'New Option'); - expect(sanitizedPoll.options.last.id, isNull); - }); - }); - - group('Edge Cases and Config Tests', () { - test('Config Allows Unlimited Poll Name Length', () { - final pollController = StreamPollController( - config: const PollConfig(nameRange: null), - )..question = 'A' * 200; - - final errors = pollController.validateGranularly(); - final containsNameRangeError = errors - .map((e) => e.mapOrNull(nameRange: (e) => e)) - .nonNulls - .isNotEmpty; - - expect(containsNameRangeError, isFalse); - }); - - test('Config Allows Unlimited Options', () { - final pollController = StreamPollController( - config: const PollConfig(optionsRange: null), - ); - - for (var i = 0; i < 50; i++) { - pollController.addOption('Option $i'); - } - - final errors = pollController.validateGranularly(); - final containsOptionsRangeError = errors - .map((e) => e.mapOrNull(optionsRange: (e) => e)) - .nonNulls - .isNotEmpty; - - expect(containsOptionsRangeError, isFalse); - }); - - test('Config Allows Unlimited Votes', () { - final pollController = StreamPollController( - config: const PollConfig(allowedVotesRange: null), - )..maxVotesAllowed = 100; - - final errors = pollController.validateGranularly(); - final containsMaxVotesAllowedError = errors - .map((e) => e.mapOrNull(maxVotesAllowed: (e) => e)) - .nonNulls - .isNotEmpty; - - expect(containsMaxVotesAllowedError, isFalse); - }); - }); -} diff --git a/packages/stream_chat_flutter_core/test/stream_thread_list_event_handler_test.dart b/packages/stream_chat_flutter_core/test/stream_thread_list_event_handler_test.dart deleted file mode 100644 index 2dd163d6df..0000000000 --- a/packages/stream_chat_flutter_core/test/stream_thread_list_event_handler_test.dart +++ /dev/null @@ -1,227 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_flutter_core/src/stream_thread_list_controller.dart'; -import 'package:stream_chat_flutter_core/src/stream_thread_list_event_handler.dart'; - -// Mock classes -class MockStreamThreadListController extends Mock - implements StreamThreadListController {} - -class MockEvent extends Mock implements Event {} - -class MockMessage extends Mock implements Message {} - -class MockUser extends Mock implements User {} - -class MockThread extends Mock implements Thread {} - -void main() { - group('StreamThreadListEventHandler', () { - late final handler = StreamThreadListEventHandler(); - late final mockController = MockStreamThreadListController(); - late final mockEvent = MockEvent(); - late final mockMessage = MockMessage(); - late final mockUser = MockUser(); - late final mockThread = MockThread(); - - setUpAll(() { - registerFallbackValue(MockEvent()); - registerFallbackValue(MockMessage()); - registerFallbackValue(MockUser()); - registerFallbackValue(MockThread()); - }); - - // Reset all the mock objects after each test. - tearDown(() { - reset(mockController); - reset(mockEvent); - reset(mockMessage); - reset(mockUser); - reset(mockThread); - }); - - test('onThreadUpdated does nothing by default', () { - handler.onThreadUpdated(mockEvent, mockController); - verifyNoMoreInteractions(mockController); - }); - - test('onConnectionRecovered calls refresh on controller', () { - when(() => mockController.refresh()).thenAnswer((_) async {}); - handler.onConnectionRecovered(mockEvent, mockController); - verify(() => mockController.refresh()); - }); - - test('onReactionNew calls updateParent first for every message', () { - when(() => mockEvent.message).thenReturn(mockMessage); - when(() => mockController.updateParent(mockMessage)).thenReturn(true); - - handler.onReactionNew(mockEvent, mockController); - verify(() => mockController.updateParent(mockMessage)); - verifyNever(() => mockController.upsertReply(mockMessage)); - }); - - test('onReactionNew calls upsertReply if updateParent fails', () { - when(() => mockEvent.message).thenReturn(mockMessage); - when(() => mockController.updateParent(mockMessage)).thenReturn(false); - when(() => mockController.upsertReply(mockMessage)).thenReturn(true); - - handler.onReactionNew(mockEvent, mockController); - verify(() => mockController.updateParent(mockMessage)); - verify(() => mockController.upsertReply(mockMessage)); - }); - - test('onReactionNew does nothing if message is null', () { - when(() => mockEvent.message).thenReturn(null); - handler.onReactionNew(mockEvent, mockController); - verifyNever(() => mockController.updateParent(any())); - verifyNever(() => mockController.upsertReply(any())); - }); - - test( - 'onNotificationThreadMessageNew updates unseenThreadIds if thread null', - () { - when(() => mockEvent.message).thenReturn(mockMessage); - when(() => mockMessage.parentId).thenReturn('parent-id'); - when(() => mockController.getThread( - parentMessageId: any(named: 'parentMessageId'))).thenReturn(null); - - handler.onNotificationThreadMessageNew(mockEvent, mockController); - verify(() => mockController.addUnseenThreadId('parent-id')); - }, - ); - - test('onMessageNew updates parent or reply message', () { - when(() => mockEvent.message).thenReturn(mockMessage); - when(() => mockController.updateParent(any())).thenReturn(false); - when(() => mockController.upsertReply(any())).thenReturn(true); - - handler.onMessageNew(mockEvent, mockController); - verify(() => mockController.updateParent(mockMessage)); - verify(() => mockController.upsertReply(mockMessage)); - }); - - test( - 'onMessageDeleted deletes thread if message is parent and hard deleted', - () { - when(() => mockMessage.id).thenReturn('message-id'); - when(() => mockMessage.parentId).thenReturn(null); - when(() => mockEvent.message).thenReturn(mockMessage); - when(() => mockEvent.hardDelete).thenReturn(true); - when(() => mockController.deleteThread(parentMessageId: 'message-id')) - .thenReturn(true); - - handler.onMessageDeleted(mockEvent, mockController); - verify( - () => mockController.deleteThread(parentMessageId: 'message-id')); - verifyNever(() => mockController.deleteReply(any())); - }, - ); - - test( - 'onMessageDeleted deletes reply if message is reply and hard deleted', - () { - when(() => mockMessage.id).thenReturn('message-id'); - when(() => mockMessage.parentId).thenReturn('parent-id'); - when(() => mockEvent.message).thenReturn(mockMessage); - when(() => mockEvent.hardDelete).thenReturn(true); - when(() => mockController.deleteReply(any())).thenReturn(true); - - handler.onMessageDeleted(mockEvent, mockController); - verify(() => mockController.deleteReply(any())); - verifyNever( - () => mockController.deleteThread(parentMessageId: 'message-id'), - ); - }, - ); - - test( - 'onMessageDeleted updates thread if message is parent and soft deleted', - () { - when(() => mockMessage.id).thenReturn('message-id'); - when(() => mockMessage.parentId).thenReturn(null); - when(() => mockEvent.message).thenReturn(mockMessage); - when(() => mockEvent.hardDelete).thenReturn(false); - when(() => mockController.updateParent(mockMessage)).thenReturn(true); - - handler.onMessageDeleted(mockEvent, mockController); - verify(() => mockController.updateParent(mockMessage)); - verifyNever(() => mockController.upsertReply(mockMessage)); - verifyNever(() => mockController.deleteReply(any())); - verifyNever( - () => mockController.deleteThread(parentMessageId: 'message-id'), - ); - }, - ); - - test( - 'onMessageDeleted updates reply if message is reply and soft deleted', - () { - when(() => mockMessage.id).thenReturn('message-id'); - when(() => mockMessage.parentId).thenReturn('parent-id'); - when(() => mockEvent.message).thenReturn(mockMessage); - when(() => mockEvent.hardDelete).thenReturn(false); - when(() => mockController.updateParent(mockMessage)).thenReturn(false); - when(() => mockController.upsertReply(mockMessage)).thenReturn(true); - - handler.onMessageDeleted(mockEvent, mockController); - verify(() => mockController.updateParent(mockMessage)); - verify(() => mockController.upsertReply(mockMessage)); - verifyNever(() => mockController.deleteReply(any())); - verifyNever( - () => mockController.deleteThread(parentMessageId: 'message-id'), - ); - }, - ); - - test('onChannelDeleted deletes threads by channel cid', () { - when(() => mockEvent.cid).thenReturn('channel-cid'); - - handler.onChannelDeleted(mockEvent, mockController); - verify(() => - mockController.deleteThreadByChannelCid(channelCid: 'channel-cid')); - }); - - test('onChannelTruncated deletes threads by channel cid', () { - when(() => mockEvent.cid).thenReturn('channel-cid'); - - handler.onChannelTruncated(mockEvent, mockController); - verify(() => - mockController.deleteThreadByChannelCid(channelCid: 'channel-cid')); - }); - - test('onMessageRead marks thread as read', () { - when(() => mockThread.copyWith(read: any(named: 'read'))) - .thenReturn(mockThread); - when(() => mockThread.parentMessageId).thenReturn('parent-id'); - when(() => mockEvent.thread).thenReturn(mockThread); - when(() => mockEvent.user).thenReturn(mockUser); - when(() => mockEvent.createdAt).thenReturn(DateTime.now()); - when(() => mockController.getThread(parentMessageId: 'parent-id')) - .thenReturn(mockThread); - when(() => mockController.updateThread(mockThread)).thenReturn(true); - - handler.onMessageRead(mockEvent, mockController); - verify(() => mockController.getThread(parentMessageId: 'parent-id')); - verify(() => mockThread.copyWith(read: any(named: 'read'))); - verify(() => mockController.updateThread(mockThread)); - }); - - test('onNotificationMarkUnread marks thread as unread', () { - when(() => mockThread.copyWith(read: any(named: 'read'))) - .thenReturn(mockThread); - when(() => mockThread.parentMessageId).thenReturn('parent-id'); - when(() => mockEvent.thread).thenReturn(mockThread); - when(() => mockEvent.user).thenReturn(mockUser); - when(() => mockEvent.createdAt).thenReturn(DateTime.now()); - when(() => mockController.getThread(parentMessageId: 'parent-id')) - .thenReturn(mockThread); - when(() => mockController.updateThread(mockThread)).thenReturn(true); - - handler.onNotificationMarkUnread(mockEvent, mockController); - verify(() => mockController.getThread(parentMessageId: 'parent-id')); - verify(() => mockThread.copyWith(read: any(named: 'read'))); - verify(() => mockController.updateThread(mockThread)); - }); - }); -} diff --git a/packages/stream_chat_localizations/.metadata b/packages/stream_chat_localizations/.metadata deleted file mode 100644 index 936336f9ef..0000000000 --- a/packages/stream_chat_localizations/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: d79295af24c3ed621c33713ecda14ad196fd9c31 - channel: stable - -project_type: package diff --git a/packages/stream_chat_localizations/CHANGELOG.md b/packages/stream_chat_localizations/CHANGELOG.md deleted file mode 100644 index 232bf6349f..0000000000 --- a/packages/stream_chat_localizations/CHANGELOG.md +++ /dev/null @@ -1,258 +0,0 @@ -## 9.4.0 - -- Updated minimum Flutter version to 3.27.4 for the SDK. - -## 9.3.0 - -- Added translations for new `slideToCancelLabel` label. -- Added translations for new `holdToRecordLabel` label. - -## 9.2.0 - -- Updated `stream_chat_flutter` dependency to [`9.2.0+1`](https://pub.dev/packages/stream_chat/changelog). - -## 9.1.0 - -- Added translations for new `repliedToLabel` label. -- Added translations for new `newThreadsLabel` label. - -## 9.0.0 - -- Added multiple new localization strings related to poll creation and validation. -- Added multiple new localization strings related to poll message interactions. -- Updated minimum Flutter version to 3.24.5 for the SDK. - -## 8.3.0 - -- Updated `stream_chat_flutter` dependency to [`8.3.0`](https://pub.dev/packages/stream_chat/changelog). - -## 8.2.0 - -- Updated `stream_chat_flutter` dependency to [`8.2.0`](https://pub.dev/packages/stream_chat/changelog). - -## 8.1.0 - -- Updated `stream_chat_flutter` dependency to [`8.1.0`](https://pub.dev/packages/stream_chat/changelog). - -## 8.0.0 - -- Updated `stream_chat_flutter` dependency to [`8.0.0`](https://pub.dev/packages/stream_chat/changelog). - -## 7.3.0 - -šŸ”„ Changed - -- Changed minimum Flutter version to 3.19 for the SDK. -- Updated `stream_chat_flutter` dependency to [`7.3.0`](https://pub.dev/packages/stream_chat/changelog). - -## 7.2.2 - -- Updated `stream_chat_flutter` dependency to [`7.2.2`](https://pub.dev/packages/stream_chat/changelog). - -## 7.2.1 - -- Updated `stream_chat_flutter` dependency to [`7.2.1`](https://pub.dev/packages/stream_chat/changelog). - -## 7.2.0-hotfix.1 - -- Updated `stream_chat_flutter` dependency to [`7.2.0-hotfix.1`](https://pub.dev/packages/stream_chat/changelog). - -# 7.2.0 - -* Updated `stream_chat_flutter` dependency to [`7.2.0`](https://pub.dev/packages/stream_chat_flutter/changelog). - -## 7.1.0 - -* Updated `stream_chat_flutter` dependency to [`7.1.0`](https://pub.dev/packages/stream_chat_flutter/changelog). - -## 7.0.2 - -* Updated `stream_chat_flutter` dependency to [`7.0.2`](https://pub.dev/packages/stream_chat_flutter/changelog). - -## 7.0.1 - -* Updated `stream_chat_flutter` dependency to [`7.0.1`](https://pub.dev/packages/stream_chat_flutter/changelog). - -## 7.0.0 - -* Updated minimum supported `SDK` version to Flutter 3.13/Dart 3.1 -* Updated `stream_chat_flutter` dependency to [`7.0.0`](https://pub.dev/packages/stream_chat_flutter/changelog). - -## 5.12.0 - -* Updated `stream_chat_flutter` dependency to [`6.12.0`](https://pub.dev/packages/stream_chat_flutter/changelog). - -## 5.11.0 - -* Updated `stream_chat_flutter` dependency to [`6.11.0`](https://pub.dev/packages/stream_chat_flutter/changelog). - -## 5.10.0 - -* Updated `stream_chat_flutter` dependency to [`6.10.0`](https://pub.dev/packages/stream_chat_flutter/changelog). - -## 5.9.0 - -* Updated minimum supported `SDK` version to Flutter 3.10/Dart 3.0 -* Updated `stream_chat_flutter` dependency to [`6.9.0`](https://pub.dev/packages/stream_chat_flutter/changelog). - -## 5.8.0 - -* Updated `stream_chat_flutter` dependency to [`6.8.0`](https://pub.dev/packages/stream_chat_flutter/changelog). - -## 5.7.0 - -* Updated `stream_chat_flutter` dependency to [`6.7.0`](https://pub.dev/packages/stream_chat_flutter/changelog). - -## 5.6.0 - -* Updated minimum supported `SDK` version to Flutter 3.7/Dart 2.19 -* Updated `stream_chat_flutter` dependency to [`6.6.0`](https://pub.dev/packages/stream_chat_flutter/changelog). - -## 5.5.0 - -* Updated `stream_chat_flutter` dependency to [`6.5.0`](https://pub.dev/packages/stream_chat_flutter/changelog). - -## 5.4.0 - -* Updated `stream_chat_flutter` dependency to [`6.4.0`](https://pub.dev/packages/stream_chat_flutter/changelog). - -## 5.3.0 - -* Updated `stream_chat_flutter` dependency to [`6.3.0`](https://pub.dev/packages/stream_chat_flutter/changelog). - -## 5.2.0 - -* Updated `stream_chat_flutter` dependency to [`6.2.0`](https://pub.dev/packages/stream_chat_flutter/changelog). - -## 5.1.0 - -* Updated `dart` sdk environment range to support `3.0.0`. -* Updated `stream_chat_flutter` dependency to [`6.1.0`](https://pub.dev/packages/stream_chat_flutter/changelog). - -## 5.0.0 - -* Updated `stream_chat_flutter` dependency to [`6.0.0`](https://pub.dev/packages/stream_chat_flutter/changelog). - -## 4.1.0 - -āœ… Added - -* Added support - for [Catalan](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ca.dart) - locale. -* Added translations for new `noPhotoOrVideoLabel` label. -* Changed text in New messages separator. Now is doesn't count the new messages and only shows "New messages". All the - translations were updated. - -šŸ”„ Changed - -* Some of the `Spanish` translations have been updated/changed for better understanding. -* Some of the `Catalan` translations have been updated/changed for better understanding. - -## 4.0.0 - -šŸ”„ Changed - -* Removed `emojiMatchingQueryText` string. - -## 4.0.0-beta.2 - -* Included the changes from version [3.3.0](#330). - -## 4.0.0-beta.1 - -āœ… Added - -* `couldNotReadBytesFromFileError` with translations -* `downloadLabel` with translations -* `toggleMuteUnmuteAction` with translations -* `toggleMuteUnmuteGroupQuestion` with translations -* `toggleMuteUnmuteGroupText` with translations -* `toggleMuteUnmuteUserQuestion` with translations -* `toggleMuteUnmuteUserText` with translations - -## 3.3.0 - -* Added support - for [Norwegian](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_no.dart) - locale. - -## 3.2.0 - -āœ… Added - -* Added support for `unreadMessagesSeparatorText` translation. - -## 3.1.0 - -* Added support - for [German](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_de.dart) - locale. - -## 3.0.0 - -* Added translations for viewLibrary. - -## 3.0.0-beta.1 - -* Updated `stream_chat_flutter` dependency to [`4.0.0-beta.1`](https://pub.dev/packages/stream_chat_flutter/changelog). - -## 2.1.0 - -āœ… Added - -* Added support - for [Portuguese](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_pt.dart) - locale. - -šŸ”„ Changed - -* Some of the `Japanese` translations have been updated/changed for better understanding. - -## 2.0.0 - -* Updated `stream_chat_flutter` dependency to [`3.0.0`](https://pub.dev/packages/stream_chat_flutter/changelog). - -šŸž Fixed - -* Fixed typos in `Italian` translations. - -## 1.1.0 - -āœ… Added - -* Added support - for [Spanish](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart) - locale. -* Added support - for [Korean](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ko.dart) - locale. -* Added support - for [Japanese](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ja.dart) - locale. -* Added translations for cooldown mode. -* Added translations for attachmentLimitExceed. - -šŸ”„ Changed - -* Some of the `Hindi` translations have been updated/changed for better understanding. - - 'ą¤°ą¤æą¤Ŗą„ą¤²ą¤¾ą¤ˆ' -> 'जवाब ą¤¦ą„‡ą¤‚' - - 'ą¤¤ą¤øą„ą¤µą„€ą¤°ą„‡ą¤‚' -> 'ą„žą„‹ą¤Ÿą„‹ą¤œ' - - 'बिता ą¤¹ą„ą¤† कल' -> 'कल' - - 'ą¤šą„ˆą¤Øą¤² ą¤®ą„Œą¤Ø ą¤¹ą„ˆ' -> 'ą¤šą„ˆą¤Øą¤² ą¤®ą„ą¤Æą„‚ą¤Ÿ ą¤¹ą„ˆ' - -## 1.0.2 - -* Updated `stream_chat_flutter` dependency - -## 1.0.1 - -* Minor Fixes - -## 1.0.0 - -* Initial Release with support for 4 locales - - [English](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_en.dart) - - [Hindi](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_hi.dart) - - [Italian](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_it.dart) - - [French](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_fr.dart) diff --git a/packages/stream_chat_localizations/LICENSE b/packages/stream_chat_localizations/LICENSE deleted file mode 100644 index 49088d477f..0000000000 --- a/packages/stream_chat_localizations/LICENSE +++ /dev/null @@ -1,219 +0,0 @@ -SOURCE CODE LICENSE AGREEMENT - -IMPORTANT - READ THIS CAREFULLY BEFORE DOWNLOADING, INSTALLING, USING OR -ELECTRONICALLY ACCESSING THIS PROPRIETARY PRODUCT. - -THIS IS A LEGAL AGREEMENT BETWEEN STREAM.IO, INC. (ā€œSTREAM.IOā€) AND THE -BUSINESS ENTITY OR PERSON FOR WHOM YOU (ā€œYOUā€) ARE ACTING (ā€œCUSTOMERā€) AS THE -LICENSEE OF THE PROPRIETARY SOFTWARE INTO WHICH THIS AGREEMENT HAS BEEN -INCLUDED (THE ā€œAGREEMENTā€). YOU AGREE THAT YOU ARE THE CUSTOMER, OR YOU ARE AN -EMPLOYEE OR AGENT OF CUSTOMER AND ARE ENTERING INTO THIS AGREEMENT FOR LICENSE -OF THE SOFTWARE BY CUSTOMER FOR CUSTOMER’S BUSINESS PURPOSES AS DESCRIBED IN -AND IN ACCORDANCE WITH THIS AGREEMENT. YOU HEREBY AGREE THAT YOU ENTER INTO -THIS AGREEMENT ON BEHALF OF CUSTOMER AND THAT YOU HAVE THE AUTHORITY TO BIND -CUSTOMER TO THIS AGREEMENT. - -STREAM.IO IS WILLING TO LICENSE THE SOFTWARE TO CUSTOMER ONLY ON THE FOLLOWING -CONDITIONS: (1) YOU ARE A CURRENT CUSTOMER OF STREAM.IO; (2) YOU ARE NOT A -COMPETITOR OF STREAM.IO; AND (3) THAT YOU ACCEPT ALL THE TERMS IN THIS -AGREEMENT. BY DOWNLOADING, INSTALLING, CONFIGURING, ACCESSING OR OTHERWISE -USING THE SOFTWARE, INCLUDING ANY UPDATES, UPGRADES, OR NEWER VERSIONS, YOU -REPRESENT, WARRANT AND ACKNOWLEDGE THAT (A) CUSTOMER IS A CURRENT CUSTOMER OF -STREAM.IO; (B) CUSTOMER IS NOT A COMPETITOR OF STREAM.IO; AND THAT (C) YOU HAVE -READ THIS AGREEMENT, UNDERSTAND THIS AGREEMENT, AND THAT CUSTOMER AGREES TO BE -BOUND BY ALL THE TERMS OF THIS AGREEMENT. - -IF YOU DO NOT AGREE TO ALL THE TERMS AND CONDITIONS OF THIS AGREEMENT, -STREAM.IO IS UNWILLING TO LICENSE THE SOFTWARE TO CUSTOMER, AND THEREFORE, DO -NOT COMPLETE THE DOWNLOAD PROCESS, ACCESS OR OTHERWISE USE THE SOFTWARE, AND -CUSTOMER SHOULD IMMEDIATELY RETURN THE SOFTWARE AND CEASE ANY USE OF THE -SOFTWARE. - -1. SOFTWARE. The Stream.io software accompanying this Agreement, may include -Source Code, Executable Object Code, associated media, printed materials and -documentation (collectively, the ā€œSoftwareā€). The Software also includes any -updates or upgrades to or new versions of the original Software, if and when -made available to you by Stream.io. ā€œSource Codeā€ means computer programming -code in human readable form that is not suitable for machine execution without -the intervening steps of interpretation or compilation. ā€œExecutable Object -Code" means the computer programming code in any other form than Source Code -that is not readily perceivable by humans and suitable for machine execution -without the intervening steps of interpretation or compilation. ā€œSiteā€ means a -Customer location controlled by Customer. ā€œAuthorized Userā€ means any employee -or contractor of Customer working at the Site, who has signed a written -confidentiality agreement with Customer or is otherwise bound in writing by -confidentiality and use obligations at least as restrictive as those imposed -under this Agreement. - -2. LICENSE GRANT. Subject to the terms and conditions of this Agreement, in -consideration for the representations, warranties, and covenants made by -Customer in this Agreement, Stream.io grants to Customer, during the term of -this Agreement, a personal, non-exclusive, non-transferable, non-sublicensable -license to: - -a. install and use Software Source Code on password protected computers at a Site, -restricted to Authorized Users; - -b. create derivative works, improvements (whether or not patentable), extensions -and other modifications to the Software Source Code (ā€œModificationsā€) to build -unique scalable newsfeeds, activity streams, and in-app messaging via Stream’s -application program interface (ā€œAPIā€); - -c. compile the Software Source Code to create Executable Object Code versions of -the Software Source Code and Modifications to build such newsfeeds, activity -streams, and in-app messaging via the API; - -d. install, execute and use such Executable Object Code versions solely for -Customer’s internal business use (including development of websites through -which data generated by Stream services will be streamed (ā€œAppsā€)); - -e. use and distribute such Executable Object Code as part of Customer’s Apps; and - -f. make electronic copies of the Software and Modifications as required for backup -or archival purposes. - -3. RESTRICTIONS. Customer is responsible for all activities that occur in -connection with the Software. Customer will not, and will not attempt to: (a) -sublicense or transfer the Software or any Source Code related to the Software -or any of Customer’s rights under this Agreement, except as otherwise provided -in this Agreement, (b) use the Software Source Code for the benefit of a third -party or to operate a service; (c) allow any third party to access or use the -Software Source Code; (d) sublicense or distribute the Software Source Code or -any Modifications in Source Code or other derivative works based on any part of -the Software Source Code; (e) use the Software in any manner that competes with -Stream.io or its business; or (e) otherwise use the Software in any manner that -exceeds the scope of use permitted in this Agreement. Customer shall use the -Software in compliance with any accompanying documentation any laws applicable -to Customer. - -4. OPEN SOURCE. Customer and its Authorized Users shall not use any software or -software components that are open source in conjunction with the Software -Source Code or any Modifications in Source Code or in any way that could -subject the Software to any open source licenses. - -5. CONTRACTORS. Under the rights granted to Customer under this Agreement, -Customer may permit its employees, contractors, and agencies of Customer to -become Authorized Users to exercise the rights to the Software granted to -Customer in accordance with this Agreement solely on behalf of Customer to -provide services to Customer; provided that Customer shall be liable for the -acts and omissions of all Authorized Users to the extent any of such acts or -omissions, if performed by Customer, would constitute a breach of, or otherwise -give rise to liability to Customer under, this Agreement. Customer shall not -and shall not permit any Authorized User to use the Software except as -expressly permitted in this Agreement. - -6. COMPETITIVE PRODUCT DEVELOPMENT. Customer shall not use the Software in any way -to engage in the development of products or services which could be reasonably -construed to provide a complete or partial functional or commercial alternative -to Stream.io’s products or services (a ā€œCompetitive Productā€). Customer shall -ensure that there is no direct or indirect use of, or sharing of, Software -source code, or other information based upon or derived from the Software to -develop such products or services. Without derogating from the generality of -the foregoing, development of Competitive Products shall include having direct -or indirect access to, supervising, consulting or assisting in the development -of, or producing any specifications, documentation, object code or source code -for, all or part of a Competitive Product. - -7. LIMITATION ON MODIFICATIONS. Notwithstanding any provision in this Agreement, -Modifications may only be created and used by Customer as permitted by this -Agreement and Modification Source Code may not be distributed to third parties. -Customer will not assert against Stream.io, its affiliates, or their customers, -direct or indirect, agents and contractors, in any way, any patent rights that -Customer may obtain relating to any Modifications for Stream.io, its -affiliates’, or their customers’, direct or indirect, agents’ and contractors’ -manufacture, use, import, offer for sale or sale of any Stream.io products or -services. - -8. DELIVERY AND ACCEPTANCE. The Software will be delivered electronically pursuant -to Stream.io standard download procedures. The Software is deemed accepted upon -delivery. - -9. IMPLEMENTATION AND SUPPORT. Stream.io has no obligation under this Agreement to -provide any support or consultation concerning the Software. - -10. TERM AND TERMINATION. The term of this Agreement begins when the Software is -downloaded or accessed and shall continue until terminated. Either party may -terminate this Agreement upon written notice. This Agreement shall -automatically terminate if Customer is or becomes a competitor of Stream.io or -makes or sells any Competitive Products. Upon termination of this Agreement for -any reason, (a) all rights granted to Customer in this Agreement immediately -cease to exist, (b) Customer must promptly discontinue all use of the Software -and return to Stream.io or destroy all copies of the Software in Customer’s -possession or control. Any continued use of the Software by Customer or attempt -by Customer to exercise any rights under this Agreement after this Agreement -has terminated shall be considered copyright infringement and subject Customer -to applicable remedies for copyright infringement. Sections 2, 5, 6, 8 and 9 -shall survive expiration or termination of this Agreement for any reason. - -11. OWNERSHIP. As between the parties, the Software and all worldwide intellectual -property rights and proprietary rights relating thereto or embodied therein, -are the exclusive property of Stream.io and its suppliers. Stream.io and its -suppliers reserve all rights in and to the Software not expressly granted to -Customer in this Agreement, and no other licenses or rights are granted by -implication, estoppel or otherwise. - -12. WARRANTY DISCLAIMER. USE OF THIS SOFTWARE IS ENTIRELY AT YOURS AND CUSTOMER’S -OWN RISK. THE SOFTWARE IS PROVIDED ā€œAS ISā€ WITHOUT ANY WARRANTY OF ANY KIND -WHATSOEVER. STREAM.IO DOES NOT MAKE, AND HEREBY DISCLAIMS, ANY WARRANTY OF ANY -KIND, WHETHER EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING WITHOUT -LIMITATION, THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -PURPOSE, TITLE, NON-INFRINGEMENT OF THIRD-PARTY RIGHTS, RESULTS, EFFORTS, -QUALITY OR QUIET ENJOYMENT. STREAM.IO DOES NOT WARRANT THAT THE SOFTWARE IS -ERROR-FREE, WILL FUNCTION WITHOUT INTERRUPTION, WILL MEET ANY SPECIFIC NEED -THAT CUSTOMER HAS, THAT ALL DEFECTS WILL BE CORRECTED OR THAT IT IS -SUFFICIENTLY DOCUMENTED TO BE USABLE BY CUSTOMER. TO THE EXTENT THAT STREAM.IO -MAY NOT DISCLAIM ANY WARRANTY AS A MATTER OF APPLICABLE LAW, THE SCOPE AND -DURATION OF SUCH WARRANTY WILL BE THE MINIMUM PERMITTED UNDER SUCH LAW. -CUSTOMER ACKNOWLEDGES THAT IT HAS RELIED ON NO WARRANTIES OTHER THAN THE -EXPRESS WARRANTIES IN THIS AGREEMENT. - -13. LIMITATION OF LIABILITY. TO THE FULLEST EXTENT PERMISSIBLE BY LAW, STREAM.IO’S -TOTAL LIABILITY FOR ALL DAMAGES ARISING OUT OF OR RELATED TO THE SOFTWARE OR -THIS AGREEMENT, WHETHER IN CONTRACT, TORT (INCLUDING NEGLIGENCE) OR OTHERWISE, -SHALL NOT EXCEED $100. IN NO EVENT WILL STREAM.IO BE LIABLE FOR ANY INDIRECT, -CONSEQUENTIAL, EXEMPLARY, PUNITIVE, SPECIAL OR INCIDENTAL DAMAGES OF ANY KIND -WHATSOEVER, INCLUDING ANY LOST DATA AND LOST PROFITS, ARISING FROM OR RELATING -TO THE SOFTWARE EVEN IF STREAM.IO HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. CUSTOMER ACKNOWLEDGES THAT THIS PROVISION REFLECTS THE AGREED UPON -ALLOCATION OF RISK FOR THIS AGREEMENT AND THAT STREAM.IO WOULD NOT ENTER INTO -THIS AGREEMENT WITHOUT THESE LIMITATIONS ON ITS LIABILITY. - -14. General. Customer may not assign or transfer this Agreement, by operation of -law or otherwise, or any of its rights under this Agreement (including the -license rights granted to Customer) to any third party without Stream.io’s -prior written consent, which consent will not be unreasonably withheld or -delayed. Stream.io may assign this Agreement, without consent, including, but -limited to, affiliate or any successor to all or substantially all its business -or assets to which this Agreement relates, whether by merger, sale of assets, -sale of stock, reorganization or otherwise. Any attempted assignment or -transfer in violation of the foregoing will be null and void. Stream.io shall -not be liable hereunder by reason of any failure or delay in the performance of -its obligations hereunder for any cause which is beyond the reasonable control. -All notices, consents, and approvals under this Agreement must be delivered in -writing by courier, by electronic mail, or by certified or registered mail, -(postage prepaid and return receipt requested) to the other party at the -address set forth in the customer agreement between Stream.io and Customer and -will be effective upon receipt or when delivery is refused. This Agreement will -be governed by and interpreted in accordance with the laws of the State of -Colorado, without reference to its choice of laws rules. The United Nations -Convention on Contracts for the International Sale of Goods does not apply to -this Agreement. Any action or proceeding arising from or relating to this -Agreement shall be brought in a federal or state court in Denver, Colorado, and -each party irrevocably submits to the jurisdiction and venue of any such court -in any such action or proceeding. All waivers must be in writing. Any waiver or -failure to enforce any provision of this Agreement on one occasion will not be -deemed a waiver of any other provision or of such provision on any other -occasion. If any provision of this Agreement is unenforceable, such provision -will be changed and interpreted to accomplish the objectives of such provision -to the greatest extent possible under applicable law and the remaining -provisions will continue in full force and effect. Customer shall not violate -any applicable law, rule or regulation, including those regarding the export of -technical data. The headings of Sections of this Agreement are for convenience -and are not to be used in interpreting this Agreement. As used in this -Agreement, the word ā€œincludingā€ means ā€œincluding but not limited to.ā€ This -Agreement (including all exhibits and attachments) constitutes the entire -agreement between the parties regarding the subject hereof and supersedes all -prior or contemporaneous agreements, understandings and communication, whether -written or oral. This Agreement may be amended only by a written document -signed by both parties. The terms of any purchase order or similar document -submitted by Customer to Stream.io will have no effect. diff --git a/packages/stream_chat_localizations/README.md b/packages/stream_chat_localizations/README.md deleted file mode 100644 index 989c00b2e4..0000000000 --- a/packages/stream_chat_localizations/README.md +++ /dev/null @@ -1,142 +0,0 @@ -# Official Localizations for [Stream Chat Flutter](https://getstream.io/chat/sdk/flutter/) library. - -> The Official localizations for Stream Chat Flutter, a service for -> building chat applications. - -[![Pub](https://img.shields.io/pub/v/stream_chat_localizations.svg)](https://pub.dartlang.org/packages/stream_chat_localizations) -![](https://img.shields.io/badge/platform-flutter%20%7C%20flutter%20web-ff69b4.svg?style=flat-square) -![CI](https://github.com/GetStream/stream-chat-flutter/workflows/stream_flutter_workflow/badge.svg?branch=master) - - -**Quick Links** - -- [Register](https://getstream.io/chat/trial/) to get an API key for Stream Chat -- [Flutter Chat Tutorial](https://getstream.io/chat/flutter/tutorial/) -- [Chat UI Kit](https://getstream.io/chat/ui-kit/) -- [UI Docs](https://getstream.io/chat/docs/sdk/flutter/stream_chat_flutter/introduction/) - -### Changelog - -Check out the [changelog on pub.dev](https://pub.dev/packages/stream_chat_localizations/changelog) to see the latest changes in the package. - -![localization_support](https://user-images.githubusercontent.com/13705472/127504329-a9690184-ce0f-4442-adb4-a33b5e3a3bf1.png) - -## What is Localization? - -If you deploy your app to users who speak another language, you'll need to internationalize (localize) it. That means you need to write the app in a way that makes it possible to localize values like text and layouts for each language or locale that the app supports. For more information, see the [Flutter documentation](https://flutter.dev/docs/development/accessibility-and-localization/**internationalization**). - -What this package allows you to do is to provide localized strings for the Stream chat widgets. For example, depending on the application locale, the Stream Chat widgets will display the appropriate language. The locale will be set automatically, based on system preferences, or you could set it programmatically in your app. The package supports several different languages, with more to be added. The package allows you to override any supported language or add a new language that isn't supported. - -## Supported languages - -At the moment we support the following languages: -- [English](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_en.dart) -- [Hindi](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_hi.dart) -- [Italian](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_it.dart) -- [French](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_fr.dart) -- [Spanish](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart) -- [Catalan](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ca.dart) -- [Japanese](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ja.dart) -- [Korean](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ko.dart) -- [Portuguese](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_pt.dart) -- [German](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_de.dart) -- [Norwegian](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_no.dart) - -More languages will be added in the future. Feel free to [contribute](https://github.com/GetStream/stream-chat-flutter/blob/master/CONTRIBUTING.md) to add more languages. - -## Add dependency - -Add this to your package's pubspec.yaml file, use the latest version [![Pub](https://img.shields.io/pub/v/stream_chat_localizations.svg)](https://pub.dartlang.org/packages/stream_chat_localizations) -```yaml -dependencies: - stream_chat_localizations: ^latest_version -``` - -Then run `flutter packages get` - -### Usage - -```dart -import 'package:flutter/material.dart'; -import 'package:stream_chat_localizations/stream_chat_localizations.dart'; - -void main() { - WidgetsFlutterBinding.ensureInitialized(); - runApp(MyApp()); -} - -class MyApp extends StatelessWidget { - @override - Widget build(BuildContext context) { - return MaterialApp( - // Add all the supported locales - supportedLocales: const [ - Locale('en'), - Locale('hi'), - Locale('fr'), - Locale('it'), - Locale('es'), - Locale('ca'), - Locale('ja'), - Locale('ko'), - Locale('pt'), - Locale('de'), - Locale('no'), - ], - // Add GlobalStreamChatLocalizations.delegates - localizationsDelegates: GlobalStreamChatLocalizations.delegates, - builder: (context, widget) => StreamChat( - client: client, - child: widget, - ), - home: StreamChannel( - channel: channel, - child: const ChannelPage(), - ), - ); - } -} -``` - -### Adding a new language - -To add a new language, create a new class extending `GlobalStreamChatLocalizations` and create a delegate for it, adding it to the `delegates` array. - -Check out [this example](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/example/lib/add_new_lang.dart) to see how to add a new language. - -### Override existing languages - -To override an existing language, create a new class extending that particular language class and create a delegate for it, adding it to the `delegates` array. - -Check out [this example](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/example/lib/override_lang.dart) to see how to override an existing language. - -### āš ļø Note on **iOS** - -For translation to work on **iOS** you need to add supported locales to -`ios/Runner/Info.plist` as described [here](https://flutter.dev/docs/development/accessibility-and-localization/internationalization#localizing-for-ios-updating-the-ios-app-bundle). - -Example: - -```xml -CFBundleLocalizations - - en - hi - fr - it - es - ca - ja - ko - pt - de - no - -``` - -## Contributing - -We welcome code changes that improve this library or fix a problem. Please make sure to follow all best practices and add tests if applicable before submitting a Pull Request on Github. -We are pleased to merge your code into the official repository. -Make sure to sign our [Contributor License Agreement (CLA)](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) first. -See our license file for more details. diff --git a/packages/stream_chat_localizations/example/.gitignore b/packages/stream_chat_localizations/example/.gitignore deleted file mode 100644 index 9d532b18a0..0000000000 --- a/packages/stream_chat_localizations/example/.gitignore +++ /dev/null @@ -1,41 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -**/doc/api/ -**/ios/Flutter/.last_build_id -.dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies -.packages -.pub-cache/ -.pub/ -/build/ - -# Web related -lib/generated_plugin_registrant.dart - -# Symbolication related -app.*.symbols - -# Obfuscation related -app.*.map.json diff --git a/packages/stream_chat_localizations/example/.metadata b/packages/stream_chat_localizations/example/.metadata deleted file mode 100644 index 182cccafd9..0000000000 --- a/packages/stream_chat_localizations/example/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: 78910062997c3a836feee883712c241a5fd22983 - channel: stable - -project_type: app diff --git a/packages/stream_chat_localizations/example/README.md b/packages/stream_chat_localizations/example/README.md deleted file mode 100644 index 003f56eee1..0000000000 --- a/packages/stream_chat_localizations/example/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# Stream Chat Persistence Example -Please see `lib/` for example code. - -## Setting a language -The application language can be changed through system preferences or programmatically. - -### System Preferences -The application locale can be changed by changing the language for your device or emulator within the device's system preferences. - -[iOS change language](https://support.apple.com/en-us/HT204031) - -[Android change language](https://support.google.com/websearch/answer/3333234?co=GENIE.Platform%3DAndroid&hl=en) - -Note that the language needs to be supported in your application to work. - -### Programmatically -You can also set the locale programmatically in your Flutter application without changing the device's language. - -```dart -return MaterialApp( - ... - locale: const Locale('fr'), - ... -); -``` - -There are many ways that this can be set for additional control. For information and examples, see this [Stack Overflow post](https://stackoverflow.com/questions/49441212/flutter-multi-lingual-application-how-to-override-the-locale). \ No newline at end of file diff --git a/packages/stream_chat_localizations/example/android/.gitignore b/packages/stream_chat_localizations/example/android/.gitignore deleted file mode 100644 index 0a741cb43d..0000000000 --- a/packages/stream_chat_localizations/example/android/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -gradle-wrapper.jar -/.gradle -/captures/ -/gradlew -/gradlew.bat -/local.properties -GeneratedPluginRegistrant.java - -# Remember to never publicly share your keystore. -# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app -key.properties diff --git a/packages/stream_chat_localizations/example/android/app/build.gradle b/packages/stream_chat_localizations/example/android/app/build.gradle deleted file mode 100644 index 373c4a16d4..0000000000 --- a/packages/stream_chat_localizations/example/android/app/build.gradle +++ /dev/null @@ -1,64 +0,0 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -android { - compileSdkVersion 33 - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - lintOptions { - disable 'InvalidPackage' - checkReleaseBuilds false - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.example" - minSdkVersion 21 - targetSdkVersion 33 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/packages/stream_chat_localizations/example/android/app/src/debug/AndroidManifest.xml b/packages/stream_chat_localizations/example/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index c208884f30..0000000000 --- a/packages/stream_chat_localizations/example/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/packages/stream_chat_localizations/example/android/app/src/main/AndroidManifest.xml b/packages/stream_chat_localizations/example/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index 764fe0041c..0000000000 --- a/packages/stream_chat_localizations/example/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/packages/stream_chat_localizations/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/packages/stream_chat_localizations/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt deleted file mode 100644 index e793a000d6..0000000000 --- a/packages/stream_chat_localizations/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.example.example - -import io.flutter.embedding.android.FlutterActivity - -class MainActivity: FlutterActivity() { -} diff --git a/packages/stream_chat_localizations/example/android/app/src/main/res/drawable/launch_background.xml b/packages/stream_chat_localizations/example/android/app/src/main/res/drawable/launch_background.xml deleted file mode 100644 index 304732f884..0000000000 --- a/packages/stream_chat_localizations/example/android/app/src/main/res/drawable/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/packages/stream_chat_localizations/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/stream_chat_localizations/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index db77bb4b7b..0000000000 Binary files a/packages/stream_chat_localizations/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/stream_chat_localizations/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 17987b79bb..0000000000 Binary files a/packages/stream_chat_localizations/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/stream_chat_localizations/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 09d4391482..0000000000 Binary files a/packages/stream_chat_localizations/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/stream_chat_localizations/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index d5f1c8d34e..0000000000 Binary files a/packages/stream_chat_localizations/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/stream_chat_localizations/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 4d6372eebd..0000000000 Binary files a/packages/stream_chat_localizations/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/android/app/src/main/res/values/styles.xml b/packages/stream_chat_localizations/example/android/app/src/main/res/values/styles.xml deleted file mode 100644 index 1f83a33fd4..0000000000 --- a/packages/stream_chat_localizations/example/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/packages/stream_chat_localizations/example/android/app/src/profile/AndroidManifest.xml b/packages/stream_chat_localizations/example/android/app/src/profile/AndroidManifest.xml deleted file mode 100644 index c208884f30..0000000000 --- a/packages/stream_chat_localizations/example/android/app/src/profile/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/packages/stream_chat_localizations/example/android/build.gradle b/packages/stream_chat_localizations/example/android/build.gradle deleted file mode 100644 index 72082e1dfe..0000000000 --- a/packages/stream_chat_localizations/example/android/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -buildscript { - ext.kotlin_version = '1.9.22' - repositories { - google() - jcenter() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.2.2' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -allprojects { - repositories { - google() - jcenter() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -tasks.register("clean", Delete) { - delete rootProject.buildDir -} diff --git a/packages/stream_chat_localizations/example/android/gradle.properties b/packages/stream_chat_localizations/example/android/gradle.properties deleted file mode 100644 index a6738207fd..0000000000 --- a/packages/stream_chat_localizations/example/android/gradle.properties +++ /dev/null @@ -1,4 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M -android.useAndroidX=true -android.enableJetifier=true -android.enableR8=true diff --git a/packages/stream_chat_localizations/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/stream_chat_localizations/example/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 02e5f58171..0000000000 --- a/packages/stream_chat_localizations/example/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Fri Jun 23 08:50:38 CEST 2017 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip diff --git a/packages/stream_chat_localizations/example/android/settings.gradle b/packages/stream_chat_localizations/example/android/settings.gradle deleted file mode 100644 index 44e62bcf06..0000000000 --- a/packages/stream_chat_localizations/example/android/settings.gradle +++ /dev/null @@ -1,11 +0,0 @@ -include ':app' - -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() - -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } - -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/packages/stream_chat_localizations/example/ios/.gitignore b/packages/stream_chat_localizations/example/ios/.gitignore deleted file mode 100644 index 151026b91b..0000000000 --- a/packages/stream_chat_localizations/example/ios/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -*.mode1v3 -*.mode2v3 -*.moved-aside -*.pbxuser -*.perspectivev3 -**/*sync/ -.sconsign.dblite -.tags* -**/.vagrant/ -**/DerivedData/ -Icon? -**/Pods/ -**/.symlinks/ -profile -xcuserdata -**/.generated/ -Flutter/App.framework -Flutter/Flutter.framework -Flutter/Flutter.podspec -Flutter/Generated.xcconfig -Flutter/ephemeral/ -Flutter/app.flx -Flutter/app.zip -Flutter/flutter_assets/ -Flutter/flutter_export_environment.sh -ServiceDefinitions.json -Runner/GeneratedPluginRegistrant.* - -# Exceptions to above rules. -!default.mode1v3 -!default.mode2v3 -!default.pbxuser -!default.perspectivev3 diff --git a/packages/stream_chat_localizations/example/ios/Flutter/AppFrameworkInfo.plist b/packages/stream_chat_localizations/example/ios/Flutter/AppFrameworkInfo.plist deleted file mode 100644 index 9625e105df..0000000000 --- a/packages/stream_chat_localizations/example/ios/Flutter/AppFrameworkInfo.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - MinimumOSVersion - 11.0 - - diff --git a/packages/stream_chat_localizations/example/ios/Flutter/Debug.xcconfig b/packages/stream_chat_localizations/example/ios/Flutter/Debug.xcconfig deleted file mode 100644 index ec97fc6f30..0000000000 --- a/packages/stream_chat_localizations/example/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "Generated.xcconfig" diff --git a/packages/stream_chat_localizations/example/ios/Flutter/Release.xcconfig b/packages/stream_chat_localizations/example/ios/Flutter/Release.xcconfig deleted file mode 100644 index c4855bfe20..0000000000 --- a/packages/stream_chat_localizations/example/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "Generated.xcconfig" diff --git a/packages/stream_chat_localizations/example/ios/Podfile b/packages/stream_chat_localizations/example/ios/Podfile deleted file mode 100644 index d97f17e223..0000000000 --- a/packages/stream_chat_localizations/example/ios/Podfile +++ /dev/null @@ -1,44 +0,0 @@ -# Uncomment this line to define a global platform for your project -# platform :ios, '12.0' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_ios_podfile_setup - -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) - target 'RunnerTests' do - inherit! :search_paths - end -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_ios_build_settings(target) - end -end diff --git a/packages/stream_chat_localizations/example/ios/Runner.xcodeproj/project.pbxproj b/packages/stream_chat_localizations/example/ios/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index b627d17952..0000000000 --- a/packages/stream_chat_localizations/example/ios/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,542 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXBuildFile section */ - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 710C8D61445A55538E32FA76 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 90B326A922854E6FACB68418 /* Pods_Runner.framework */; }; - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 2C064A83D93670719ABCFB4E /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 406F253F79064E73E6570F91 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - 425E1032537699D21AA7599E /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 90B326A922854E6FACB68418 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 97C146EB1CF9000F007C117D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 710C8D61445A55538E32FA76 /* Pods_Runner.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 9740EEB11CF90186004384FC /* Flutter */ = { - isa = PBXGroup; - children = ( - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 9740EEB31CF90195004384FC /* Generated.xcconfig */, - ); - name = Flutter; - sourceTree = ""; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - 9740EEB11CF90186004384FC /* Flutter */, - 97C146F01CF9000F007C117D /* Runner */, - 97C146EF1CF9000F007C117D /* Products */, - D27696257385EFA4B6A3AECC /* Pods */, - A92D4F040E5D68A2DFDFF35C /* Frameworks */, - ); - sourceTree = ""; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - ); - name = Products; - sourceTree = ""; - }; - 97C146F01CF9000F007C117D /* Runner */ = { - isa = PBXGroup; - children = ( - 97C146FA1CF9000F007C117D /* Main.storyboard */, - 97C146FD1CF9000F007C117D /* Assets.xcassets */, - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 97C147021CF9000F007C117D /* Info.plist */, - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, - ); - path = Runner; - sourceTree = ""; - }; - A92D4F040E5D68A2DFDFF35C /* Frameworks */ = { - isa = PBXGroup; - children = ( - 90B326A922854E6FACB68418 /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - D27696257385EFA4B6A3AECC /* Pods */ = { - isa = PBXGroup; - children = ( - 2C064A83D93670719ABCFB4E /* Pods-Runner.debug.xcconfig */, - 406F253F79064E73E6570F91 /* Pods-Runner.release.xcconfig */, - 425E1032537699D21AA7599E /* Pods-Runner.profile.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 97C146ED1CF9000F007C117D /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - BC7B06B6092566B153F8E77E /* [CP] Check Pods Manifest.lock */, - 9740EEB61CF901F6004384FC /* Run Script */, - 97C146EA1CF9000F007C117D /* Sources */, - 97C146EB1CF9000F007C117D /* Frameworks */, - 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 91A9FD3C359F260AE72CC421 /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Runner; - productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* Runner.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 97C146E61CF9000F007C117D /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1430; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 97C146ED1CF9000F007C117D = { - CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 1100; - }; - }; - }; - buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 97C146E51CF9000F007C117D; - productRefGroup = 97C146EF1CF9000F007C117D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 97C146ED1CF9000F007C117D /* Runner */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 97C146EC1CF9000F007C117D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 91A9FD3C359F260AE72CC421 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; - BC7B06B6092566B153F8E77E /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 97C146EA1CF9000F007C117D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 97C146FA1CF9000F007C117D /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C146FB1CF9000F007C117D /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 249021D3217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Profile; - }; - 249021D4217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.example.example; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Profile; - }; - 97C147031CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 97C147041CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 97C147061CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.example.example; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - 97C147071CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.example.example; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147031CF9000F007C117D /* Debug */, - 97C147041CF9000F007C117D /* Release */, - 249021D3217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147061CF9000F007C117D /* Debug */, - 97C147071CF9000F007C117D /* Release */, - 249021D4217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 97C146E61CF9000F007C117D /* Project object */; -} diff --git a/packages/stream_chat_localizations/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/stream_chat_localizations/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a625..0000000000 --- a/packages/stream_chat_localizations/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/packages/stream_chat_localizations/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/stream_chat_localizations/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/packages/stream_chat_localizations/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/packages/stream_chat_localizations/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/stream_chat_localizations/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5ea..0000000000 --- a/packages/stream_chat_localizations/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/packages/stream_chat_localizations/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/stream_chat_localizations/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index b52b2e698b..0000000000 --- a/packages/stream_chat_localizations/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/stream_chat_localizations/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/stream_chat_localizations/example/ios/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 21a3cc14c7..0000000000 --- a/packages/stream_chat_localizations/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/packages/stream_chat_localizations/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/stream_chat_localizations/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/packages/stream_chat_localizations/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/packages/stream_chat_localizations/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/stream_chat_localizations/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5ea..0000000000 --- a/packages/stream_chat_localizations/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/packages/stream_chat_localizations/example/ios/Runner/AppDelegate.swift b/packages/stream_chat_localizations/example/ios/Runner/AppDelegate.swift deleted file mode 100644 index 70693e4a8c..0000000000 --- a/packages/stream_chat_localizations/example/ios/Runner/AppDelegate.swift +++ /dev/null @@ -1,13 +0,0 @@ -import UIKit -import Flutter - -@UIApplicationMain -@objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } -} diff --git a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d36b1fab2d..0000000000 --- a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - }, - { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "Icon-App-1024x1024@1x.png", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png deleted file mode 100644 index dc9ada4725..0000000000 Binary files a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png deleted file mode 100644 index 28c6bf0301..0000000000 Binary files a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png deleted file mode 100644 index 2ccbfd967d..0000000000 Binary files a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index f091b6b0bc..0000000000 Binary files a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png deleted file mode 100644 index 4cde12118d..0000000000 Binary files a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index d0ef06e7ed..0000000000 Binary files a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png deleted file mode 100644 index dcdc2306c2..0000000000 Binary files a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png deleted file mode 100644 index 2ccbfd967d..0000000000 Binary files a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index c8f9ed8f5c..0000000000 Binary files a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png deleted file mode 100644 index a6d6b8609d..0000000000 Binary files a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index a6d6b8609d..0000000000 Binary files a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index 75b2d164a5..0000000000 Binary files a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png deleted file mode 100644 index c4df70d39d..0000000000 Binary files a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png deleted file mode 100644 index 6a84f41e14..0000000000 Binary files a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png deleted file mode 100644 index d0e1f58536..0000000000 Binary files a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json deleted file mode 100644 index 0bedcf2fd4..0000000000 --- a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "LaunchImage.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png deleted file mode 100644 index 9da19eacad..0000000000 Binary files a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png deleted file mode 100644 index 9da19eacad..0000000000 Binary files a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png deleted file mode 100644 index 9da19eacad..0000000000 Binary files a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png and /dev/null differ diff --git a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md deleted file mode 100644 index 89c2725b70..0000000000 --- a/packages/stream_chat_localizations/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Launch Screen Assets - -You can customize the launch screen with your own desired assets by replacing the image files in this directory. - -You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/packages/stream_chat_localizations/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/stream_chat_localizations/example/ios/Runner/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index f2e259c7c9..0000000000 --- a/packages/stream_chat_localizations/example/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/stream_chat_localizations/example/ios/Runner/Base.lproj/Main.storyboard b/packages/stream_chat_localizations/example/ios/Runner/Base.lproj/Main.storyboard deleted file mode 100644 index f3c28516fb..0000000000 --- a/packages/stream_chat_localizations/example/ios/Runner/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/stream_chat_localizations/example/ios/Runner/Info.plist b/packages/stream_chat_localizations/example/ios/Runner/Info.plist deleted file mode 100644 index 88370336c0..0000000000 --- a/packages/stream_chat_localizations/example/ios/Runner/Info.plist +++ /dev/null @@ -1,60 +0,0 @@ - - - - - CFBundleLocalizations - - en - hi - fr - it - es - ja - ko - pt - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - example - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - CADisableMinimumFrameDurationOnPhone - - UIApplicationSupportsIndirectInputEvents - - - diff --git a/packages/stream_chat_localizations/example/ios/Runner/Runner-Bridging-Header.h b/packages/stream_chat_localizations/example/ios/Runner/Runner-Bridging-Header.h deleted file mode 100644 index 308a2a560b..0000000000 --- a/packages/stream_chat_localizations/example/ios/Runner/Runner-Bridging-Header.h +++ /dev/null @@ -1 +0,0 @@ -#import "GeneratedPluginRegistrant.h" diff --git a/packages/stream_chat_localizations/example/lib/add_new_lang.dart b/packages/stream_chat_localizations/example/lib/add_new_lang.dart deleted file mode 100644 index 5f1575377d..0000000000 --- a/packages/stream_chat_localizations/example/lib/add_new_lang.dart +++ /dev/null @@ -1,764 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:stream_chat_localizations/stream_chat_localizations.dart'; - -class _NnStreamChatLocalizationsDelegate - extends LocalizationsDelegate { - const _NnStreamChatLocalizationsDelegate(); - - @override - bool isSupported(Locale locale) => locale.languageCode == 'nn'; - - @override - Future load(Locale locale) => - SynchronousFuture(const NnStreamChatLocalizations()); - - @override - bool shouldReload(_NnStreamChatLocalizationsDelegate old) => false; -} - -/// A custom set of localizations for the 'nn' locale. In this example, only -/// the value for launchUrlError was modified to use a custom message as -/// an example. Everything else uses the American English (en_US) messages -/// and formatting. -class NnStreamChatLocalizations extends GlobalStreamChatLocalizations { - /// Create an instance of the translation bundle for English. - const NnStreamChatLocalizations({super.localeName = 'nn'}); - - /// A [LocalizationsDelegate] for [NnStreamChatLocalizations]. - static const delegate = _NnStreamChatLocalizationsDelegate(); - - @override - String get launchUrlError => 'Custom error'; - - @override - String get loadingUsersError => 'Error loading users'; - - @override - String get noUsersLabel => 'There are no users currently'; - - @override - String get noPhotoOrVideoLabel => 'There is no photo or video'; - - @override - String get retryLabel => 'Retry'; - - @override - String get userLastOnlineText => 'Last online'; - - @override - String get userOnlineText => 'Online'; - - @override - String userTypingText(Iterable users) { - if (users.isEmpty) return ''; - final first = users.first; - if (users.length == 1) { - return '${first.name} is typing'; - } - return '${first.name} and ${users.length - 1} more are typing'; - } - - @override - String get threadReplyLabel => 'Thread Reply'; - - @override - String get onlyVisibleToYouText => 'Only visible to you'; - - @override - String threadReplyCountText(int count) => '$count Thread Replies'; - - @override - String attachmentsUploadProgressText({ - required int remaining, - required int total, - }) => - 'Uploading $remaining/$total ...'; - - @override - String pinnedByUserText({ - required User pinnedBy, - required User currentUser, - }) { - final pinnedByCurrentUser = currentUser.id == pinnedBy.id; - if (pinnedByCurrentUser) return 'Pinned by You'; - return 'Pinned by ${pinnedBy.name}'; - } - - @override - String get sendMessagePermissionError => - "You don't have permission to send messages"; - - @override - String get emptyMessagesText => 'There are no messages currently'; - - @override - String get genericErrorText => 'Something went wrong'; - - @override - String get loadingMessagesError => 'Error loading messages'; - - @override - String resultCountText(int count) => '$count results'; - - @override - String get messageDeletedText => 'This message is deleted.'; - - @override - String get messageDeletedLabel => 'Message deleted'; - - @override - String get editedMessageLabel => 'Edited'; - - @override - String get messageReactionsLabel => 'Message Reactions'; - - @override - String get emptyChatMessagesText => 'No chats here yet...'; - - @override - String threadSeparatorText(int replyCount) { - if (replyCount == 1) return '1 Reply'; - return '$replyCount Replies'; - } - - @override - String get connectedLabel => 'Connected'; - - @override - String get disconnectedLabel => 'Disconnected'; - - @override - String get reconnectingLabel => 'Reconnecting...'; - - @override - String get alsoSendAsDirectMessageLabel => 'Also send as direct message'; - - @override - String get addACommentOrSendLabel => 'Add a comment or send'; - - @override - String get searchGifLabel => 'Search GIFs'; - - @override - String get writeAMessageLabel => 'Write a message'; - - @override - String get instantCommandsLabel => 'Instant Commands'; - - @override - String fileTooLargeAfterCompressionError(double limitInMB) => - 'The file is too large to upload. ' - 'The file size limit is $limitInMB MB. ' - 'We tried compressing it, but it was not enough.'; - - @override - String fileTooLargeError(double limitInMB) => - 'The file is too large to upload. The file size limit is $limitInMB MB.'; - - @override - String get couldNotReadBytesFromFileError => - 'Could not read bytes from file.'; - - @override - String get addAFileLabel => 'Add a file'; - - @override - String get photoFromCameraLabel => 'Photo from camera'; - - @override - String get uploadAFileLabel => 'Upload a file'; - - @override - String get uploadAPhotoLabel => 'Upload a photo'; - - @override - String get uploadAVideoLabel => 'Upload a video'; - - @override - String get videoFromCameraLabel => 'Video from camera'; - - @override - String get okLabel => 'OK'; - - @override - String get somethingWentWrongError => 'Something went wrong'; - - @override - String get addMoreFilesLabel => 'Add more files'; - - @override - String get enablePhotoAndVideoAccessMessage => - 'Please enable access to your photos' - '\nand videos so you can share them with friends.'; - - @override - String get allowGalleryAccessMessage => 'Allow access to your gallery'; - - @override - String get flagMessageLabel => 'Flag Message'; - - @override - String get flagMessageQuestion => - 'Do you want to send a copy of this message to a' - '\nmoderator for further investigation?'; - - @override - String get flagLabel => 'FLAG'; - - @override - String get cancelLabel => 'CANCEL'; - - @override - String get flagMessageSuccessfulLabel => 'Message flagged'; - - @override - String get flagMessageSuccessfulText => - 'The message has been reported to a moderator.'; - - @override - String get deleteLabel => 'DELETE'; - - @override - String get deleteMessageLabel => 'Delete Message'; - - @override - String get deleteMessageQuestion => - 'Are you sure you want to permanently delete this\nmessage?'; - - @override - String get operationCouldNotBeCompletedText => - "The operation couldn't be completed."; - - @override - String get replyLabel => 'Reply'; - - @override - String togglePinUnpinText({required bool pinned}) { - if (pinned) return 'Unpin from Conversation'; - return 'Pin to Conversation'; - } - - @override - String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed}) { - if (isDeleteFailed) return 'Retry Deleting Message'; - return 'Delete Message'; - } - - @override - String get copyMessageLabel => 'Copy Message'; - - @override - String get editMessageLabel => 'Edit Message'; - - @override - String toggleResendOrResendEditedMessage({required bool isUpdateFailed}) { - if (isUpdateFailed) return 'Resend Edited Message'; - return 'Resend'; - } - - @override - String get photosLabel => 'Photos'; - - String _getDay(DateTime dateTime) { - final now = DateTime.now(); - final today = DateTime(now.year, now.month, now.day); - final yesterday = DateTime(now.year, now.month, now.day - 1); - - final date = DateTime(dateTime.year, dateTime.month, dateTime.day); - - if (date == today) { - return 'today'; - } else if (date == yesterday) { - return 'yesterday'; - } else { - return 'on ${Jiffy.parseFromDateTime(date).MMMd}'; - } - } - - @override - String sentAtText({required DateTime date, required DateTime time}) { - final atTime = Jiffy.parseFromDateTime(time.toLocal()); - return 'Sent ${_getDay(date)} at ${atTime.jm}'; - } - - @override - String get todayLabel => 'Today'; - - @override - String get yesterdayLabel => 'Yesterday'; - - @override - String get channelIsMutedText => 'Channel is muted'; - - @override - String get noTitleText => 'No title'; - - @override - String get letsStartChattingLabel => 'Let’s start chatting!'; - - @override - String get sendingFirstMessageLabel => - 'How about sending your first message to a friend?'; - - @override - String get startAChatLabel => 'Start a chat'; - - @override - String get loadingChannelsError => 'Error loading channels'; - - @override - String get deleteConversationLabel => 'Delete Conversation'; - - @override - String get deleteConversationQuestion => - 'Are you sure you want to delete this conversation?'; - - @override - String get streamChatLabel => 'Stream Chat'; - - @override - String get searchingForNetworkText => 'Searching for Network'; - - @override - String get offlineLabel => 'Offline...'; - - @override - String get tryAgainLabel => 'Try Again'; - - @override - String membersCountText(int count) { - if (count == 1) return '1 Member'; - return '$count Members'; - } - - @override - String watchersCountText(int count) { - if (count == 1) return '1 Online'; - return '$count Online'; - } - - @override - String get viewInfoLabel => 'View Info'; - - @override - String get leaveGroupLabel => 'Leave Group'; - - @override - String get leaveLabel => 'LEAVE'; - - @override - String get leaveConversationLabel => 'Leave conversation'; - - @override - String get leaveConversationQuestion => - 'Are you sure you want to leave this conversation?'; - - @override - String get showInChatLabel => 'Show in Chat'; - - @override - String get saveImageLabel => 'Save Image'; - - @override - String get saveVideoLabel => 'Save Video'; - - @override - String get uploadErrorLabel => 'UPLOAD ERROR'; - - @override - String get giphyLabel => 'Giphy'; - - @override - String get shuffleLabel => 'Shuffle'; - - @override - String get sendLabel => 'Send'; - - @override - String get withText => 'with'; - - @override - String get inText => 'in'; - - @override - String get youText => 'You'; - - @override - String galleryPaginationText({ - required int currentPage, - required int totalPages, - }) => - '$currentPage of $totalPages'; - - @override - String get fileText => 'File'; - - @override - String get replyToMessageLabel => 'Reply to Message'; - - @override - String attachmentLimitExceedError(int limit) => - 'Attachment limit exceeded, limit: $limit'; - - @override - String get slowModeOnLabel => 'Slow mode ON'; - - @override - String get downloadLabel => 'Download'; - - @override - String toggleMuteUnmuteUserText({required bool isMuted}) { - if (isMuted) { - return 'Unmute User'; - } else { - return 'Mute User'; - } - } - - @override - String toggleMuteUnmuteGroupQuestion({required bool isMuted}) { - if (isMuted) { - return 'Are you sure you want to unmute this group?'; - } else { - return 'Are you sure you want to mute this group?'; - } - } - - @override - String toggleMuteUnmuteUserQuestion({required bool isMuted}) { - if (isMuted) { - return 'Are you sure you want to unmute this user?'; - } else { - return 'Are you sure you want to mute this user?'; - } - } - - @override - String toggleMuteUnmuteAction({required bool isMuted}) { - if (isMuted) { - return 'UNMUTE'; - } else { - return 'MUTE'; - } - } - - @override - String toggleMuteUnmuteGroupText({required bool isMuted}) { - if (isMuted) { - return 'Unmute Group'; - } else { - return 'Mute Group'; - } - } - - @override - String get linkDisabledDetails => - 'Sending links is not allowed in this conversation.'; - - @override - String get linkDisabledError => 'Links are disabled'; - - @override - String get viewLibrary => 'View library'; - - @override - String unreadMessagesSeparatorText() => 'New messages'; - - @override - String get enableFileAccessMessage => 'Enable file access to continue'; - - @override - String get allowFileAccessMessage => 'Allow access to files'; - - @override - String get markAsUnreadLabel => 'Mark as unread'; - - @override - String unreadCountIndicatorLabel({required int unreadCount}) { - return '$unreadCount unread'; - } - - @override - String get markUnreadError => - 'Error marking message unread. Cannot mark unread messages older than' - ' the newest 100 channel messages.'; - - @override - String createPollLabel({bool isNew = false}) { - if (isNew) return 'Create a new poll'; - return 'Create Poll'; - } - - @override - String get questionsLabel => 'Questions'; - - @override - String get askAQuestionLabel => 'Ask a question'; - - @override - String? pollQuestionValidationError(int length, Range range) { - final (:min, :max) = range; - - // Check if the question is too short. - if (min != null && length < min) { - return 'Question must be at least $min characters long'; - } - - // Check if the question is too long. - if (max != null && length > max) { - return 'Question must be at most $max characters long'; - } - - return null; - } - - @override - String optionLabel({bool isPlural = false}) { - if (isPlural) return 'Options'; - return 'Option'; - } - - @override - String get pollOptionEmptyError => 'Option cannot be empty'; - - @override - String get pollOptionDuplicateError => 'This is already an option'; - - @override - String get addAnOptionLabel => 'Add an option'; - - @override - String get multipleAnswersLabel => 'Multiple answers'; - - @override - String get maximumVotesPerPersonLabel => 'Maximum votes per person'; - - @override - String? maxVotesPerPersonValidationError(int votes, Range range) { - final (:min, :max) = range; - - if (min != null && votes < min) { - return 'Vote count must be at least $min'; - } - - if (max != null && votes > max) { - return 'Vote count must be at most $max'; - } - - return null; - } - - @override - String get anonymousPollLabel => 'Anonymous poll'; - - @override - String get pollOptionsLabel => 'Poll Options'; - - @override - String get suggestAnOptionLabel => 'Suggest an option'; - - @override - String get enterANewOptionLabel => 'Enter a new option'; - - @override - String get addACommentLabel => 'Add a comment'; - - @override - String get pollCommentsLabel => 'Poll Comments'; - - @override - String get updateYourCommentLabel => 'Update your comment'; - - @override - String get enterYourCommentLabel => 'Enter your comment'; - - @override - String get createLabel => 'Create'; - - @override - String pollVotingModeLabel(PollVotingMode votingMode) { - return votingMode.when( - disabled: () => 'Vote ended', - unique: () => 'Select one', - limited: (count) => 'Select up to $count', - all: () => 'Select one or more', - ); - } - - @override - String seeAllOptionsLabel({int? count}) { - if (count == null) return 'See all options'; - return 'See all $count options'; - } - - @override - String get viewCommentsLabel => 'View Comments'; - - @override - String get viewResultsLabel => 'View Results'; - - @override - String get endVoteLabel => 'End Vote'; - - @override - String get pollResultsLabel => 'Poll Results'; - - @override - String showAllVotesLabel({int? count}) { - if (count == null) return 'Show all votes'; - return 'Show all $count votes'; - } - - @override - String voteCountLabel({int? count}) => switch (count) { - null || < 1 => '0 votes', - 1 => '1 vote', - _ => '$count votes', - }; - - @override - String get noPollVotesLabel => 'There are no poll votes currently'; - - @override - String get loadingPollVotesError => 'Error loading poll votes'; - - @override - String get repliedToLabel => 'replied to:'; - - @override - String newThreadsLabel({required int count}) { - if (count == 1) return '1 new thread'; - return '$count new threads'; - } - - @override - String get slideToCancelLabel => 'Slide to cancel'; - - @override - String get holdToRecordLabel => 'Hold to record, release to send.'; -} - -void main() async { - WidgetsFlutterBinding.ensureInitialized(); - - /// Create a new instance of [StreamChatClient] passing the apikey obtained - /// from your project dashboard. - final client = StreamChatClient( - 's2dxdhpxd94g', - logLevel: Level.INFO, - ); - - /// Set the current user and connect the websocket. In a production - /// scenario, this should be done using a backend to generate a user token - /// using our server SDK. - /// - /// Please see the following for more information: - /// https://getstream.io/chat/docs/ios_user_setup_and_tokens/ - await client.connectUser( - User(id: 'super-band-9'), - '''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''', - ); - - final channel = client.channel('messaging', id: 'godevs'); - - await channel.watch(); - - runApp( - MyApp( - client: client, - channel: channel, - ), - ); -} - -/// Example application using Stream Chat Flutter widgets. -/// -/// Stream Chat Flutter is a set of Flutter widgets which provide full chat -/// functionalities for building Flutter applications using Stream. If you'd -/// prefer using minimal wrapper widgets for your app, please see our other -/// package, `stream_chat_flutter_core`. -class MyApp extends StatelessWidget { - /// Example using Stream's Flutter package. - /// - /// If you'd prefer using minimal wrapper widgets for your app, please see - /// our other package, `stream_chat_flutter_core`. - const MyApp({ - super.key, - required this.client, - required this.channel, - }); - - /// Instance of Stream Client. - /// - /// Stream's [StreamChatClient] can be used to connect to our servers and - /// set the default user for the application. Performing these actions - /// trigger a websocket connection allowing for real-time updates. - final StreamChatClient client; - - /// Instance of the Channel - final Channel channel; - - @override - Widget build(BuildContext context) { - return MaterialApp( - theme: ThemeData.light(), - darkTheme: ThemeData.dark(), - // Add all the supported locales - supportedLocales: const [ - Locale('en'), - Locale('hi'), - Locale('fr'), - Locale('it'), - Locale('es'), - Locale('ja'), - Locale('ko'), - // Add support for additional 'nn' locale - Locale('nn'), - ], - // Add overridden "NnStreamChatLocalizations.delegate" along with - // "GlobalStreamChatLocalizations.delegates" - localizationsDelegates: const [ - NnStreamChatLocalizations.delegate, - ...GlobalStreamChatLocalizations.delegates, - ], - builder: (context, widget) => StreamChat( - client: client, - child: widget, - ), - home: StreamChannel( - channel: channel, - child: const ChannelPage(), - ), - ); - } -} - -/// A list of messages sent in the current channel. -/// -/// This is implemented using [StreamMessageListView], -/// a widget that provides query -/// functionalities fetching the messages from the api and showing them in a -/// listView. -class ChannelPage extends StatelessWidget { - /// Creates the page that shows the list of messages - const ChannelPage({ - super.key, - }); - - @override - Widget build(BuildContext context) { - return const Scaffold( - appBar: StreamChannelHeader(), - body: Column( - children: [ - Expanded( - child: StreamMessageListView(), - ), - StreamMessageInput(), - ], - ), - ); - } -} diff --git a/packages/stream_chat_localizations/example/lib/main.dart b/packages/stream_chat_localizations/example/lib/main.dart deleted file mode 100644 index 22abb65fab..0000000000 --- a/packages/stream_chat_localizations/example/lib/main.dart +++ /dev/null @@ -1,121 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:stream_chat_localizations/stream_chat_localizations.dart'; - -void main() async { - WidgetsFlutterBinding.ensureInitialized(); - - /// Create a new instance of [StreamChatClient] passing the apikey obtained - /// from your project dashboard. - final client = StreamChatClient( - 's2dxdhpxd94g', - logLevel: Level.INFO, - ); - - /// Set the current user and connect the websocket. In a production - /// scenario, this should be done using a backend to generate a user token - /// using our server SDK. - /// - /// Please see the following for more information: - /// https://getstream.io/chat/docs/ios_user_setup_and_tokens/ - await client.connectUser( - User(id: 'super-band-9'), - '''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''', - ); - - final channel = client.channel('messaging', id: 'godevs'); - - await channel.watch(); - - runApp( - MyApp( - client: client, - channel: channel, - ), - ); -} - -/// Example application using Stream Chat Flutter widgets. -/// -/// Stream Chat Flutter is a set of Flutter widgets which provide full chat -/// functionalities for building Flutter applications using Stream. If you'd -/// prefer using minimal wrapper widgets for your app, please see our other -/// package, `stream_chat_flutter_core`. -class MyApp extends StatelessWidget { - /// Example using Stream's Flutter package. - /// - /// If you'd prefer using minimal wrapper widgets for your app, please see - /// our other package, `stream_chat_flutter_core`. - const MyApp({ - super.key, - required this.client, - required this.channel, - }); - - /// Instance of Stream Client. - /// - /// Stream's [StreamChatClient] can be used to connect to our servers and - /// set the default user for the application. Performing these actions - /// trigger a websocket connection allowing for real-time updates. - final StreamChatClient client; - - /// Instance of the Channel - final Channel channel; - - @override - Widget build(BuildContext context) => MaterialApp( - theme: ThemeData.light(), - darkTheme: ThemeData.dark(), - // Add all the supported locales - supportedLocales: const [ - Locale('en'), - Locale('hi'), - Locale('fr'), - Locale('it'), - Locale('es'), - Locale('ja'), - Locale('ko'), - Locale('pt'), - ], - // Add GlobalStreamChatLocalizations.delegates - localizationsDelegates: GlobalStreamChatLocalizations.delegates, - // Programatically set the locale (this is a global change) - locale: const Locale('fr'), - builder: (context, widget) => StreamChat( - client: client, - child: widget, - ), - home: StreamChannel( - channel: channel, - child: const ChannelPage(), - ), - ); -} - -/// A list of messages sent in the current channel. -/// -/// This is implemented using [StreamMessageListView], -/// a widget that provides query -/// functionalities fetching the messages from the api and showing them in a -/// listView. -class ChannelPage extends StatelessWidget { - /// Creates the page that shows the list of messages - const ChannelPage({ - super.key, - }); - - @override - Widget build(BuildContext context) { - return const Scaffold( - appBar: StreamChannelHeader(), - body: Column( - children: [ - Expanded( - child: StreamMessageListView(), - ), - StreamMessageInput(), - ], - ), - ); - } -} diff --git a/packages/stream_chat_localizations/example/lib/override_lang.dart b/packages/stream_chat_localizations/example/lib/override_lang.dart deleted file mode 100644 index 1dc05f9100..0000000000 --- a/packages/stream_chat_localizations/example/lib/override_lang.dart +++ /dev/null @@ -1,148 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:stream_chat_localizations/stream_chat_localizations.dart'; - -class _CustomStreamChatLocalizationsDelegate - extends LocalizationsDelegate { - const _CustomStreamChatLocalizationsDelegate(); - - @override - bool isSupported(Locale locale) => locale.languageCode == 'en'; - - @override - Future load(Locale locale) => - SynchronousFuture(CustomStreamChatLocalizationsEn()); - - @override - bool shouldReload(_CustomStreamChatLocalizationsDelegate old) => false; -} - -/// Customized translations for English ('en') -class CustomStreamChatLocalizationsEn extends StreamChatLocalizationsEn { - /// A [LocalizationsDelegate] for [StreamChatLocalizationsEn]. - static const delegate = _CustomStreamChatLocalizationsDelegate(); - - @override - String get launchUrlError => 'My custom error'; -} - -void main() async { - WidgetsFlutterBinding.ensureInitialized(); - - /// Create a new instance of [StreamChatClient] passing the apikey obtained - /// from your project dashboard. - final client = StreamChatClient( - 's2dxdhpxd94g', - logLevel: Level.INFO, - ); - - /// Set the current user and connect the websocket. In a production - /// scenario, this should be done using a backend to generate a user token - /// using our server SDK. - /// - /// Please see the following for more information: - /// https://getstream.io/chat/docs/ios_user_setup_and_tokens/ - await client.connectUser( - User(id: 'super-band-9'), - '''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''', - ); - - final channel = client.channel('messaging', id: 'godevs'); - - await channel.watch(); - - runApp( - MyApp( - client: client, - channel: channel, - ), - ); -} - -/// Example application using Stream Chat Flutter widgets. -/// -/// Stream Chat Flutter is a set of Flutter widgets which provide full chat -/// functionalities for building Flutter applications using Stream. If you'd -/// prefer using minimal wrapper widgets for your app, please see our other -/// package, `stream_chat_flutter_core`. -class MyApp extends StatelessWidget { - /// Example using Stream's Flutter package. - /// - /// If you'd prefer using minimal wrapper widgets for your app, please see - /// our other package, `stream_chat_flutter_core`. - const MyApp({ - super.key, - required this.client, - required this.channel, - }); - - /// Instance of Stream Client. - /// - /// Stream's [StreamChatClient] can be used to connect to our servers and - /// set the default user for the application. Performing these actions - /// trigger a websocket connection allowing for real-time updates. - final StreamChatClient client; - - /// Instance of the Channel - final Channel channel; - - @override - Widget build(BuildContext context) => MaterialApp( - theme: ThemeData.light(), - darkTheme: ThemeData.dark(), - // Add all the supported locales - supportedLocales: const [ - Locale('en'), - Locale('hi'), - Locale('fr'), - Locale('it'), - Locale('es'), - Locale('ja'), - Locale('ko'), - Locale('pt'), - ], - // Add overridden "CustomStreamChatLocalizationsEn.delegate" along with - // "GlobalStreamChatLocalizations.delegates" - localizationsDelegates: const [ - CustomStreamChatLocalizationsEn.delegate, - ...GlobalStreamChatLocalizations.delegates, - ], - builder: (context, widget) => StreamChat( - client: client, - child: widget, - ), - home: StreamChannel( - channel: channel, - child: const ChannelPage(), - ), - ); -} - -/// A list of messages sent in the current channel. -/// -/// This is implemented using [StreamMessageListView], -/// a widget that provides query -/// functionalities fetching the messages from the api and showing them in a -/// listView. -class ChannelPage extends StatelessWidget { - /// Creates the page that shows the list of messages - const ChannelPage({ - super.key, - }); - - @override - Widget build(BuildContext context) { - return const Scaffold( - appBar: StreamChannelHeader(), - body: Column( - children: [ - Expanded( - child: StreamMessageListView(), - ), - StreamMessageInput(), - ], - ), - ); - } -} diff --git a/packages/stream_chat_localizations/example/pubspec.yaml b/packages/stream_chat_localizations/example/pubspec.yaml deleted file mode 100644 index 2d66ba6f9b..0000000000 --- a/packages/stream_chat_localizations/example/pubspec.yaml +++ /dev/null @@ -1,31 +0,0 @@ -name: stream_chat_localizations_example -description: A new Flutter project. - -publish_to: 'none' -version: 1.0.0+1 - -# Note: The environment configuration and dependency versions are managed by Melos. -# -# Do not edit them manually. -# -# Steps to update dependencies: -# 1. Modify the version in the melos.yaml file. -# 2. Run `melos bootstrap` to apply changes. -# -# Steps to add a new dependency: -# 1. Add the dependency to this list. -# 2. Add it to the melos.yaml file for future updates. - -environment: - sdk: ^3.6.2 - flutter: ">=3.27.4" - -dependencies: - cupertino_icons: ^1.0.3 - flutter: - sdk: flutter - stream_chat_flutter: ^9.4.0 - stream_chat_localizations: ^9.4.0 - -flutter: - uses-material-design: true \ No newline at end of file diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations.dart deleted file mode 100644 index 62248df818..0000000000 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations.dart +++ /dev/null @@ -1,191 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_localizations/flutter_localizations.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -part 'stream_chat_localizations_ca.dart'; -part 'stream_chat_localizations_de.dart'; -part 'stream_chat_localizations_en.dart'; -part 'stream_chat_localizations_es.dart'; -part 'stream_chat_localizations_fr.dart'; -part 'stream_chat_localizations_hi.dart'; -part 'stream_chat_localizations_it.dart'; -part 'stream_chat_localizations_ja.dart'; -part 'stream_chat_localizations_ko.dart'; -part 'stream_chat_localizations_pt.dart'; -part 'stream_chat_localizations_no.dart'; - -/// The set of supported languages, as language code strings. -/// -/// The [GlobalStreamChatLocalizations.delegate] can generate localizations for -/// any [Locale] with a language code from this set. -/// -/// See also: -/// -/// * [getStreamChatTranslation], whose documentation describes these values. -const kStreamChatSupportedLanguages = { - 'en', - 'hi', - 'fr', - 'it', - 'es', - 'ca', - 'ja', - 'ko', - 'pt', - 'de', - 'no', -}; - -/// Creates a [GlobalStreamChatLocalizations] instance for the given `locale`. -/// -/// All of the function's arguments except `locale` will be passed to the -/// [GlobalStreamChatLocalizations] constructor. (The `localeName` argument -/// of that constructor is specified by the actual subclass constructor by this -/// function.) -/// -/// The following locales are supported by this package: -/// -/// * `en` - English -/// -/// Generally speaking, this method is only intended to be used by -/// [GlobalStreamChatLocalizations.delegate]. -GlobalStreamChatLocalizations? getStreamChatTranslation(Locale locale) { - final languageCode = locale.languageCode; - assert( - kStreamChatSupportedLanguages.contains(languageCode), - 'getStreamChatTranslation() called for unsupported locale "$locale"', - ); - switch (locale.languageCode) { - case 'en': - return const StreamChatLocalizationsEn(); - case 'hi': - return const StreamChatLocalizationsHi(); - case 'fr': - return const StreamChatLocalizationsFr(); - case 'it': - return const StreamChatLocalizationsIt(); - case 'es': - return const StreamChatLocalizationsEs(); - case 'ca': - return const StreamChatLocalizationsCa(); - case 'ja': - return const StreamChatLocalizationsJa(); - case 'ko': - return const StreamChatLocalizationsKo(); - case 'pt': - return const StreamChatLocalizationsPt(); - case 'de': - return const StreamChatLocalizationsDe(); - case 'no': - return const StreamChatLocalizationsNo(); - default: - return null; - } -} - -/// Implementation of localized strings for the stream chat widgets -/// -/// ## Supported languages -/// -/// This class supports locales with the following [Locale.languageCode]s: -/// -/// {@macro flutter.localizations.material.languages} -/// -/// This list is available programmatically via [kStreamChatSupportedLanguages]. -/// -/// ## Sample code -/// -/// To include the localizations provided by this class in a [MaterialApp], -/// add [GlobalStreamChatLocalizations.delegates] to -/// [MaterialApp.localizationsDelegates], and specify the locales your -/// app supports with [MaterialApp.supportedLocales]: -/// -/// ```dart -/// new MaterialApp( -/// localizationsDelegates: GlobalStreamChatLocalizations.delegates, -/// supportedLocales: [ -/// const Locale('en', 'US'), // American English -/// // ... -/// ], -/// // ... -/// ) -/// ``` -/// -abstract class GlobalStreamChatLocalizations - implements StreamChatLocalizations { - /// Initializes an object that defines the StreamChat widget's localized - /// strings for the given `localeName`. - const GlobalStreamChatLocalizations({ - required String localeName, - }) : _localeName = localeName; - - // ignore: unused_field - final String _localeName; - - /// A [LocalizationsDelegate] for [StreamChatLocalizations]. - /// - /// Most internationalized apps will use - /// [GlobalStreamChatLocalizations.delegates] as the value of - /// [MaterialApp.localizationsDelegates] to include the localizations for both - /// the flutter and stream chat widget libraries. - static const LocalizationsDelegate delegate = - _StreamChatLocalizationsDelegate(); - - /// A value for [MaterialApp.localizationsDelegates] that's typically used by - /// internationalized apps. - /// - /// ## Sample code - /// - /// To include the localizations provided by this class and by - /// [GlobalWidgetsLocalizations] in a [MaterialApp], - /// use [GlobalStreamChatLocalizations.delegates] as the value of - /// [MaterialApp.localizationsDelegates], and specify the locales your - /// app supports with [MaterialApp.supportedLocales]: - /// - /// ```dart - /// new MaterialApp( - /// localizationsDelegates: GlobalStreamChatLocalizations.delegates, - /// supportedLocales: [ - /// const Locale('en', 'US'), // English - /// ], - /// // ... - /// ) - /// ``` - static const List delegates = [ - GlobalStreamChatLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - ]; -} - -class _StreamChatLocalizationsDelegate - extends LocalizationsDelegate { - const _StreamChatLocalizationsDelegate(); - - @override - bool isSupported(Locale locale) => - kStreamChatSupportedLanguages.contains(locale.languageCode); - - static final _loadedTranslations = - >{}; - - @override - Future load(Locale locale) { - assert(isSupported(locale), ''); - return _loadedTranslations.putIfAbsent( - locale, - () => SynchronousFuture( - getStreamChatTranslation(locale)!, - ), - ); - } - - @override - bool shouldReload(_StreamChatLocalizationsDelegate old) => false; - - @override - String toString() => 'GlobalStreamChatLocalizations.delegate(' - '${kStreamChatSupportedLanguages.length} locales)'; -} diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ca.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ca.dart deleted file mode 100644 index a27d599110..0000000000 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ca.dart +++ /dev/null @@ -1,623 +0,0 @@ -part of 'stream_chat_localizations.dart'; - -/// The translations for Catalan (`ca`). -class StreamChatLocalizationsCa extends GlobalStreamChatLocalizations { - /// Create an instance of the translation bundle for Catalan. - const StreamChatLocalizationsCa({super.localeName = 'ca'}); - - @override - String get launchUrlError => "No s'ha pogut obrir l'enllaƧ"; - - @override - String get loadingUsersError => "Error de cĆ rrega de l'usuari"; - - @override - String get noUsersLabel => 'Actualment no hi ha usuaris'; - - @override - String get noPhotoOrVideoLabel => 'No hi ha fotos ni vĆ­deos'; - - @override - String get retryLabel => 'Torna-ho a provar'; - - @override - String get userLastOnlineText => 'Última vegada en lĆ­nia'; - - @override - String get userOnlineText => 'En lĆ­nia'; - - @override - String userTypingText(Iterable users) { - if (users.isEmpty) return ''; - final first = users.first; - if (users.length == 1) { - return '${first.name} estĆ  escrivint'; - } - return '${first.name} y ${users.length - 1} estan escrivint'; - } - - @override - String get threadReplyLabel => 'Respon al fil'; - - @override - String get onlyVisibleToYouText => 'NomĆ©s visible per a tu'; - - @override - String threadReplyCountText(int count) => '$count respostes al fil'; - - @override - String attachmentsUploadProgressText({ - required int remaining, - required int total, - }) => - 'TransferĆØncia en curs $remaining/$total ...'; - - @override - String pinnedByUserText({ - required User pinnedBy, - required User currentUser, - }) { - final pinnedByCurrentUser = currentUser.id == pinnedBy.id; - if (pinnedByCurrentUser) return 'Fixat per tu'; - return 'Fixat per ${pinnedBy.name}'; - } - - @override - String get sendMessagePermissionError => - 'No tens permĆ­s per enviar missatges'; - - @override - String get emptyMessagesText => 'Actualment no hi ha missatges'; - - @override - String get genericErrorText => 'Hi ha hagut un problema'; - - @override - String get loadingMessagesError => - 'Hi ha hagut un error mentre carregava el missatge'; - - @override - String resultCountText(int count) => '$count resultats'; - - @override - String get messageDeletedText => 'Aquest missatge ha estat esborrat'; - - @override - String get messageDeletedLabel => 'Missatge esborrat'; - - @override - String get editedMessageLabel => 'Editat'; - - @override - String get messageReactionsLabel => 'Reaccions dels missatges'; - - @override - String get emptyChatMessagesText => 'Encara no hi ha missatges...'; - - @override - String threadSeparatorText(int replyCount) { - if (replyCount == 1) return '1 resposta'; - return '$replyCount respostes'; - } - - @override - String get connectedLabel => 'Connectat'; - - @override - String get disconnectedLabel => 'Desconnectat'; - - @override - String get reconnectingLabel => 'Reconnectant...'; - - @override - String get alsoSendAsDirectMessageLabel => - 'Enviar tambĆ© com a missatge directe'; - - @override - String get addACommentOrSendLabel => 'Afegir un comentari o enviar'; - - @override - String get searchGifLabel => 'Cerca de GIFs'; - - @override - String get writeAMessageLabel => 'Escriure un missatge'; - - @override - String get instantCommandsLabel => 'Commandes instantĆ nies'; - - @override - String fileTooLargeAfterCompressionError(double limitInMB) => - 'El fitxer Ć©s massa gran per descarregar-lo. ' - 'La mida mĆ xima del fitxer Ć©s de $limitInMB MB. ' - 'Hem intentat comprimir-lo, pero no ha estat suficient.'; - - @override - String fileTooLargeError(double limitInMB) => - 'El fitxer Ć©s massa gran per descarregar-lo. ' - 'La mida mĆ xima del fitxer Ć©s de $limitInMB MB.'; - - @override - String get couldNotReadBytesFromFileError => - "No s'han pogut llegir els bytes del fitxer."; - - @override - String get addAFileLabel => 'Afegeix un fitxer'; - - @override - String get photoFromCameraLabel => 'Foto de la cĆ mera'; - - @override - String get uploadAFileLabel => 'Transferir un fitxer'; - - @override - String get uploadAPhotoLabel => 'Puja una foto'; - - @override - String get uploadAVideoLabel => 'Puja un vĆ­deo'; - - @override - String get videoFromCameraLabel => 'VĆ­deo de la cĆ mera'; - - @override - String get okLabel => "D'acord"; - - @override - String get somethingWentWrongError => 'Alguna cosa ha anat malament'; - - @override - String get addMoreFilesLabel => 'Afegir mĆ©s fitxers'; - - @override - String get enablePhotoAndVideoAccessMessage => - "Si us plau, permet l'accĆ©s a les teves fotos" - '\ni vĆ­deos per a que puguis compartir-los'; - - @override - String get allowGalleryAccessMessage => "Permet l'accĆ©s a la galeria"; - - @override - String get flagMessageLabel => 'Reporta un missatge'; - - @override - String get flagMessageQuestion => - "Vols enviar una còpia d'aquest missatge a un" - '\nmoderador per una major investigació?'; - - @override - String get flagLabel => 'REPORTA'; - - @override - String get cancelLabel => 'CANCELĀ·LA'; - - @override - String get flagMessageSuccessfulLabel => 'Missatge reportat'; - - @override - String get flagMessageSuccessfulText => - 'Aquest missatge ha estat reportat a un moderador'; - - @override - String get deleteLabel => 'ESBORRA'; - - @override - String get deleteMessageLabel => 'Esborra el missatge'; - - @override - String get deleteMessageQuestion => - 'EstĆ s segur que vols esborrar aquest\nmissatge de forma permanent?'; - - @override - String get operationCouldNotBeCompletedText => - "L'operació no s'ha pogut completar"; - - @override - String get replyLabel => 'Respondre'; - - @override - String togglePinUnpinText({required bool pinned}) { - if (pinned) return 'Desfixa de la conversa'; - return 'Fixa a la conversa'; - } - - @override - String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed}) { - if (isDeleteFailed) return 'Reintenta esborrar el missatge'; - return 'Esborra el missatge'; - } - - @override - String get copyMessageLabel => 'Copia el missatge'; - - @override - String get editMessageLabel => 'Edita el missatge'; - - @override - String toggleResendOrResendEditedMessage({required bool isUpdateFailed}) { - if (isUpdateFailed) return 'Reenvia el missatge modificat'; - return 'Reenvia'; - } - - @override - String get photosLabel => 'Fotos'; - - String _getDay(DateTime dateTime) { - final now = DateTime.now(); - final today = DateTime(now.year, now.month, now.day); - final yesterday = DateTime(now.year, now.month, now.day - 1); - - final date = DateTime(dateTime.year, dateTime.month, dateTime.day); - - if (date == today) { - return 'avui'; - } else if (date == yesterday) { - return 'ahir'; - } else { - return 'el ${Jiffy.parseFromDateTime(date).MMMd}'; - } - } - - @override - String sentAtText({required DateTime date, required DateTime time}) { - final atTime = Jiffy.parseFromDateTime(time.toLocal()); - return 'Enviat el ${_getDay(date)} a les ${atTime.jm}'; - } - - @override - String get todayLabel => 'Avui'; - - @override - String get yesterdayLabel => 'Ahir'; - - @override - String get channelIsMutedText => 'El canal estĆ  silenciat'; - - @override - String get noTitleText => 'Sense tĆ­tol'; - - @override - String get letsStartChattingLabel => 'Comencem a parlar!'; - - @override - String get sendingFirstMessageLabel => - 'QuĆØ et sembla enviar el teu primer missatge?'; - - @override - String get startAChatLabel => 'Inicia una conversa'; - - @override - String get loadingChannelsError => 'Error al carregar els canals'; - - @override - String get deleteConversationLabel => 'Esborra la conversa'; - - @override - String get deleteConversationQuestion => - 'EstĆ s segur que vols esborrar aquesta conversa?'; - - @override - String get streamChatLabel => 'Stream Chat'; - - @override - String get searchingForNetworkText => 'Cercant xarxa'; - - @override - String get offlineLabel => 'Sense connexió...'; - - @override - String get tryAgainLabel => 'Torna-ho a provar'; - - @override - String membersCountText(int count) { - if (count == 1) return '1 membre'; - return '$count membres'; - } - - @override - String watchersCountText(int count) { - if (count == 1) return '1 En lĆ­nia'; - return '$count En lĆ­nia'; - } - - @override - String get viewInfoLabel => 'Veure informació'; - - @override - String get leaveGroupLabel => 'Sortir del Grup'; - - @override - String get leaveLabel => 'SURT'; - - @override - String get leaveConversationLabel => 'Surt de la conversa'; - - @override - String get leaveConversationQuestion => - "EstĆ s segur que vols sortir d'aquesta conversa?"; - - @override - String get showInChatLabel => 'Mostra al xat'; - - @override - String get saveImageLabel => 'Guarda la imatge'; - - @override - String get saveVideoLabel => 'Guarda el vĆ­deo'; - - @override - String get uploadErrorLabel => 'ERROR DE TRANSFERƈNCIA'; - - @override - String get giphyLabel => 'Giphy'; - - @override - String get shuffleLabel => 'Remena'; - - @override - String get sendLabel => 'Envia'; - - @override - String get withText => 'amb'; - - @override - String get inText => 'a'; - - @override - String get youText => 'VostĆØ'; - - @override - String galleryPaginationText({ - required int currentPage, - required int totalPages, - }) => - '${currentPage + 1} de $totalPages'; - - @override - String get fileText => 'Fitxer'; - - @override - String get replyToMessageLabel => 'Respondre al missatge'; - - @override - String attachmentLimitExceedError(int limit) => - 'No Ć©s possible afegir mĆ©s de $limit fitxers adjunts'; - - @override - String get viewLibrary => 'Veure llibreria'; - - @override - String get slowModeOnLabel => 'Mode lent activat'; - - @override - String get downloadLabel => 'Descarrega'; - - @override - String toggleMuteUnmuteUserText({required bool isMuted}) { - if (isMuted) { - return "Activa so de l'usuari"; - } else { - return "Silencia l'usuari"; - } - } - - @override - String toggleMuteUnmuteGroupQuestion({required bool isMuted}) { - if (isMuted) { - return "EstĆ s segur que vols activar el so d'aquest grup?"; - } else { - return 'EstĆ s segur que vols silenciar aquest grup?'; - } - } - - @override - String toggleMuteUnmuteUserQuestion({required bool isMuted}) { - if (isMuted) { - return "EstĆ s segur que vols activar el so d'aquest usuari?"; - } else { - return 'EstĆ s segur que vols silenciar aquest usuari?'; - } - } - - @override - String toggleMuteUnmuteAction({required bool isMuted}) { - if (isMuted) { - return 'ACTIVA SO'; - } else { - return 'SILENCIA'; - } - } - - @override - String toggleMuteUnmuteGroupText({required bool isMuted}) { - if (isMuted) { - return 'Activa so del grup'; - } else { - return 'Silencia el grup'; - } - } - - @override - String get linkDisabledDetails => - 'No es permet enviar enllaƧos a aquesta conversa'; - - @override - String get linkDisabledError => 'Els enllaƧos estan deshabilitats'; - - @override - String unreadMessagesSeparatorText() => 'Missatges nous'; - - @override - String get enableFileAccessMessage => "Habilita l'accĆ©s als fitxers" - '\nper poder compartir-los amb amics'; - - @override - String get allowFileAccessMessage => "Permet l'accĆ©s als fitxers"; - - @override - String get markAsUnreadLabel => 'Marcar com no llegit'; - - @override - String unreadCountIndicatorLabel({required int unreadCount}) { - return '$unreadCount sense llegir'; - } - - @override - String get markUnreadError => - 'Error en marcar el missatge com a no llegit. No es poden marcar' - ' missatges no llegits mĆ©s antics que els 100 missatges mĆ©s recents del' - ' canal.'; - - @override - String createPollLabel({bool isNew = false}) { - if (isNew) return 'Crear una enquesta nova'; - return 'Crea enquesta'; - } - - @override - String get questionsLabel => 'Preguntes'; - - @override - String get askAQuestionLabel => 'Fes una pregunta'; - - @override - String? pollQuestionValidationError(int length, Range range) { - final (:min, :max) = range; - - // Check if the question is too short. - if (min != null && length < min) { - return 'La pregunta ha de tenir com a mĆ­nim $min carĆ cters'; - } - - // Check if the question is too long. - if (max != null && length > max) { - return 'La pregunta ha de tenir com a mĆ xim $max carĆ cters'; - } - - return null; - } - - @override - String optionLabel({bool isPlural = false}) { - if (isPlural) return 'Opcions'; - return 'Opció'; - } - - @override - String get pollOptionEmptyError => "L'opció no pot estar buida"; - - @override - String get pollOptionDuplicateError => 'Això ja Ć©s una opció'; - - @override - String get addAnOptionLabel => 'Afegeix una opció'; - - @override - String get multipleAnswersLabel => 'Respostes mĆŗltiples'; - - @override - String get maximumVotesPerPersonLabel => 'MĆ xim de vots per persona'; - - @override - String? maxVotesPerPersonValidationError(int votes, Range range) { - final (:min, :max) = range; - - if (min != null && votes < min) { - return 'El recompte de vots ha de ser com a mĆ­nim de $min'; - } - - if (max != null && votes > max) { - return 'El recompte de vots ha de ser com a mĆ xim de $max'; - } - - return null; - } - - @override - String get anonymousPollLabel => 'Votació anònima'; - - @override - String get pollOptionsLabel => 'Opcions de votació'; - - @override - String get suggestAnOptionLabel => 'Suggerir una opció'; - - @override - String get enterANewOptionLabel => 'IntroduĆÆu una nova opció'; - - @override - String get addACommentLabel => 'Afegir un comentari'; - - @override - String get pollCommentsLabel => 'Comentaris de la votació'; - - @override - String get updateYourCommentLabel => 'Actualitzar el vostre comentari'; - - @override - String get enterYourCommentLabel => 'IntroduĆÆu el vostre comentari'; - - @override - String get createLabel => 'Crear'; - - @override - String pollVotingModeLabel(PollVotingMode votingMode) { - return votingMode.when( - disabled: () => 'Votació finalitzada', - unique: () => 'Seleccionar un', - limited: (count) => 'Seleccionar fins a $count', - all: () => 'Seleccionar un o mĆ©s', - ); - } - - @override - String seeAllOptionsLabel({int? count}) { - if (count == null) return 'Veure totes les opcions'; - return 'Veure totes les $count opcions'; - } - - @override - String get viewCommentsLabel => 'Veure comentaris'; - - @override - String get viewResultsLabel => 'Veure resultats'; - - @override - String get endVoteLabel => 'Finalitzar votació'; - - @override - String get pollResultsLabel => 'Resultats de la votació'; - - @override - String showAllVotesLabel({int? count}) { - if (count == null) return 'Mostrar tots els vots'; - return 'Mostrar tots els $count vots'; - } - - @override - String voteCountLabel({int? count}) => switch (count) { - null || < 1 => '0 vots', - 1 => '1 vot', - _ => '$count vots', - }; - - @override - String get noPollVotesLabel => 'No hi ha vots en aquest moment'; - - @override - String get loadingPollVotesError => 'Error en carregar els vots'; - - @override - String get repliedToLabel => 'resposta a:'; - - @override - String newThreadsLabel({required int count}) { - if (count == 1) return '1 fil nou'; - return '$count fils nous'; - } - - @override - String get slideToCancelLabel => 'Llisca per cancelĀ·lar'; - - @override - String get holdToRecordLabel => - 'MantĆ©n premut per gravar, deixa anar per enviar'; -} diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_de.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_de.dart deleted file mode 100644 index 02f6fe9e9f..0000000000 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_de.dart +++ /dev/null @@ -1,616 +0,0 @@ -part of 'stream_chat_localizations.dart'; - -/// The translations for German (`de`). -class StreamChatLocalizationsDe extends GlobalStreamChatLocalizations { - /// Create an instance of the translation bundle for German. - const StreamChatLocalizationsDe({super.localeName = 'de'}); - - @override - String get launchUrlError => 'Die Url kann nicht geƶffnet werden'; - - @override - String get loadingUsersError => 'Fehler beim Laden von Usern'; - - @override - String get noUsersLabel => 'Derzeit gibt es keine User'; - - @override - String get noPhotoOrVideoLabel => 'Es gibt kein Foto oder Video'; - - @override - String get retryLabel => 'Erneut versuchen'; - - @override - String get userLastOnlineText => 'Zuletzt online'; - - @override - String get userOnlineText => 'Online'; - - @override - String userTypingText(Iterable users) { - if (users.isEmpty) return ''; - final first = users.first; - if (users.length == 1) { - return '${first.name} tippt'; - } - return '${first.name} und ${users.length - 1} weitere tippen'; - } - - @override - String get threadReplyLabel => 'Thread-Antwort'; - - @override - String get onlyVisibleToYouText => 'Nur für dich sichtbar'; - - @override - String threadReplyCountText(int count) => '$count Thread-Antworten'; - - @override - String attachmentsUploadProgressText({ - required int remaining, - required int total, - }) => - 'Hochladen $remaining/$total ...'; - - @override - String pinnedByUserText({ - required User pinnedBy, - required User currentUser, - }) { - final pinnedByCurrentUser = currentUser.id == pinnedBy.id; - if (pinnedByCurrentUser) return 'Angeheftet von Dir'; - return 'Angeheftet von ${pinnedBy.name}'; - } - - @override - String get emptyMessagesText => 'Derzeit sind keine Nachrichten vorhanden'; - - @override - String get genericErrorText => 'Etwas ist schief gelaufen'; - - @override - String get loadingMessagesError => 'Fehler beim Laden der Nachrichten'; - - @override - String resultCountText(int count) => '$count Ergebnisse'; - - @override - String get messageDeletedText => 'Diese Nachricht ist gelƶscht.'; - - @override - String get messageDeletedLabel => 'Nachricht gelƶscht'; - - @override - String get editedMessageLabel => 'Bearbeitet'; - - @override - String get messageReactionsLabel => 'Nachricht-Reaktionen'; - - @override - String get emptyChatMessagesText => 'Noch keine Nachrichten hier...'; - - @override - String threadSeparatorText(int replyCount) { - if (replyCount == 1) return '1 Antwort'; - return '$replyCount Antworten'; - } - - @override - String get connectedLabel => 'Verbunden'; - - @override - String get disconnectedLabel => 'Getrennt'; - - @override - String get reconnectingLabel => 'Verbindung wird wiederhergestellt...'; - - @override - String get alsoSendAsDirectMessageLabel => 'Auch als Direktnachricht senden'; - - @override - String get addACommentOrSendLabel => 'Kommentar hinzufügen oder senden'; - - @override - String get searchGifLabel => 'GIFs suchen'; - - @override - String get writeAMessageLabel => 'Nachricht schreiben'; - - @override - String get instantCommandsLabel => 'Sofort-Befehle'; - - @override - String fileTooLargeAfterCompressionError(double limitInMB) => - 'Die Datei ist zu groß zum Hochladen. ' - 'Die maximale Dateigröße betrƤgt $limitInMB MB. ' - 'Wir haben versucht, sie zu komprimieren, aber das war nicht genug.'; - - @override - String fileTooLargeError(double limitInMB) => - 'Die Datei ist zu groß zum Hochladen. ' - 'Die Dateigröße ist begrenzt auf $limitInMB MB.'; - - @override - String get addAFileLabel => 'Datei hinzufügen'; - - @override - String get photoFromCameraLabel => 'Foto von der Kamera'; - - @override - String get uploadAFileLabel => 'Datei hochladen'; - - @override - String get uploadAPhotoLabel => 'Foto hochladen'; - - @override - String get uploadAVideoLabel => 'Video hochladen'; - - @override - String get videoFromCameraLabel => 'Video von der Kamera'; - - @override - String get okLabel => 'OK'; - - @override - String get somethingWentWrongError => 'Etwas ist schief gelaufen'; - - @override - String get addMoreFilesLabel => 'Weitere Dateien hinzufügen'; - - @override - String get enablePhotoAndVideoAccessMessage => - 'Bitte aktivieren Sie den Zugriff auf Ihre Fotos' - '\nund Videos, damit Sie sie mit Freunden teilen kƶnnen.'; - - @override - String get allowGalleryAccessMessage => 'Zugang zu Ihrer Galerie gewƤhren'; - - @override - String get flagMessageLabel => 'Nachricht melden'; - - @override - String get flagMessageQuestion => - 'Mƶchten Sie eine Kopie dieser Nachricht an einen' - '\nModerator für weitere Untersuchungen senden?'; - - @override - String get flagLabel => 'MELDEN'; - - @override - String get cancelLabel => 'ABBRECHEN'; - - @override - String get flagMessageSuccessfulLabel => 'Nachricht gemeldet'; - - @override - String get flagMessageSuccessfulText => - 'Die Nachricht wurde an einen Moderator weitergeleitet.'; - - @override - String get deleteLabel => 'LƖSCHEN'; - - @override - String get deleteMessageLabel => 'Nachricht lƶschen'; - - @override - String get deleteMessageQuestion => - 'Sind Sie sicher, dass Sie diese Nachricht endgültig lƶschen wollen?'; - - @override - String get operationCouldNotBeCompletedText => - 'Die Operation konnte nicht abgeschlossen werden.'; - - @override - String get replyLabel => 'Antwort'; - - @override - String togglePinUnpinText({required bool pinned}) { - if (pinned) return 'Aus der Unterhaltung lƶsen'; - return 'Anheften an die Unterhaltung'; - } - - @override - String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed}) { - if (isDeleteFailed) return 'Lƶschen der Nachricht wiederholen'; - return 'Nachricht lƶschen'; - } - - @override - String get copyMessageLabel => 'Nachricht kopieren'; - - @override - String get editMessageLabel => 'Nachricht bearbeiten'; - - @override - String toggleResendOrResendEditedMessage({required bool isUpdateFailed}) { - if (isUpdateFailed) return 'Bearbeitete Nachricht erneut senden'; - return 'Erneut senden'; - } - - @override - String get photosLabel => 'Fotos'; - - String _getDay(DateTime dateTime) { - final now = DateTime.now(); - final today = DateTime(now.year, now.month, now.day); - final yesterday = DateTime(now.year, now.month, now.day - 1); - - final date = DateTime(dateTime.year, dateTime.month, dateTime.day); - - if (date == today) { - return 'Heute'; - } else if (date == yesterday) { - return 'Gestern'; - } else { - return 'am ${Jiffy.parseFromDateTime(date).MMMd}'; - } - } - - @override - String sentAtText({required DateTime date, required DateTime time}) { - final atTime = Jiffy.parseFromDateTime(time.toLocal()); - return 'Gesendet ${_getDay(date)} am ${atTime.jm}'; - } - - @override - String get todayLabel => 'Heute'; - - @override - String get yesterdayLabel => 'Gestern'; - - @override - String get channelIsMutedText => 'Kanal ist stummgeschaltet'; - - @override - String get noTitleText => 'Kein Titel'; - - @override - String get letsStartChattingLabel => 'Lass uns anfangen zu chatten!'; - - @override - String get sendingFirstMessageLabel => 'Wie wƤre es, wenn Sie Ihre erste ' - 'Nachricht an einen Freund senden würden?'; - - @override - String get startAChatLabel => 'Chat beginnen'; - - @override - String get loadingChannelsError => 'Fehler beim Laden der KanƤle'; - - @override - String get deleteConversationLabel => 'Unterhaltung lƶschen'; - - @override - String get deleteConversationQuestion => - 'Sind Sie sicher, dass Sie diese Unterhaltung lƶschen wollen?'; - - @override - String get streamChatLabel => 'Stream Chat'; - - @override - String get searchingForNetworkText => 'Netzwerk wird gesucht'; - - @override - String get offlineLabel => 'Offline...'; - - @override - String get tryAgainLabel => 'Erneut versuchen'; - - @override - String membersCountText(int count) { - if (count == 1) return '1 Mitglied'; - return '$count Mitglieder'; - } - - @override - String watchersCountText(int count) { - return '$count Online'; - } - - @override - String get viewInfoLabel => 'Infos anzeigen'; - - @override - String get leaveGroupLabel => 'Gruppe verlassen'; - - @override - String get leaveLabel => 'VERLASSEN'; - - @override - String get leaveConversationLabel => 'Unterhaltung verlassen'; - - @override - String get leaveConversationQuestion => - 'Sind Sie sicher, dass Sie diese Unterhaltung verlassen wollen?'; - - @override - String get showInChatLabel => 'Im Chat anzeigen'; - - @override - String get saveImageLabel => 'Bild speichern'; - - @override - String get saveVideoLabel => 'Video speichern'; - - @override - String get uploadErrorLabel => 'UPLOAD-FEHLER'; - - @override - String get giphyLabel => 'Giphy'; - - @override - String get shuffleLabel => 'Mischen'; - - @override - String get sendLabel => 'Senden'; - - @override - String get withText => 'mit'; - - @override - String get inText => 'in'; - - @override - String get youText => 'Du'; - - @override - String galleryPaginationText({ - required int currentPage, - required int totalPages, - }) => - '${currentPage + 1} von $totalPages'; - - @override - String get fileText => 'Datei'; - - @override - String get replyToMessageLabel => 'Auf Nachricht antworten'; - - @override - String attachmentLimitExceedError(int limit) => - 'Dateigröße überschritten, Grenze: $limit'; - - @override - String get slowModeOnLabel => 'Langsamer Modus: EIN'; - - @override - String get linkDisabledDetails => - 'Das Senden von Links ist in dieser Konversation nicht erlaubt.'; - - @override - String get linkDisabledError => 'Verknüpfungen sind deaktiviert'; - - @override - String get sendMessagePermissionError => - 'Sie sind nicht berechtigt Nachrichten zu senden'; - - @override - String get couldNotReadBytesFromFileError => - 'Kan bytes niet uit bestand lezen.'; - - @override - String get downloadLabel => 'Downloaden'; - - @override - String toggleMuteUnmuteAction({required bool isMuted}) { - if (isMuted) { - return 'UNMUTE'; - } else { - return 'STOM'; - } - } - - @override - String toggleMuteUnmuteGroupQuestion({required bool isMuted}) { - if (isMuted) { - return 'Weet je zeker dat je het dempen van deze groep wilt opheffen?'; - } else { - return 'Weet je zeker dat je deze groep wilt dempen?'; - } - } - - @override - String toggleMuteUnmuteGroupText({required bool isMuted}) { - if (isMuted) { - return 'Dempen groep opheffen'; - } else { - return 'Groep dempen'; - } - } - - @override - String toggleMuteUnmuteUserQuestion({required bool isMuted}) { - if (isMuted) { - return '''Weet je zeker dat je het dempen van deze gebruiker wilt opheffen?'''; - } else { - return 'Weet u zeker dat u deze gebruiker wilt dempen?'; - } - } - - @override - String toggleMuteUnmuteUserText({required bool isMuted}) { - return 'Gebruiker dempen'; - } - - @override - String get viewLibrary => 'Bibliothek ƶffnen'; - - @override - String unreadMessagesSeparatorText() => 'Neue Nachrichten'; - - @override - String get enableFileAccessMessage => - 'Bitte aktivieren Sie den Zugriff auf Dateien,' - '\ndamit Sie sie mit Freunden teilen kƶnnen.'; - - @override - String get allowFileAccessMessage => 'Zugriff auf Dateien zulassen'; - - @override - String get markAsUnreadLabel => 'Als ungelesen markieren'; - - @override - String unreadCountIndicatorLabel({required int unreadCount}) { - return '$unreadCount ungelesen'; - } - - @override - String get markUnreadError => - 'Fehler beim Markieren der Nachricht als ungelesen. Kann keine Ƥlteren' - ' ungelesenen Nachrichten markieren als die neuesten 100' - ' Kanalnachrichten.'; - - @override - String createPollLabel({bool isNew = false}) { - if (isNew) return 'Erstellen einer neuen Umfrage'; - return 'Umfrage erstellen'; - } - - @override - String get questionsLabel => 'Fragen'; - - @override - String get askAQuestionLabel => 'Stellen Sie eine Frage'; - - @override - String? pollQuestionValidationError(int length, Range range) { - final (:min, :max) = range; - - // Check if the question is too short. - if (min != null && length < min) { - return 'Die Frage muss mindestens $min Zeichen lang sein'; - } - - // Check if the question is too long. - if (max != null && length > max) { - return 'Die Frage darf hƶchstens $max Zeichen lang sein'; - } - - return null; - } - - @override - String optionLabel({bool isPlural = false}) { - if (isPlural) return 'Optionen'; - return 'Option'; - } - - @override - String get pollOptionEmptyError => 'Option darf nicht leer sein'; - - @override - String get pollOptionDuplicateError => 'Dies ist bereits eine Option'; - - @override - String get addAnOptionLabel => 'Option hinzufügen'; - - @override - String get multipleAnswersLabel => 'Mehrere Antworten'; - - @override - String get maximumVotesPerPersonLabel => 'Maximale Stimmen pro Person'; - - @override - String? maxVotesPerPersonValidationError(int votes, Range range) { - final (:min, :max) = range; - - if (min != null && votes < min) { - return 'Die StimmenauszƤhlung muss mindestens $min betragen'; - } - - if (max != null && votes > max) { - return 'Die StimmenauszƤhlung darf hƶchstens $max betragen'; - } - - return null; - } - - @override - String get anonymousPollLabel => 'Anonyme Umfrage'; - - @override - String get pollOptionsLabel => 'Umfrage-Optionen'; - - @override - String get suggestAnOptionLabel => 'Option vorschlagen'; - - @override - String get enterANewOptionLabel => 'Neue Option eingeben'; - - @override - String get addACommentLabel => 'Kommentar hinzufügen'; - - @override - String get pollCommentsLabel => 'Umfrage-Kommentare'; - - @override - String get updateYourCommentLabel => 'Kommentar aktualisieren'; - - @override - String get enterYourCommentLabel => 'Kommentar eingeben'; - - @override - String get createLabel => 'Erstellen'; - - @override - String pollVotingModeLabel(PollVotingMode votingMode) { - return votingMode.when( - disabled: () => 'Abstimmung beendet', - unique: () => 'Eine auswƤhlen', - limited: (count) => 'Bis zu $count auswƤhlen', - all: () => 'Eine oder mehrere auswƤhlen', - ); - } - - @override - String seeAllOptionsLabel({int? count}) { - if (count == null) return 'Alle Optionen anzeigen'; - return 'Alle $count Optionen anzeigen'; - } - - @override - String get viewCommentsLabel => 'Kommentare anzeigen'; - - @override - String get viewResultsLabel => 'Ergebnisse anzeigen'; - - @override - String get endVoteLabel => 'Abstimmung beenden'; - - @override - String get pollResultsLabel => 'Umfrage-Ergebnisse'; - - @override - String showAllVotesLabel({int? count}) { - if (count == null) return 'Alle Stimmen anzeigen'; - return 'Alle $count Stimmen anzeigen'; - } - - @override - String voteCountLabel({int? count}) => switch (count) { - null || < 1 => '0 Stimmen', - 1 => '1 Stimme', - _ => '$count Stimmen', - }; - - @override - String get noPollVotesLabel => 'Derzeit keine Umfrage-Stimmen'; - - @override - String get loadingPollVotesError => 'Fehler beim Laden der Umfrage-Stimmen'; - - @override - String get repliedToLabel => 'antwortete:'; - - @override - String newThreadsLabel({required int count}) { - if (count == 1) return '1 neuer Thread'; - return '$count neue Threads'; - } - - @override - String get slideToCancelLabel => 'Zum Abbrechen schieben'; - - @override - String get holdToRecordLabel => 'Zum Aufnehmen halten, zum Senden loslassen'; -} diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_en.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_en.dart deleted file mode 100644 index ee1d9c91bc..0000000000 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_en.dart +++ /dev/null @@ -1,618 +0,0 @@ -part of 'stream_chat_localizations.dart'; - -/// The translations for English (`en`). -class StreamChatLocalizationsEn extends GlobalStreamChatLocalizations { - /// Create an instance of the translation bundle for English. - const StreamChatLocalizationsEn({super.localeName = 'en'}); - - @override - String get launchUrlError => 'Cannot launch the url'; - - @override - String get loadingUsersError => 'Error loading users'; - - @override - String get noUsersLabel => 'There are no users currently'; - - @override - String get noPhotoOrVideoLabel => 'There is no photo or video'; - - @override - String get retryLabel => 'Retry'; - - @override - String get userLastOnlineText => 'Last online'; - - @override - String get userOnlineText => 'Online'; - - @override - String userTypingText(Iterable users) { - if (users.isEmpty) return ''; - final first = users.first; - if (users.length == 1) { - return '${first.name} is typing'; - } - return '${first.name} and ${users.length - 1} more are typing'; - } - - @override - String get threadReplyLabel => 'Thread Reply'; - - @override - String get onlyVisibleToYouText => 'Only visible to you'; - - @override - String threadReplyCountText(int count) => '$count Thread Replies'; - - @override - String attachmentsUploadProgressText({ - required int remaining, - required int total, - }) => - 'Uploading $remaining/$total ...'; - - @override - String pinnedByUserText({ - required User pinnedBy, - required User currentUser, - }) { - final pinnedByCurrentUser = currentUser.id == pinnedBy.id; - if (pinnedByCurrentUser) return 'Pinned by You'; - return 'Pinned by ${pinnedBy.name}'; - } - - @override - String get sendMessagePermissionError => - "You don't have permission to send messages"; - - @override - String get emptyMessagesText => 'There are no messages currently'; - - @override - String get genericErrorText => 'Something went wrong'; - - @override - String get loadingMessagesError => 'Error loading messages'; - - @override - String resultCountText(int count) => '$count results'; - - @override - String get messageDeletedText => 'This message is deleted.'; - - @override - String get messageDeletedLabel => 'Message deleted'; - - @override - String get editedMessageLabel => 'Edited'; - - @override - String get messageReactionsLabel => 'Message Reactions'; - - @override - String get emptyChatMessagesText => 'No chats here yet...'; - - @override - String threadSeparatorText(int replyCount) { - if (replyCount == 1) return '1 Reply'; - return '$replyCount Replies'; - } - - @override - String get connectedLabel => 'Connected'; - - @override - String get disconnectedLabel => 'Disconnected'; - - @override - String get reconnectingLabel => 'Reconnecting...'; - - @override - String get alsoSendAsDirectMessageLabel => 'Also send as direct message'; - - @override - String get addACommentOrSendLabel => 'Add a comment or send'; - - @override - String get searchGifLabel => 'Search GIFs'; - - @override - String get writeAMessageLabel => 'Write a message'; - - @override - String get instantCommandsLabel => 'Instant Commands'; - - @override - String fileTooLargeAfterCompressionError(double limitInMB) => - 'The file is too large to upload. ' - 'The file size limit is $limitInMB MB. ' - 'We tried compressing it, but it was not enough.'; - - @override - String fileTooLargeError(double limitInMB) => - 'The file is too large to upload. The file size limit is $limitInMB MB.'; - - @override - String get couldNotReadBytesFromFileError => - 'Could not read bytes from file.'; - - @override - String get addAFileLabel => 'Add a file'; - - @override - String get photoFromCameraLabel => 'Photo from camera'; - - @override - String get uploadAFileLabel => 'Upload a file'; - - @override - String get uploadAPhotoLabel => 'Upload a photo'; - - @override - String get uploadAVideoLabel => 'Upload a video'; - - @override - String get videoFromCameraLabel => 'Video from camera'; - - @override - String get okLabel => 'OK'; - - @override - String get somethingWentWrongError => 'Something went wrong'; - - @override - String get addMoreFilesLabel => 'Add more files'; - - @override - String get enablePhotoAndVideoAccessMessage => - 'Please enable access to your photos' - '\nand videos so you can share them with friends.'; - - @override - String get allowGalleryAccessMessage => 'Allow access to your gallery'; - - @override - String get flagMessageLabel => 'Flag Message'; - - @override - String get flagMessageQuestion => - 'Do you want to send a copy of this message to a' - '\nmoderator for further investigation?'; - - @override - String get flagLabel => 'FLAG'; - - @override - String get cancelLabel => 'CANCEL'; - - @override - String get flagMessageSuccessfulLabel => 'Message flagged'; - - @override - String get flagMessageSuccessfulText => - 'The message has been reported to a moderator.'; - - @override - String get deleteLabel => 'DELETE'; - - @override - String get deleteMessageLabel => 'Delete Message'; - - @override - String get deleteMessageQuestion => - 'Are you sure you want to permanently delete this\nmessage?'; - - @override - String get operationCouldNotBeCompletedText => - "The operation couldn't be completed."; - - @override - String get replyLabel => 'Reply'; - - @override - String togglePinUnpinText({required bool pinned}) { - if (pinned) return 'Unpin from Conversation'; - return 'Pin to Conversation'; - } - - @override - String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed}) { - if (isDeleteFailed) return 'Retry Deleting Message'; - return 'Delete Message'; - } - - @override - String get copyMessageLabel => 'Copy Message'; - - @override - String get editMessageLabel => 'Edit Message'; - - @override - String toggleResendOrResendEditedMessage({required bool isUpdateFailed}) { - if (isUpdateFailed) return 'Resend Edited Message'; - return 'Resend'; - } - - @override - String get photosLabel => 'Photos'; - - String _getDay(DateTime dateTime) { - final now = DateTime.now(); - final today = DateTime(now.year, now.month, now.day); - final yesterday = DateTime(now.year, now.month, now.day - 1); - - final date = DateTime(dateTime.year, dateTime.month, dateTime.day); - - if (date == today) { - return 'today'; - } else if (date == yesterday) { - return 'yesterday'; - } else { - return 'on ${Jiffy.parseFromDateTime(date).MMMd}'; - } - } - - @override - String sentAtText({required DateTime date, required DateTime time}) { - final atTime = Jiffy.parseFromDateTime(time.toLocal()); - return 'Sent ${_getDay(date)} at ${atTime.jm}'; - } - - @override - String get todayLabel => 'Today'; - - @override - String get yesterdayLabel => 'Yesterday'; - - @override - String get channelIsMutedText => 'Channel is muted'; - - @override - String get noTitleText => 'No title'; - - @override - String get letsStartChattingLabel => 'Let’s start chatting!'; - - @override - String get sendingFirstMessageLabel => - 'How about sending your first message to a friend?'; - - @override - String get startAChatLabel => 'Start a chat'; - - @override - String get loadingChannelsError => 'Error loading channels'; - - @override - String get deleteConversationLabel => 'Delete Conversation'; - - @override - String get deleteConversationQuestion => - 'Are you sure you want to delete this conversation?'; - - @override - String get streamChatLabel => 'Stream Chat'; - - @override - String get searchingForNetworkText => 'Searching for Network'; - - @override - String get offlineLabel => 'Offline...'; - - @override - String get tryAgainLabel => 'Try Again'; - - @override - String membersCountText(int count) { - if (count == 1) return '1 Member'; - return '$count Members'; - } - - @override - String watchersCountText(int count) { - if (count == 1) return '1 Online'; - return '$count Online'; - } - - @override - String get viewInfoLabel => 'View Info'; - - @override - String get leaveGroupLabel => 'Leave Group'; - - @override - String get leaveLabel => 'LEAVE'; - - @override - String get leaveConversationLabel => 'Leave conversation'; - - @override - String get leaveConversationQuestion => - 'Are you sure you want to leave this conversation?'; - - @override - String get showInChatLabel => 'Show in Chat'; - - @override - String get saveImageLabel => 'Save Image'; - - @override - String get saveVideoLabel => 'Save Video'; - - @override - String get uploadErrorLabel => 'UPLOAD ERROR'; - - @override - String get giphyLabel => 'Giphy'; - - @override - String get shuffleLabel => 'Shuffle'; - - @override - String get sendLabel => 'Send'; - - @override - String get withText => 'with'; - - @override - String get inText => 'in'; - - @override - String get youText => 'You'; - - @override - String galleryPaginationText({ - required int currentPage, - required int totalPages, - }) => - '${currentPage + 1} of $totalPages'; - - @override - String get fileText => 'File'; - - @override - String get replyToMessageLabel => 'Reply to Message'; - - @override - String attachmentLimitExceedError(int limit) => - 'Attachment limit exceeded, limit: $limit'; - - @override - String get slowModeOnLabel => 'Slow mode ON'; - - @override - String get downloadLabel => 'Download'; - - @override - String toggleMuteUnmuteUserText({required bool isMuted}) { - if (isMuted) { - return 'Unmute User'; - } else { - return 'Mute User'; - } - } - - @override - String toggleMuteUnmuteGroupQuestion({required bool isMuted}) { - if (isMuted) { - return 'Are you sure you want to unmute this group?'; - } else { - return 'Are you sure you want to mute this group?'; - } - } - - @override - String toggleMuteUnmuteUserQuestion({required bool isMuted}) { - if (isMuted) { - return 'Are you sure you want to unmute this user?'; - } else { - return 'Are you sure you want to mute this user?'; - } - } - - @override - String toggleMuteUnmuteAction({required bool isMuted}) { - if (isMuted) { - return 'UNMUTE'; - } else { - return 'MUTE'; - } - } - - @override - String toggleMuteUnmuteGroupText({required bool isMuted}) { - if (isMuted) { - return 'Unmute Group'; - } else { - return 'Mute Group'; - } - } - - @override - String get linkDisabledDetails => - 'Sending links is not allowed in this conversation.'; - - @override - String get linkDisabledError => 'Links are disabled'; - - @override - String get viewLibrary => 'View library'; - - @override - String unreadMessagesSeparatorText() => 'New messages'; - - @override - String get enableFileAccessMessage => 'Please enable access to files' - '\nso you can share them with friends.'; - - @override - String get allowFileAccessMessage => 'Allow access to files'; - - @override - String get markAsUnreadLabel => 'Mark as Unread'; - - @override - String unreadCountIndicatorLabel({required int unreadCount}) { - return '$unreadCount unread'; - } - - @override - String get markUnreadError => - 'Error marking message unread. Cannot mark unread messages older' - ' than the newest 100 channel messages.'; - - @override - String createPollLabel({bool isNew = false}) { - if (isNew) return 'Create a new poll'; - return 'Create Poll'; - } - - @override - String get questionsLabel => 'Questions'; - - @override - String get askAQuestionLabel => 'Ask a question'; - - @override - String? pollQuestionValidationError(int length, Range range) { - final (:min, :max) = range; - - // Check if the question is too short. - if (min != null && length < min) { - return 'Question must be at least $min characters long'; - } - - // Check if the question is too long. - if (max != null && length > max) { - return 'Question must be at most $max characters long'; - } - - return null; - } - - @override - String optionLabel({bool isPlural = false}) { - if (isPlural) return 'Options'; - return 'Option'; - } - - @override - String get pollOptionEmptyError => 'Option cannot be empty'; - - @override - String get pollOptionDuplicateError => 'This is already an option'; - - @override - String get addAnOptionLabel => 'Add an option'; - - @override - String get multipleAnswersLabel => 'Multiple answers'; - - @override - String get maximumVotesPerPersonLabel => 'Maximum votes per person'; - - @override - String? maxVotesPerPersonValidationError(int votes, Range range) { - final (:min, :max) = range; - - if (min != null && votes < min) { - return 'Vote count must be at least $min'; - } - - if (max != null && votes > max) { - return 'Vote count must be at most $max'; - } - - return null; - } - - @override - String get anonymousPollLabel => 'Anonymous poll'; - - @override - String get pollOptionsLabel => 'Poll Options'; - - @override - String get suggestAnOptionLabel => 'Suggest an option'; - - @override - String get enterANewOptionLabel => 'Enter a new option'; - - @override - String get addACommentLabel => 'Add a comment'; - - @override - String get pollCommentsLabel => 'Poll Comments'; - - @override - String get updateYourCommentLabel => 'Update your comment'; - - @override - String get enterYourCommentLabel => 'Enter your comment'; - - @override - String get createLabel => 'Create'; - - @override - String pollVotingModeLabel(PollVotingMode votingMode) { - return votingMode.when( - disabled: () => 'Vote ended', - unique: () => 'Select one', - limited: (count) => 'Select up to $count', - all: () => 'Select one or more', - ); - } - - @override - String seeAllOptionsLabel({int? count}) { - if (count == null) return 'See all options'; - return 'See all $count options'; - } - - @override - String get viewCommentsLabel => 'View Comments'; - - @override - String get viewResultsLabel => 'View Results'; - - @override - String get endVoteLabel => 'End Vote'; - - @override - String get pollResultsLabel => 'Poll Results'; - - @override - String showAllVotesLabel({int? count}) { - if (count == null) return 'Show all votes'; - return 'Show all $count votes'; - } - - @override - String voteCountLabel({int? count}) => switch (count) { - null || < 1 => '0 votes', - 1 => '1 vote', - _ => '$count votes', - }; - - @override - String get noPollVotesLabel => 'There are no poll votes currently'; - - @override - String get loadingPollVotesError => 'Error loading poll votes'; - - @override - String get repliedToLabel => 'replied to:'; - - @override - String newThreadsLabel({required int count}) { - if (count == 1) return '1 new thread'; - return '$count new threads'; - } - - @override - String get slideToCancelLabel => 'Slide to cancel'; - - @override - String get holdToRecordLabel => 'Hold to record, release to send.'; -} diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart deleted file mode 100644 index b03824ab90..0000000000 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart +++ /dev/null @@ -1,625 +0,0 @@ -part of 'stream_chat_localizations.dart'; - -/// The translations for Spanish (`es`). -class StreamChatLocalizationsEs extends GlobalStreamChatLocalizations { - /// Create an instance of the translation bundle for Spanish. - const StreamChatLocalizationsEs({super.localeName = 'es'}); - - @override - String get launchUrlError => 'No se pudo abrir la url'; - - @override - String get loadingUsersError => 'Error de carga del usuario'; - - @override - String get noUsersLabel => 'No hay usuarios actualmente'; - - @override - String get noPhotoOrVideoLabel => 'No hay fotos ni vĆ­deos'; - - @override - String get retryLabel => 'IntĆ©ntelo de nuevo'; - - @override - String get userLastOnlineText => 'Última vez en lĆ­nea'; - - @override - String get userOnlineText => 'En lĆ­nea'; - - @override - String userTypingText(Iterable users) { - if (users.isEmpty) return ''; - final first = users.first; - if (users.length == 1) { - return '${first.name} estĆ” escribiendo'; - } - return '${first.name} y ${users.length - 1} estĆ”n escribiendo'; - } - - @override - String get threadReplyLabel => 'Responder al hilo de discusión'; - - @override - String get onlyVisibleToYouText => 'Sólo visible para usted'; - - @override - String threadReplyCountText(int count) => - '$count respuestas al hilo de discusión'; - - @override - String attachmentsUploadProgressText({ - required int remaining, - required int total, - }) => - 'Transferencia en curso $remaining/$total ...'; - - @override - String pinnedByUserText({ - required User pinnedBy, - required User currentUser, - }) { - final pinnedByCurrentUser = currentUser.id == pinnedBy.id; - if (pinnedByCurrentUser) return 'Fijado por ti'; - return 'Fijado por ${pinnedBy.name}'; - } - - @override - String get sendMessagePermissionError => - 'No tienes permiso para enviar mensajes'; - - @override - String get emptyMessagesText => 'Actualmente no hay mensajes'; - - @override - String get genericErrorText => 'Hubo un problema'; - - @override - String get loadingMessagesError => - 'Hubo un error mientras se cargaba el mensaje'; - - @override - String resultCountText(int count) => '$count resultados'; - - @override - String get messageDeletedText => 'Este mensaje ha sido borrado.'; - - @override - String get messageDeletedLabel => 'Mensaje borrado'; - - @override - String get editedMessageLabel => 'Editado'; - - @override - String get messageReactionsLabel => 'Reacciones de los mensajes'; - - @override - String get emptyChatMessagesText => 'TodavĆ­a no hay charlas aquĆ­...'; - - @override - String threadSeparatorText(int replyCount) { - if (replyCount == 1) return '1 respuesta'; - return '$replyCount respuestas'; - } - - @override - String get connectedLabel => 'Conectado'; - - @override - String get disconnectedLabel => 'Desconectado'; - - @override - String get reconnectingLabel => 'Reconectando...'; - - @override - String get alsoSendAsDirectMessageLabel => - 'Enviar tambiĆ©n como mensaje directo'; - - @override - String get addACommentOrSendLabel => 'AƱadir un comentario o enviar'; - - @override - String get searchGifLabel => 'BĆŗsqueda de GIFs'; - - @override - String get writeAMessageLabel => 'Escribir un mensaje'; - - @override - String get instantCommandsLabel => 'Comandos instantĆ”neos'; - - @override - String fileTooLargeAfterCompressionError(double limitInMB) => - 'El archivo es demasiado grande para descargarlo. ' - 'El tamaƱo mĆ”ximo del archivo es de $limitInMB MB. ' - 'Intentamos comprimirlo, pero no fue suficiente.'; - - @override - String fileTooLargeError(double limitInMB) => - 'El archivo es demasiado grande para descargarlo. ' - 'El lĆ­mite de tamaƱo de los archivos es de $limitInMB MB.'; - - @override - String get couldNotReadBytesFromFileError => - 'No se pudieron leer los bytes del archivo.'; - - @override - String get addAFileLabel => 'AƱadir un archivo'; - - @override - String get photoFromCameraLabel => 'Foto de la cĆ”mara'; - - @override - String get uploadAFileLabel => 'Transferir un archivo'; - - @override - String get uploadAPhotoLabel => 'Subir una foto'; - - @override - String get uploadAVideoLabel => 'Subir una vĆ­deo'; - - @override - String get videoFromCameraLabel => 'VĆ­deo de la cĆ”mara'; - - @override - String get okLabel => 'Vale'; - - @override - String get somethingWentWrongError => 'Algo ha salido mal'; - - @override - String get addMoreFilesLabel => 'AƱadir mĆ”s archivos'; - - @override - String get enablePhotoAndVideoAccessMessage => - 'Por favor, permita el acceso a sus fotos' - '\ny vĆ­deos para que pueda compartirlos con sus amigos.'; - - @override - String get allowGalleryAccessMessage => 'Permitir el acceso a su galerĆ­a'; - - @override - String get flagMessageLabel => 'Reportar un mensaje'; - - @override - String get flagMessageQuestion => - 'ĀæQuiere enviar una copia de este mensaje a un' - '\nmoderador para una mayor investigación?'; - - @override - String get flagLabel => 'REPORTAR'; - - @override - String get cancelLabel => 'CANCELAR'; - - @override - String get flagMessageSuccessfulLabel => 'Mensaje reportado'; - - @override - String get flagMessageSuccessfulText => - 'Este mensaje ha sido reportado a un moderador.'; - - @override - String get deleteLabel => 'BORRAR'; - - @override - String get deleteMessageLabel => 'Borrar el mensaje'; - - @override - String get deleteMessageQuestion => - 'ĀæEstĆ”s seguro de que quieres borrar este\nmensaje de forma permanente?'; - - @override - String get operationCouldNotBeCompletedText => - 'La operación no pudo completarse.'; - - @override - String get replyLabel => 'Responder'; - - @override - String togglePinUnpinText({required bool pinned}) { - if (pinned) return 'Desfijar de la conversación'; - return 'Fijar a la conversación'; - } - - @override - String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed}) { - if (isDeleteFailed) return 'Reintentar borrar el mensaje'; - return 'Borrar el mensaje'; - } - - @override - String get copyMessageLabel => 'Copiar el mensaje'; - - @override - String get editMessageLabel => 'Editar el mensaje'; - - @override - String toggleResendOrResendEditedMessage({required bool isUpdateFailed}) { - if (isUpdateFailed) return 'Reenviar el mensaje modificado'; - return 'Reenviar'; - } - - @override - String get photosLabel => 'Fotos'; - - String _getDay(DateTime dateTime) { - final now = DateTime.now(); - final today = DateTime(now.year, now.month, now.day); - final yesterday = DateTime(now.year, now.month, now.day - 1); - - final date = DateTime(dateTime.year, dateTime.month, dateTime.day); - - if (date == today) { - return 'hoy'; - } else if (date == yesterday) { - return 'ayer'; - } else { - return 'el ${Jiffy.parseFromDateTime(date).MMMd}'; - } - } - - @override - String sentAtText({required DateTime date, required DateTime time}) { - final atTime = Jiffy.parseFromDateTime(time.toLocal()); - return 'Enviado el ${_getDay(date)} a las ${atTime.jm}'; - } - - @override - String get todayLabel => 'Hoy'; - - @override - String get yesterdayLabel => 'Ayer'; - - @override - String get channelIsMutedText => 'El canal estĆ” silenciado'; - - @override - String get noTitleText => 'Sin tĆ­tulo'; - - @override - String get letsStartChattingLabel => 'Ā”Empecemos a charlar!'; - - @override - String get sendingFirstMessageLabel => - 'ĀæQuĆ© le parece enviar su primer mensaje a un amigo?'; - - @override - String get startAChatLabel => 'Iniciar una conversación'; - - @override - String get loadingChannelsError => 'Error al cargar los canales'; - - @override - String get deleteConversationLabel => 'Borrar la conversación'; - - @override - String get deleteConversationQuestion => - 'ĀæEstĆ”s seguro de que quieres borrar esta conversación?'; - - @override - String get streamChatLabel => 'Stream Chat'; - - @override - String get searchingForNetworkText => 'Buscando red'; - - @override - String get offlineLabel => 'Sin conexión...'; - - @override - String get tryAgainLabel => 'IntĆ©ntelo de nuevo'; - - @override - String membersCountText(int count) { - if (count == 1) return '1 miembro'; - return '$count miembros'; - } - - @override - String watchersCountText(int count) { - if (count == 1) return '1 En lĆ­nea'; - return '$count En lĆ­nea'; - } - - @override - String get viewInfoLabel => 'Ver información'; - - @override - String get leaveGroupLabel => 'Salir del Grupo'; - - @override - String get leaveLabel => 'SALIR'; - - @override - String get leaveConversationLabel => 'Salir de la conversación'; - - @override - String get leaveConversationQuestion => - 'ĀæEstĆ”s seguro de que quiere salir de esta conversación?'; - - @override - String get showInChatLabel => 'Mostrar en el chat'; - - @override - String get saveImageLabel => 'Guardar la imagen'; - - @override - String get saveVideoLabel => 'Guardar el vĆ­deo'; - - @override - String get uploadErrorLabel => 'ERROR DE TRANSFERENCIA'; - - @override - String get giphyLabel => 'Giphy'; - - @override - String get shuffleLabel => 'Mezclar'; - - @override - String get sendLabel => 'Enviar'; - - @override - String get withText => 'con'; - - @override - String get inText => 'en'; - - @override - String get youText => 'Usted'; - - @override - String galleryPaginationText({ - required int currentPage, - required int totalPages, - }) => - '${currentPage + 1} de $totalPages'; - - @override - String get fileText => 'Archivo'; - - @override - String get replyToMessageLabel => 'Responder al Mensaje'; - - @override - String attachmentLimitExceedError(int limit) => ''' -No es posible aƱadir mĆ”s de $limit archivos adjuntos - '''; - - @override - String get viewLibrary => 'Ver LibrerĆ­a'; - - @override - String get slowModeOnLabel => 'Modo lento activado'; - - @override - String get downloadLabel => 'Descargar'; - - @override - String toggleMuteUnmuteUserText({required bool isMuted}) { - if (isMuted) { - return 'No silenciar usuario'; - } else { - return 'Silenciar usuario'; - } - } - - @override - String toggleMuteUnmuteGroupQuestion({required bool isMuted}) { - if (isMuted) { - return 'ĀæEstĆ”s seguro de que quieres activar el sonido de este grupo?'; - } else { - return 'ĀæEstĆ”s seguro de que quieres silenciar a este grupo?'; - } - } - - @override - String toggleMuteUnmuteUserQuestion({required bool isMuted}) { - if (isMuted) { - return 'ĀæEstĆ”s seguro de que quieres activar el sonido de este usuario?'; - } else { - return 'ĀæEstĆ”s seguro de que quieres silenciar a este usuario?'; - } - } - - @override - String toggleMuteUnmuteAction({required bool isMuted}) { - if (isMuted) { - return 'DESACTIVAR'; - } else { - return 'SILENCIO'; - } - } - - @override - String toggleMuteUnmuteGroupText({required bool isMuted}) { - if (isMuted) { - return 'Activar grupo'; - } else { - return 'Silenciar grupo'; - } - } - - @override - String get linkDisabledDetails => - 'No se permite enviar enlaces en esta conversación.'; - - @override - String get linkDisabledError => 'Los enlaces estĆ”n deshabilitados'; - - @override - String unreadMessagesSeparatorText() => 'Nuevos mensajes'; - - @override - String get enableFileAccessMessage => 'Habilite el acceso a los archivos' - '\npara poder compartirlos con amigos.'; - - @override - String get allowFileAccessMessage => 'Permitir el acceso a los archivos'; - - @override - String get markAsUnreadLabel => 'Marcar como no leĆ­do'; - - @override - String unreadCountIndicatorLabel({required int unreadCount}) { - return '$unreadCount no leĆ­dos'; - } - - @override - String get markUnreadError => - 'Error al marcar el mensaje como no leĆ­do. No se pueden marcar mensajes' - ' no leĆ­dos mĆ”s antiguos que los Ćŗltimos 100 mensajes del canal.'; - - @override - String createPollLabel({bool isNew = false}) { - if (isNew) return 'Crear un nuevo sondeo'; - return 'Crear sondeo'; - } - - @override - String get questionsLabel => 'Preguntas'; - - @override - String get askAQuestionLabel => 'Hacer una pregunta'; - - @override - String? pollQuestionValidationError(int length, Range range) { - final (:min, :max) = range; - - // Check if the question is too short. - if (min != null && length < min) { - return 'La pregunta debe tener al menos $min caracteres'; - } - - // Check if the question is too long. - if (max != null && length > max) { - return 'La pregunta no puede tener mĆ”s de $max caracteres'; - } - - return null; - } - - @override - String optionLabel({bool isPlural = false}) { - if (isPlural) return 'Opciones'; - return 'Opción'; - } - - @override - String get pollOptionEmptyError => 'Esta opción no puede estar vacĆ­a'; - - @override - String get pollOptionDuplicateError => 'Las opciones no pueden ser iguales'; - - @override - String get addAnOptionLabel => 'AƱadir una opción'; - - @override - String get multipleAnswersLabel => 'Respuestas mĆŗltiples'; - - @override - String get maximumVotesPerPersonLabel => 'MĆ”ximo de votos por persona'; - - @override - String? maxVotesPerPersonValidationError(int votes, Range range) { - final (:min, :max) = range; - - if (min != null && votes < min) { - return 'El recuento de votos debe ser al menos $min'; - } - - if (max != null && votes > max) { - return 'El recuento de votos no puede ser superior a $max'; - } - - return null; - } - - @override - String get anonymousPollLabel => 'Encuesta anónima'; - - @override - String get pollOptionsLabel => 'Opciones de la encuesta'; - - @override - String get suggestAnOptionLabel => 'Sugerir una opción'; - - @override - String get enterANewOptionLabel => 'Ingresar una nueva opción'; - - @override - String get addACommentLabel => 'Agregar un comentario'; - - @override - String get pollCommentsLabel => 'Comentarios de la encuesta'; - - @override - String get updateYourCommentLabel => 'Actualizar tu comentario'; - - @override - String get enterYourCommentLabel => 'Ingresar tu comentario'; - - @override - String get createLabel => 'Crear'; - - @override - String pollVotingModeLabel(PollVotingMode votingMode) { - return votingMode.when( - disabled: () => 'Votación finalizada', - unique: () => 'Seleccionar uno', - limited: (count) => 'Seleccionar hasta $count', - all: () => 'Seleccionar uno o mĆ”s', - ); - } - - @override - String seeAllOptionsLabel({int? count}) { - if (count == null) return 'Ver todas las opciones'; - return 'Ver todas las $count opciones'; - } - - @override - String get viewCommentsLabel => 'Ver comentarios'; - - @override - String get viewResultsLabel => 'Ver resultados'; - - @override - String get endVoteLabel => 'Finalizar votación'; - - @override - String get pollResultsLabel => 'Resultados de la encuesta'; - - @override - String showAllVotesLabel({int? count}) { - if (count == null) return 'Mostrar todos los votos'; - return 'Mostrar todos los $count votos'; - } - - @override - String voteCountLabel({int? count}) => switch (count) { - null || < 1 => '0 votos', - 1 => '1 voto', - _ => '$count votos', - }; - - @override - String get noPollVotesLabel => 'No hay votos en la encuesta actualmente'; - - @override - String get loadingPollVotesError => - 'Error al cargar los votos de la encuesta'; - - @override - String get repliedToLabel => 'respondido a:'; - - @override - String newThreadsLabel({required int count}) { - if (count == 1) return '1 nuevo hilo'; - return '$count nuevos hilos'; - } - - @override - String get slideToCancelLabel => 'Desliza para cancelar'; - - @override - String get holdToRecordLabel => - 'MantĆ©n pulsado para grabar, suelta para enviar'; -} diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_fr.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_fr.dart deleted file mode 100644 index 2bb0230c7a..0000000000 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_fr.dart +++ /dev/null @@ -1,628 +0,0 @@ -part of 'stream_chat_localizations.dart'; - -/// The translations for French (`fr`). -class StreamChatLocalizationsFr extends GlobalStreamChatLocalizations { - /// Create an instance of the translation bundle for French. - const StreamChatLocalizationsFr({super.localeName = 'fr'}); - - @override - String get launchUrlError => "Impossible de lancer l'url"; - - @override - String get loadingUsersError => 'Erreur de chargement des utilisateurs'; - - @override - String get noUsersLabel => "Il n'y a pas d'utilisateurs actuellement"; - - @override - String get noPhotoOrVideoLabel => "Il n'y a ni photo ni vidĆ©o"; - - @override - String get retryLabel => 'RĆ©essayer'; - - @override - String get userLastOnlineText => 'DerniĆØre fois en ligne'; - - @override - String get userOnlineText => 'En ligne'; - - @override - String userTypingText(Iterable users) { - if (users.isEmpty) return ''; - final first = users.first; - if (users.length == 1) { - return "${first.name} est en train d'Ć©crire"; - } - return "${first.name} et ${users.length - 1} sont entrain d'Ć©crire"; - } - - @override - String get threadReplyLabel => 'RĆ©ponse au fil de discussion'; - - @override - String get onlyVisibleToYouText => 'Seulement visible par vous'; - - @override - String threadReplyCountText(int count) => - '$count RĆ©ponses au fil de discussion'; - - @override - String attachmentsUploadProgressText({ - required int remaining, - required int total, - }) => - 'Transfert en cours $remaining/$total ...'; - - @override - String pinnedByUserText({ - required User pinnedBy, - required User currentUser, - }) { - final pinnedByCurrentUser = currentUser.id == pinnedBy.id; - if (pinnedByCurrentUser) return 'ƉpinglĆ© par vous'; - return 'ƉpinglĆ© par ${pinnedBy.name}'; - } - - @override - String get sendMessagePermissionError => - "Vous n'ĆŖtes pas autorisĆ© Ć  envoyer des messages"; - - @override - String get emptyMessagesText => "Il n'y a pas de messages actuellement"; - - @override - String get genericErrorText => 'Il y a eu un problĆØme'; - - @override - String get loadingMessagesError => 'Erreur de chargement des messages'; - - @override - String resultCountText(int count) => '$count rĆ©sultats'; - - @override - String get messageDeletedText => 'Ce message a Ć©tĆ© supprimĆ©.'; - - @override - String get messageDeletedLabel => 'Message supprimĆ©'; - - @override - String get editedMessageLabel => 'ƉditĆ©'; - - @override - String get messageReactionsLabel => 'RĆ©actions aux messages'; - - @override - String get emptyChatMessagesText => 'Pas encore de chats ici...'; - - @override - String threadSeparatorText(int replyCount) { - if (replyCount == 1) return '1 RĆ©ponse'; - return '$replyCount Replies'; - } - - @override - String get connectedLabel => 'ConnectĆ©'; - - @override - String get disconnectedLabel => 'DĆ©connectĆ©'; - - @override - String get reconnectingLabel => 'Reconnexion...'; - - @override - String get alsoSendAsDirectMessageLabel => - 'Envoyer aussi comme message direct'; - - @override - String get addACommentOrSendLabel => 'Ajouter un commentaire ou envoyer'; - - @override - String get searchGifLabel => 'Recherche de GIFs'; - - @override - String get writeAMessageLabel => 'Ɖcrire un message'; - - @override - String get instantCommandsLabel => 'Commandes instantanĆ©es'; - - @override - String fileTooLargeAfterCompressionError(double limitInMB) => - 'Le fichier est trop volumineux pour ĆŖtre tĆ©lĆ©chargĆ©. ' - 'La taille maximale des fichiers est de $limitInMB Mo. ' - "Nous avons essayĆ© de le compresser, mais ce n'Ć©tait pas suffisant."; - - @override - String fileTooLargeError(double limitInMB) => - 'Le fichier est trop volumineux pour ĆŖtre tĆ©lĆ©chargĆ©. ' - 'La taille limite du fichier est de $limitInMB Mo.'; - - @override - String get couldNotReadBytesFromFileError => - 'Impossible de lire les octets du fichier.'; - - @override - String get addAFileLabel => 'Ajouter un fichier'; - - @override - String get photoFromCameraLabel => "Photo de l'appareil photo"; - - @override - String get uploadAFileLabel => 'TransfĆ©rer un fichier'; - - @override - String get uploadAPhotoLabel => 'TransfĆ©rer une photo'; - - @override - String get uploadAVideoLabel => 'TransfĆ©rer une vidĆ©o'; - - @override - String get videoFromCameraLabel => 'VidĆ©o depuis la camera'; - - @override - String get okLabel => 'OK'; - - @override - String get somethingWentWrongError => 'Quelque chose a mal tournĆ©'; - - @override - String get addMoreFilesLabel => "Ajouter d'autres fichiers"; - - @override - String get enablePhotoAndVideoAccessMessage => - "Veuillez autoriser l'accĆØs Ć  vos photos" - '\net vidĆ©os afin de pouvoir les partager avec vos amis.'; - - @override - String get allowGalleryAccessMessage => "Autoriser l'accĆØs Ć  votre galerie"; - - @override - String get flagMessageLabel => 'Signaler un message'; - - @override - String get flagMessageQuestion => - 'Voulez-vous envoyer une copie de ce message Ć  un' - '\nmodĆ©rateur pour une enquĆŖte plus approfondie ?'; - - @override - String get flagLabel => 'SIGNALER'; - - @override - String get cancelLabel => 'ANNULER'; - - @override - String get flagMessageSuccessfulLabel => 'Message signalĆ©'; - - @override - String get flagMessageSuccessfulText => - 'Ce message a Ć©tĆ© signalĆ© Ć  un modĆ©rateur.'; - - @override - String get deleteLabel => 'SUPPRIMER'; - - @override - String get deleteMessageLabel => 'Supprimer le message'; - - @override - String get deleteMessageQuestion => - 'Êtes-vous sĆ»r de vouloir supprimer dĆ©finitivement ce\nmessage ?'; - - @override - String get operationCouldNotBeCompletedText => - "L'opĆ©ration n'a pas pu ĆŖtre terminĆ©e."; - - @override - String get replyLabel => 'RĆ©pondre'; - - @override - String togglePinUnpinText({required bool pinned}) { - if (pinned) return 'DĆ©crocher de la conversation'; - return 'Ɖpingler Ć  la conversation'; - } - - @override - String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed}) { - if (isDeleteFailed) return 'Retenter de supprimer le message'; - return 'Supprimer le message'; - } - - @override - String get copyMessageLabel => 'Copier le message'; - - @override - String get editMessageLabel => 'Modifier le message'; - - @override - String toggleResendOrResendEditedMessage({required bool isUpdateFailed}) { - if (isUpdateFailed) return 'Renvoyer le message modifiĆ©'; - return 'Renvoyer'; - } - - @override - String get photosLabel => 'Photos'; - - String _getDay(DateTime dateTime) { - final now = DateTime.now(); - final today = DateTime(now.year, now.month, now.day); - final yesterday = DateTime(now.year, now.month, now.day - 1); - - final date = DateTime(dateTime.year, dateTime.month, dateTime.day); - - if (date == today) { - return "aujourd'hui"; - } else if (date == yesterday) { - return 'hier'; - } else { - return 'le ${Jiffy.parseFromDateTime(date).MMMd}'; - } - } - - @override - String sentAtText({required DateTime date, required DateTime time}) { - final atTime = Jiffy.parseFromDateTime(time.toLocal()); - return 'EnvoyĆ© ${_getDay(date)} Ć  ${atTime.jm}'; - } - - @override - String get todayLabel => "Aujourd'hui"; - - @override - String get yesterdayLabel => 'Hier'; - - @override - String get channelIsMutedText => 'Le canal est coupĆ©'; - - @override - String get noTitleText => 'Aucun titre'; - - @override - String get letsStartChattingLabel => 'CommenƧons Ć  discuter !'; - - @override - String get sendingFirstMessageLabel => - "Que diriez-vous d'envoyer votre premier message Ć  un ami ?"; - - @override - String get startAChatLabel => 'Commencer une discussion'; - - @override - String get loadingChannelsError => 'Erreur lors du chargement des canaux'; - - @override - String get deleteConversationLabel => 'Supprimer la conversation'; - - @override - String get deleteConversationQuestion => - 'Vous ĆŖtes sĆ»r de vouloir supprimer cette conversation ?'; - - @override - String get streamChatLabel => 'Stream Chat'; - - @override - String get searchingForNetworkText => 'Recherche de rĆ©seau'; - - @override - String get offlineLabel => 'Hors ligne...'; - - @override - String get tryAgainLabel => 'Essayer Ć  nouveau'; - - @override - String membersCountText(int count) { - if (count == 1) return '1 Membre'; - return '$count Membres'; - } - - @override - String watchersCountText(int count) { - if (count == 1) return '1 En ligne'; - return '$count En ligne'; - } - - @override - String get viewInfoLabel => 'Voir les informations'; - - @override - String get leaveGroupLabel => 'Quitter le Groupe'; - - @override - String get leaveLabel => 'QUITTER'; - - @override - String get leaveConversationLabel => 'Quitter la conversation'; - - @override - String get leaveConversationQuestion => - 'Etes-vous sĆ»r de vouloir quitter cette conversation ?'; - - @override - String get showInChatLabel => 'Montrer dans la Discussion'; - - @override - String get saveImageLabel => "Sauvegarder l'image"; - - @override - String get saveVideoLabel => 'Sauvegarder la vidĆ©o'; - - @override - String get uploadErrorLabel => 'ERREUR DE TRANSFERT'; - - @override - String get giphyLabel => 'Giphy'; - - @override - String get shuffleLabel => 'MĆ©langer'; - - @override - String get sendLabel => 'Envoyer'; - - @override - String get withText => 'avec'; - - @override - String get inText => 'dans'; - - @override - String get youText => 'Vous'; - - @override - String galleryPaginationText({ - required int currentPage, - required int totalPages, - }) => - '${currentPage + 1} de $totalPages'; - - @override - String get fileText => 'Fichier'; - - @override - String get replyToMessageLabel => 'RĆ©pondre au Message'; - - @override - String attachmentLimitExceedError(int limit) => ''' -Limite de piĆØces jointes dĆ©passĆ©e : il n'est pas possible d'ajouter plus de $limit piĆØces jointes - '''; - - @override - String get viewLibrary => 'Voir la bibliothĆØque'; - - @override - String get slowModeOnLabel => 'Mode lent activĆ©'; - - @override - String get downloadLabel => 'TĆ©lĆ©charger'; - - @override - String toggleMuteUnmuteUserText({required bool isMuted}) { - if (isMuted) { - return "RĆ©activer l'utilisateur"; - } else { - return 'Utilisateur muet'; - } - } - - @override - String toggleMuteUnmuteGroupQuestion({required bool isMuted}) { - if (isMuted) { - return 'Voulez-vous vraiment rĆ©activer le son de ce groupeĀ ?'; - } else { - return 'ĀæEstĆ”s seguro de que quieres silenciar a este grupo?'; - } - } - - @override - String toggleMuteUnmuteUserQuestion({required bool isMuted}) { - if (isMuted) { - return 'Voulez-vous vraiment rĆ©activer le son de cet utilisateurĀ ?'; - } else { - return 'Voulez-vous vraiment dĆ©sactiver cet utilisateurĀ ?'; - } - } - - @override - String toggleMuteUnmuteAction({required bool isMuted}) { - if (isMuted) { - return 'RƉACTIVER LE MUET'; - } else { - return 'MUET'; - } - } - - @override - String toggleMuteUnmuteGroupText({required bool isMuted}) { - if (isMuted) { - return 'Activer le groupe'; - } else { - return 'Groupe muet'; - } - } - - @override - String get linkDisabledDetails => - "L'envoi de liens n'est pas autorisĆ© dans cette conversation."; - - @override - String get linkDisabledError => 'Les liens sont dĆ©sactivĆ©s'; - - @override - String unreadMessagesSeparatorText() => 'Nouveaux messages'; - - @override - String get enableFileAccessMessage => - "Veuillez autoriser l'accĆØs aux fichiers" - '\nafin de pouvoir les partager avec des amis.'; - - @override - String get allowFileAccessMessage => "Autoriser l'accĆØs aux fichiers"; - - @override - String get markAsUnreadLabel => 'Marquer comme non lu'; - - @override - String unreadCountIndicatorLabel({required int unreadCount}) { - return '$unreadCount non lus'; - } - - @override - String get markUnreadError => - 'Erreur lors de la marque du message comme non lu. Impossible de marquer' - ' des messages non lus plus anciens que les 100 derniers messages' - ' du canal.'; - - @override - String createPollLabel({bool isNew = false}) { - if (isNew) return 'CrĆ©er un sondage'; - return 'CrĆ©er sondage'; - } - - @override - String get questionsLabel => 'Questions'; - - @override - String get askAQuestionLabel => 'Poser une question'; - - @override - String? pollQuestionValidationError(int length, Range range) { - final (:min, :max) = range; - - // Check if the question is too short. - if (min != null && length < min) { - return 'La question doit comporter au moins $min caractĆØres'; - } - - // Check if the question is too long. - if (max != null && length > max) { - return 'La question doit comporter au plus $max caractĆØres'; - } - - return null; - } - - @override - String optionLabel({bool isPlural = false}) { - if (isPlural) return 'Options'; - return 'Option'; - } - - @override - String get pollOptionEmptyError => 'L’option ne peut pas ĆŖtre vide'; - - @override - String get pollOptionDuplicateError => 'C’est dĆ©jĆ  une option'; - - @override - String get addAnOptionLabel => 'Ajouter une option'; - - @override - String get multipleAnswersLabel => 'RĆ©ponses multiples'; - - @override - String get maximumVotesPerPersonLabel => - 'Nombre maximum de votes par personne'; - - @override - String? maxVotesPerPersonValidationError(int votes, Range range) { - final (:min, :max) = range; - - if (min != null && votes < min) { - return 'Le dĆ©compte des votes doit ĆŖtre d’au moins $min'; - } - - if (max != null && votes > max) { - return 'Le dĆ©compte des votes doit ĆŖtre d’au plus $max'; - } - - return null; - } - - @override - String get anonymousPollLabel => 'Sondage anonyme'; - - @override - String get pollOptionsLabel => 'Options du sondage'; - - @override - String get suggestAnOptionLabel => 'SuggĆ©rer une option'; - - @override - String get enterANewOptionLabel => 'Saisir une nouvelle option'; - - @override - String get addACommentLabel => 'Ajouter un commentaire'; - - @override - String get pollCommentsLabel => 'Commentaires du sondage'; - - @override - String get updateYourCommentLabel => 'Mettre Ć  jour votre commentaire'; - - @override - String get enterYourCommentLabel => 'Saisir votre commentaire'; - - @override - String get createLabel => 'CrĆ©er'; - - @override - String pollVotingModeLabel(PollVotingMode votingMode) { - return votingMode.when( - disabled: () => 'Vote terminĆ©', - unique: () => 'SĆ©lectionner un', - limited: (count) => "SĆ©lectionner jusqu'Ć  $count", - all: () => 'SĆ©lectionner un ou plusieurs', - ); - } - - @override - String seeAllOptionsLabel({int? count}) { - if (count == null) return 'Voir toutes les options'; - return 'Voir toutes les $count options'; - } - - @override - String get viewCommentsLabel => 'Voir les commentaires'; - - @override - String get viewResultsLabel => 'Voir les rĆ©sultats'; - - @override - String get endVoteLabel => 'Terminer le vote'; - - @override - String get pollResultsLabel => 'RĆ©sultats du sondage'; - - @override - String showAllVotesLabel({int? count}) { - if (count == null) return 'Afficher tous les votes'; - return 'Afficher tous les $count votes'; - } - - @override - String voteCountLabel({int? count}) => switch (count) { - null || < 1 => '0 vote', - 1 => '1 vote', - _ => '$count votes', - }; - - @override - String get noPollVotesLabel => - "Il n'y a pas de votes de sondage actuellement"; - - @override - String get loadingPollVotesError => - 'Erreur de chargement des votes du sondage'; - - @override - String get repliedToLabel => 'rĆ©pondu Ć :'; - - @override - String newThreadsLabel({required int count}) { - if (count == 1) return '1 Nouveau fil'; - return '$count Nouveaux fils'; - } - - @override - String get slideToCancelLabel => 'Glissez pour annuler'; - - @override - String get holdToRecordLabel => - 'Maintenez pour enregistrer, relĆ¢chez pour envoyer'; -} diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_hi.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_hi.dart deleted file mode 100644 index c527d5f7c9..0000000000 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_hi.dart +++ /dev/null @@ -1,619 +0,0 @@ -part of 'stream_chat_localizations.dart'; - -/// The translations for Hindi (`hi`). -class StreamChatLocalizationsHi extends GlobalStreamChatLocalizations { - /// Create an instance of the translation bundle for Hindi. - const StreamChatLocalizationsHi({super.localeName = 'hi'}); - - @override - String get launchUrlError => 'ą¤Æą„‚ą¤†ą¤°ą¤ą¤² ą¤²ą„‰ą¤Øą„ą¤š ą¤Øą¤¹ą„€ą¤‚ कर ą¤øą¤•ą¤¤ą„‡'; - - @override - String get loadingUsersError => 'ą¤Æą„‚ą¤œą¤° ą¤²ą„‹ą¤” ą¤•ą¤°ą¤Øą„‡ ą¤®ą„‡ą¤‚ ą¤øą¤®ą¤øą„ą¤Æą¤¾'; - - @override - String get noUsersLabel => 'ą¤µą¤°ą„ą¤¤ą¤®ą¤¾ą¤Ø ą¤®ą„‡ą¤‚ ą¤•ą„‹ą¤ˆ ą¤Æą„‚ą¤œą¤° ą¤Øą¤¹ą„€ą¤‚ ą¤¹ą„ˆą¤‚'; - - @override - String get noPhotoOrVideoLabel => 'ą¤•ą„‹ą¤ˆ ą¤«ą„‹ą¤Ÿą„‹ या ą¤µą„€ą¤”ą¤æą¤Æą„‹ ą¤Øą¤¹ą„€ą¤‚ ą¤¹ą„ˆą„¤'; - - @override - String get retryLabel => 'ą¤Ŗą„ą¤Ø: ą¤Ŗą„ą¤°ą¤Æą¤¾ą¤ø ą¤•ą¤°ą„‡'; - - @override - String get userLastOnlineText => 'अंतिम ऑनलाइन'; - - @override - String get userOnlineText => 'ऑनलाइन'; - - @override - String userTypingText(Iterable users) { - if (users.isEmpty) return ''; - final first = users.first; - if (users.length == 1) { - return '${first.name} ą¤Ÿą¤¾ą¤‡ą¤Ŗ कर रहा ą¤¹ą„ˆ'; - } - return '${first.name} और ${users.length - 1} और ą¤Ÿą¤¾ą¤‡ą¤Ŗ कर ą¤°ą¤¹ą„‡ ą¤¹ą„ˆą¤‚'; - } - - @override - String get threadReplyLabel => 'ą¤„ą„ą¤°ą„‡ą¤” जवाब'; - - @override - String get onlyVisibleToYouText => 'ą¤•ą„‡ą¤µą¤² ą¤†ą¤Ŗą¤•ą„‹ ą¤¦ą¤æą¤–ą¤¾ą¤ˆ ą¤¦ą„‡ रहा ą¤¹ą„ˆ'; - - @override - String threadReplyCountText(int count) => '$count ą¤„ą„ą¤°ą„‡ą¤” जवाब'; - - @override - String attachmentsUploadProgressText({ - required int remaining, - required int total, - }) => - 'ą¤…ą¤Ŗą¤²ą„‹ą¤”ą¤æą¤‚ą¤— $remaining/$total ...'; - - @override - String pinnedByUserText({ - required User pinnedBy, - required User currentUser, - }) { - final pinnedByCurrentUser = currentUser.id == pinnedBy.id; - if (pinnedByCurrentUser) return 'ą¤†ą¤Ŗą¤•ą„‡ ą¤¦ą„ą¤µą¤¾ą¤°ą¤¾ पिन किया गया'; - return '${pinnedBy.name} ą¤¦ą„ą¤µą¤¾ą¤°ą¤¾ पिन किया गया'; - } - - @override - String get sendMessagePermissionError => 'ą¤†ą¤Ŗą¤•ą„‹ ą¤øą¤‚ą¤¦ą„‡ą¤¶ ą¤­ą„‡ą¤œą¤Øą„‡ ą¤•ą„€ ą¤…ą¤Øą„ą¤®ą¤¤ą¤æ ą¤Øą¤¹ą„€ą¤‚ ą¤¹ą„ˆ'; - - @override - String get emptyMessagesText => 'ą¤µą¤°ą„ą¤¤ą¤®ą¤¾ą¤Ø ą¤®ą„‡ą¤‚ ą¤•ą„‹ą¤ˆ ą¤øą¤‚ą¤¦ą„‡ą¤¶ ą¤Øą¤¹ą„€ą¤‚ ą¤¹ą„ˆ'; - - @override - String get genericErrorText => 'ą¤•ą„ą¤› ą¤øą¤®ą¤øą„ą¤Æą¤¾ ą¤¹ą„‹ ą¤—ą¤ˆ'; - - @override - String get loadingMessagesError => 'ą¤øą¤‚ą¤¦ą„‡ą¤¶ ą¤²ą„‹ą¤” ą¤•ą¤°ą¤Øą„‡ ą¤®ą„‡ą¤‚ ą¤øą¤®ą¤øą„ą¤Æą¤¾'; - - @override - String resultCountText(int count) => '$count परिणाम'; - - @override - String get messageDeletedText => 'यह ą¤øą¤‚ą¤¦ą„‡ą¤¶ हटा दिया गया ą¤¹ą„ˆą„¤'; - - @override - String get messageDeletedLabel => 'ą¤øą¤‚ą¤¦ą„‡ą¤¶ ą¤¹ą¤Ÿą¤¾ą¤Æą„‡'; - - @override - String get editedMessageLabel => 'संपादित'; - - @override - String get messageReactionsLabel => 'ą¤øą¤‚ą¤¦ą„‡ą¤¶ ą¤Ŗą„ą¤°ą¤¤ą¤æą¤•ą„ą¤°ą¤æą¤Æą¤¾ą¤ą¤‚'; - - @override - String get emptyChatMessagesText => 'यहां ą¤…ą¤­ą„€ तक ą¤•ą„‹ą¤ˆ ą¤šą„ˆą¤Ÿ ą¤Øą¤¹ą„€ą¤‚...'; - - @override - String threadSeparatorText(int replyCount) { - if (replyCount == 1) return '1 जवाब'; - return '$replyCount जवाब'; - } - - @override - String get connectedLabel => 'ą¤•ą¤Øą„‡ą¤•ą„ą¤Ÿą„‡ą¤”'; - - @override - String get disconnectedLabel => 'ą¤”ą¤æą¤øą„ą¤•ą¤Øą„‡ą¤•ą„ą¤Ÿą„‡ą¤”'; - - @override - String get reconnectingLabel => 'ą¤Ŗą„ą¤Øą¤ƒ ą¤•ą¤Øą„‡ą¤•ą„ą¤Ÿą¤æą¤‚ą¤—...'; - - @override - String get alsoSendAsDirectMessageLabel => 'ą¤øą„€ą¤§ą„‡ ą¤øą¤‚ą¤¦ą„‡ą¤¶ ą¤•ą„‡ ą¤°ą„‚ą¤Ŗ ą¤®ą„‡ą¤‚ ą¤­ą„€ ą¤­ą„‡ą¤œą„‡ą¤‚'; - - @override - String get addACommentOrSendLabel => 'ą¤ą¤• ą¤Ÿą¤æą¤Ŗą„ą¤Ŗą¤£ą„€ ą¤œą„‹ą¤”ą¤¼ą„‡ą¤‚ या ą¤­ą„‡ą¤œą„‡ą¤‚'; - - @override - String get searchGifLabel => 'ą¤œą„€ą¤†ą¤ˆą¤ą¤« ą¤–ą„‹ą¤œą„‡ą¤‚'; - - @override - String get writeAMessageLabel => 'ą¤ą¤• ą¤øą¤Øą„ą¤¦ą„‡ą¤¶ ą¤²ą¤æą¤–ą¤æą¤'; - - @override - String get instantCommandsLabel => 'ą¤¤ą¤¤ą„ą¤•ą¤¾ą¤² ą¤†ą¤¦ą„‡ą¤¶'; - - @override - String fileTooLargeAfterCompressionError(double limitInMB) => - 'फ़ाइल ą¤…ą¤Ŗą¤²ą„‹ą¤” ą¤•ą¤°ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤¬ą¤¹ą„ą¤¤ ą¤¬ą¤”ą¤¼ą„€ ą¤¹ą„ˆą„¤ ' - 'फ़ाइल आकार ą¤øą„€ą¤®ą¤¾ $limitInMB MB ą¤¹ą„ˆą„¤ ' - 'ą¤¹ą¤®ą¤Øą„‡ ą¤‡ą¤øą„‡ ą¤•ą¤‚ą¤Ŗą„ą¤°ą„‡ą¤ø ą¤•ą¤°ą¤Øą„‡ ą¤•ą„€ ą¤•ą„‹ą¤¶ą¤æą¤¶ ą¤•ą„€, ą¤²ą„‡ą¤•ą¤æą¤Ø यह ą¤•ą¤¾ą¤«ą„€ ą¤Øą¤¹ą„€ą¤‚ ą¤„ą¤¾ą„¤'; - - @override - String fileTooLargeError(double limitInMB) => - 'फ़ाइल ą¤…ą¤Ŗą¤²ą„‹ą¤” ą¤•ą¤°ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤¬ą¤¹ą„ą¤¤ ą¤¬ą¤”ą¤¼ą„€ ą¤¹ą„ˆą„¤ फ़ाइल आकार ą¤øą„€ą¤®ą¤¾ $limitInMB MB ą¤¹ą„ˆą„¤'; - - @override - String get couldNotReadBytesFromFileError => 'फ़ाइल ą¤øą„‡ ą¤¬ą¤¾ą¤‡ą¤Ÿ ą¤Øą¤¹ą„€ą¤‚ पढ़ सका.'; - - @override - String get addAFileLabel => 'ą¤ą¤• फ़ाइल ą¤œą„‹ą¤”ą¤¼ą„‡ą¤‚'; - - @override - String get photoFromCameraLabel => 'ą¤•ą„ˆą¤®ą¤°ą„‡ ą¤øą„‡ ą¤«ą„‹ą¤Ÿą„‹'; - - @override - String get uploadAFileLabel => 'ą¤ą¤• फाइल ą¤…ą¤Ŗą¤²ą„‹ą¤” ą¤•ą¤°ą„‡ą¤‚'; - - @override - String get uploadAPhotoLabel => 'ą¤ą¤• ą¤«ą„‹ą¤Ÿą„‹ ą¤…ą¤Ŗą¤²ą„‹ą¤” ą¤•ą¤°ą„‹'; - - @override - String get uploadAVideoLabel => 'ą¤ą¤• ą¤µą„€ą¤”ą¤æą¤Æą„‹ ą¤…ą¤Ŗą¤²ą„‹ą¤” ą¤•ą¤°ą„‡ą¤‚'; - - @override - String get videoFromCameraLabel => 'ą¤•ą„ˆą¤®ą¤°ą„‡ ą¤øą„‡ ą¤µą„€ą¤”ą¤æą¤Æą„‹'; - - @override - String get okLabel => 'ą¤ ą„€ą¤•'; - - @override - String get somethingWentWrongError => 'ą¤²ą„‹ą¤” ą¤•ą¤°ą¤Øą„‡ ą¤®ą„‡ą¤‚ ą¤øą¤®ą¤øą„ą¤Æą¤¾'; - - @override - String get addMoreFilesLabel => 'और ą¤«ą¤¼ą¤¾ą¤‡ą¤²ą„‡ą¤‚ ą¤œą„‹ą¤”ą¤¼ą„‡ą¤‚'; - - @override - String get enablePhotoAndVideoAccessMessage => - 'ą¤•ą„ƒą¤Ŗą¤Æą¤¾ ą¤…ą¤Ŗą¤Øą„‡ ą¤«ą¤¼ą„‹ą¤Ÿą„‹ और ą¤µą„€ą¤”ą¤æą¤Æą„‹ तक ą¤Ŗą¤¹ą„ą¤‚ą¤š ą¤øą¤•ą„ą¤·ą¤® ą¤•ą¤°ą„‡ą¤‚' - '\nताकि आप ą¤‰ą¤Øą„ą¤¹ą„‡ą¤‚ ą¤®ą¤æą¤¤ą„ą¤°ą„‹ą¤‚ ą¤•ą„‡ साऄ ą¤øą¤¾ą¤ą¤¾ कर ą¤øą¤•ą„‡ą¤‚ą„¤'; - - @override - String get allowGalleryAccessMessage => 'ą¤…ą¤Ŗą¤Øą„€ ą¤—ą„ˆą¤²ą¤°ą„€ तक ą¤Ŗą¤¹ą„ą¤‚ą¤š ą¤•ą„€ ą¤…ą¤Øą„ą¤®ą¤¤ą¤æ ą¤¦ą„‡ą¤‚'; - - @override - String get flagMessageLabel => 'ą¤«ą„ą¤²ą„ˆą¤— ą¤øą¤‚ą¤¦ą„‡ą¤¶'; - - @override - String get flagMessageQuestion => 'ą¤•ą„ą¤Æą¤¾ आप ą¤†ą¤—ą„‡ ą¤•ą„€ ą¤œą¤¾ą¤‚ą¤š ą¤•ą„‡ ą¤²ą¤æą¤ इस ą¤øą¤‚ą¤¦ą„‡ą¤¶ ą¤•ą„€' - '\ną¤ą¤• ą¤Ŗą„ą¤°ą¤¤ą¤æ ą¤®ą„‰ą¤”ą¤°ą„‡ą¤Ÿą¤° ą¤•ą„‹ ą¤­ą„‡ą¤œą¤Øą¤¾ ą¤šą¤¾ą¤¹ą¤¤ą„‡ ą¤¹ą„ˆą¤‚?'; - - @override - String get flagLabel => 'ą¤«ą„ą¤²ą„ˆą¤—'; - - @override - String get cancelLabel => 'ą¤°ą¤¦ą„ą¤¦ ą¤•ą¤°ą„‡ą¤‚'; - - @override - String get flagMessageSuccessfulLabel => 'ą¤øą¤‚ą¤¦ą„‡ą¤¶ ą¤«ą„ą¤²ą„ˆą¤— ą¤¹ą„‹ गया'; - - @override - String get flagMessageSuccessfulText => - 'ą¤øą¤‚ą¤¦ą„‡ą¤¶ ą¤•ą„€ ą¤°ą¤æą¤Ŗą„‹ą¤°ą„ą¤Ÿ ą¤ą¤• ą¤®ą„‰ą¤”ą¤°ą„‡ą¤Ÿą¤° ą¤•ą„‹ कर ą¤¦ą„€ ą¤—ą¤ˆ ą¤¹ą„ˆą„¤'; - - @override - String get deleteLabel => 'ą¤¹ą¤Ÿą¤¾ą¤ą¤'; - - @override - String get deleteMessageLabel => 'ą¤øą¤‚ą¤¦ą„‡ą¤¶ ą¤¹ą¤Ÿą¤¾ą¤ą¤‚'; - - @override - String get deleteMessageQuestion => - 'ą¤•ą„ą¤Æą¤¾ आप ą¤µą¤¾ą¤•ą¤ˆ इस ą¤øą¤‚ą¤¦ą„‡ą¤¶ ą¤•ą„‹ ą¤øą„ą¤„ą¤¾ą¤Æą„€ ą¤°ą„‚ą¤Ŗ ą¤øą„‡\nहटाना ą¤šą¤¾ą¤¹ą¤¤ą„‡ ą¤¹ą„ˆą¤‚?'; - - @override - String get operationCouldNotBeCompletedText => - 'ą¤•ą¤¾ą¤°ą„ą¤°ą¤µą¤¾ą¤ˆ ą¤Ŗą„‚ą¤°ą„€ ą¤Øą¤¹ą„€ą¤‚ ą¤•ą„€ जा ą¤øą¤•ą„€.'; - - @override - String get replyLabel => 'जवाब ą¤¦ą„‡ą¤‚'; - - @override - String togglePinUnpinText({required bool pinned}) { - if (pinned) return 'ą¤¬ą¤¾ą¤¤ą¤šą„€ą¤¤ ą¤øą„‡ अनपिन ą¤•ą¤°ą„‡ą¤‚'; - return 'ą¤¬ą¤¾ą¤¤ą¤šą„€ą¤¤ ą¤®ą„‡ą¤‚ पिन ą¤•ą¤°ą„‡ą¤‚'; - } - - @override - String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed}) { - if (isDeleteFailed) return 'ą¤øą¤‚ą¤¦ą„‡ą¤¶ ą¤¹ą¤Ÿą¤¾ą¤Øą„‡ का ą¤Ŗą„ą¤Øą¤ƒ ą¤Ŗą„ą¤°ą¤Æą¤¾ą¤ø ą¤•ą¤°ą„‡ą¤‚'; - return 'ą¤øą¤‚ą¤¦ą„‡ą¤¶ ą¤•ą„‹ ą¤¹ą¤Ÿą¤¾ą¤ą¤‚'; - } - - @override - String get copyMessageLabel => 'ą¤øą¤‚ą¤¦ą„‡ą¤¶ ą¤•ą„‰ą¤Ŗą„€ ą¤•ą¤°ą„‡ą¤‚'; - - @override - String get editMessageLabel => 'ą¤øą¤‚ą¤¦ą„‡ą¤¶ ą¤ą¤”ą¤æą¤Ÿ ą¤•ą¤°ą„‡ą¤‚'; - - @override - String toggleResendOrResendEditedMessage({required bool isUpdateFailed}) { - if (isUpdateFailed) return 'ą¤ą¤”ą¤æą¤Ÿ ą¤øą¤‚ą¤¦ą„‡ą¤¶ फिर ą¤øą„‡ ą¤­ą„‡ą¤œą„‡ą¤‚'; - return 'ą¤Ŗą„ą¤Ø: ą¤­ą„‡ą¤œą„‡ą¤‚'; - } - - @override - String get photosLabel => 'ą„žą„‹ą¤Ÿą„‹ą¤œ'; - - String _getDay(DateTime dateTime) { - final now = DateTime.now(); - final today = DateTime(now.year, now.month, now.day); - final yesterday = DateTime(now.year, now.month, now.day - 1); - - final date = DateTime(dateTime.year, dateTime.month, dateTime.day); - - if (date == today) { - return 'ą¤†ą¤œ'; - } else if (date == yesterday) { - return 'कल'; - } else { - return '${Jiffy.parseFromDateTime(date).MMMd} ą¤•ą„‹'; - } - } - - @override - String sentAtText({required DateTime date, required DateTime time}) { - final atTime = Jiffy.parseFromDateTime(time.toLocal()); - return '${_getDay(date)} ${atTime.jm} ą¤¬ą¤œą„‡ ą¤­ą„‡ą¤œą¤¾ गया'; - } - - @override - String get todayLabel => 'ą¤†ą¤œ'; - - @override - String get yesterdayLabel => 'कल'; - - @override - String get channelIsMutedText => 'ą¤šą„ˆą¤Øą¤² ą¤®ą„ą¤Æą„‚ą¤Ÿ ą¤¹ą„ˆ'; - - @override - String get noTitleText => 'ą¤•ą„‹ą¤ˆ ą¤¶ą„€ą¤°ą„ą¤·ą¤• ą¤Øą¤¹ą„€ą¤‚'; - - @override - String get letsStartChattingLabel => 'ą¤šą¤²ą„‹ ą¤šą„ˆą¤Ÿ करना ą¤¶ą„ą¤°ą„‚ ą¤•ą¤°ą„‡ą¤‚!'; - - @override - String get sendingFirstMessageLabel => - 'ą¤•ą¤æą¤øą„€ ą¤®ą¤æą¤¤ą„ą¤° ą¤•ą„‹ अपना पहला ą¤øą¤‚ą¤¦ą„‡ą¤¶ ą¤­ą„‡ą¤œą¤Øą„‡ ą¤•ą„‡ ą¤¬ą¤¾ą¤°ą„‡ ą¤®ą„‡ą¤‚ ą¤•ą„ą¤Æą¤¾ विचार ą¤¹ą„ˆ?'; - - @override - String get startAChatLabel => 'ą¤šą„ˆą¤Ÿ ą¤¶ą„ą¤°ą„‚ ą¤•ą¤°ą„‡ą¤‚'; - - @override - String get loadingChannelsError => 'ą¤šą„ˆą¤Øą¤² ą¤²ą„‹ą¤” ą¤•ą¤°ą¤Øą„‡ ą¤®ą„‡ą¤‚ ą¤øą¤®ą¤øą„ą¤Æą¤¾'; - - @override - String get deleteConversationLabel => 'ą¤µą¤¾ą¤°ą„ą¤¤ą¤¾ą¤²ą¤¾ą¤Ŗ ą¤¹ą¤Ÿą¤¾ą¤'; - - @override - String get deleteConversationQuestion => - 'ą¤•ą„ą¤Æą¤¾ आप ą¤µą¤¾ą¤•ą¤ˆ इस ą¤µą¤¾ą¤°ą„ą¤¤ą¤¾ą¤²ą¤¾ą¤Ŗ ą¤•ą„‹ हटाना ą¤šą¤¾ą¤¹ą¤¤ą„‡ ą¤¹ą„ˆą¤‚?'; - - @override - String get streamChatLabel => 'ą¤øą„ą¤Ÿą„ą¤°ą„€ą¤® ą¤šą„ˆą¤Ÿ'; - - @override - String get searchingForNetworkText => 'ą¤Øą„‡ą¤Ÿą¤µą¤°ą„ą¤• ą¤–ą„‹ą¤œ ą¤°ą¤¹ą„‡ ą¤¹ą„ˆą¤‚'; - - @override - String get offlineLabel => 'ऑफलाइन...'; - - @override - String get tryAgainLabel => 'ą¤Ŗą„ą¤Øą¤ƒ ą¤Ŗą„ą¤°ą¤Æą¤¾ą¤ø ą¤•ą¤°ą„‡ą¤‚'; - - @override - String membersCountText(int count) { - if (count == 1) return '1 ą¤øą¤¦ą¤øą„ą¤Æ'; - return '$count ą¤øą¤¦ą¤øą„ą¤Æ'; - } - - @override - String watchersCountText(int count) { - if (count == 1) return '1 ऑनलाइन'; - return '$count ऑनलाइन'; - } - - @override - String get viewInfoLabel => 'ą¤œą¤¾ą¤Øą¤•ą¤¾ą¤°ą„€ ą¤¦ą„‡ą¤–ą„‡ą¤‚'; - - @override - String get leaveGroupLabel => 'ą¤øą¤®ą„‚ą¤¹ ą¤›ą„‹ą„œą„‡'; - - @override - String get leaveLabel => 'ą¤›ą„‹ą„œą„‡'; - - @override - String get leaveConversationLabel => 'ą¤µą¤¾ą¤°ą„ą¤¤ą¤¾ą¤²ą¤¾ą¤Ŗ ą¤›ą„‹ą„œą„‡'; - - @override - String get leaveConversationQuestion => - 'ą¤•ą„ą¤Æą¤¾ आप ą¤µą¤¾ą¤•ą¤ˆ इस ą¤¬ą¤¾ą¤¤ą¤šą„€ą¤¤ ą¤•ą„‹ ą¤›ą„‹ą¤”ą¤¼ą¤Øą¤¾ ą¤šą¤¾ą¤¹ą¤¤ą„‡ ą¤¹ą„ˆą¤‚?'; - - @override - String get showInChatLabel => 'ą¤šą„ˆą¤Ÿ ą¤®ą„‡ą¤‚ ą¤¦ą¤æą¤–ą¤¾ą¤ą¤‚'; - - @override - String get saveImageLabel => 'ą¤šą¤æą¤¤ą„ą¤° ą¤•ą„‹ ą¤øą„‡ą¤µ ą¤•ą¤°ą„‡ą¤‚'; - - @override - String get saveVideoLabel => 'ą¤µą„€ą¤”ą¤æą¤Æą„‹ ą¤•ą„‹ ą¤øą„‡ą¤µ ą¤•ą¤°ą„‡'; - - @override - String get uploadErrorLabel => 'ą¤…ą¤Ŗą¤²ą„‹ą¤” ą¤øą¤®ą¤øą„ą¤Æą¤¾'; - - @override - String get giphyLabel => 'ą¤œą¤æą„žą„€'; - - @override - String get shuffleLabel => 'ą¤¬ą¤¦ą¤²ą„‡ą¤‚'; - - @override - String get sendLabel => 'ą¤­ą„‡ą¤œą„‡ą¤‚'; - - @override - String get withText => 'विद'; - - @override - String get inText => 'इन'; - - @override - String get youText => 'आप'; - - @override - String galleryPaginationText({ - required int currentPage, - required int totalPages, - }) => - '${currentPage + 1} ऑफ़ $totalPages'; - - @override - String get fileText => 'फ़ाइल'; - - @override - String get replyToMessageLabel => 'ą¤øą¤‚ą¤¦ą„‡ą¤¶ का जवाब'; - - @override - String attachmentLimitExceedError(int limit) => ''' -ą¤…ą¤Ÿą„ˆą¤šą¤®ą„‡ą¤‚ą¤Ÿ लिमिट: $limit ą¤…ą¤Ÿą„ˆą¤šą¤®ą„‡ą¤‚ą¤Ÿ ą¤øą„‡ अधिक ą¤œą„‹ą¤”ą¤¼ą¤Øą¤¾ संभव ą¤Øą¤¹ą„€ą¤‚ ą¤¹ą„ˆ - '''; - - @override - String get viewLibrary => 'ą¤Ŗą„ą¤øą„ą¤¤ą¤•ą¤¾ą¤²ą¤Æ ą¤¦ą„‡ą¤–ą¤æą¤Æą„‡'; - - @override - String get slowModeOnLabel => 'ą¤øą„ą¤²ą„‹ ą¤®ą„‹ą¤” ą¤šą¤¾ą¤²ą„‚'; - - @override - String get downloadLabel => 'ą¤”ą¤¾ą¤‰ą¤Øą¤²ą„‹ą¤”'; - - @override - String toggleMuteUnmuteUserText({required bool isMuted}) { - if (isMuted) { - return 'ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ ą¤•ą„‹ ą¤…ą¤Øą¤®ą„ą¤Æą„‚ą¤Ÿ ą¤•ą¤°ą„‡ą¤‚'; - } else { - return 'ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ ą¤•ą„‹ ą¤®ą„ą¤Æą„‚ą¤Ÿ ą¤•ą¤°ą„‡ą¤‚'; - } - } - - @override - String toggleMuteUnmuteGroupQuestion({required bool isMuted}) { - if (isMuted) { - return 'ą¤•ą„ą¤Æą¤¾ आप ą¤µą¤¾ą¤•ą¤ˆ इस ą¤øą¤®ą„‚ą¤¹ ą¤•ą„‹ ą¤…ą¤Øą¤®ą„ą¤Æą„‚ą¤Ÿ करना ą¤šą¤¾ą¤¹ą¤¤ą„‡ ą¤¹ą„ˆą¤‚?'; - } else { - return 'ą¤•ą„ą¤Æą¤¾ आप ą¤µą¤¾ą¤•ą¤ˆ इस ą¤øą¤®ą„‚ą¤¹ ą¤•ą„‹ ą¤®ą„ą¤Æą„‚ą¤Ÿ करना ą¤šą¤¾ą¤¹ą¤¤ą„‡ ą¤¹ą„ˆą¤‚?'; - } - } - - @override - String toggleMuteUnmuteUserQuestion({required bool isMuted}) { - if (isMuted) { - return 'ą¤•ą„ą¤Æą¤¾ आप ą¤µą¤¾ą¤•ą¤ˆ इस ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ ą¤•ą„‹ ą¤…ą¤Øą¤®ą„ą¤Æą„‚ą¤Ÿ करना ą¤šą¤¾ą¤¹ą¤¤ą„‡ ą¤¹ą„ˆą¤‚?'; - } else { - return 'ą¤•ą„ą¤Æą¤¾ आप ą¤µą¤¾ą¤•ą¤ˆ इस ą¤‰ą¤Ŗą¤Æą„‹ą¤—ą¤•ą¤°ą„ą¤¤ą¤¾ ą¤•ą„‹ ą¤®ą„ą¤Æą„‚ą¤Ÿ करना ą¤šą¤¾ą¤¹ą¤¤ą„‡ ą¤¹ą„ˆą¤‚?'; - } - } - - @override - String toggleMuteUnmuteAction({required bool isMuted}) { - if (isMuted) { - return 'ą¤…ą¤Øą¤®ą„ą¤Æą„‚ą¤Ÿ'; - } else { - return 'ą¤®ą„‚ą¤•'; - } - } - - @override - String toggleMuteUnmuteGroupText({required bool isMuted}) { - if (isMuted) { - return 'ą¤øą¤®ą„‚ą¤¹ ą¤…ą¤Øą¤®ą„ą¤Æą„‚ą¤Ÿ ą¤•ą¤°ą„‡ą¤‚'; - } else { - return 'ą¤®ą„‚ą¤• ą¤øą¤®ą„‚ą¤¹'; - } - } - - @override - String get linkDisabledDetails => - 'इस ą¤¬ą¤¾ą¤¤ą¤šą„€ą¤¤ ą¤®ą„‡ą¤‚ लिंक ą¤­ą„‡ą¤œą¤Øą„‡ ą¤•ą„€ ą¤…ą¤Øą„ą¤®ą¤¤ą¤æ ą¤Øą¤¹ą„€ą¤‚ ą¤¹ą„ˆ.'; - - @override - String get linkDisabledError => 'लिंक ą¤­ą„‡ą¤œą¤Øą¤¾ ą¤Ŗą„ą¤°ą¤¤ą¤æą¤¬ą¤‚ą¤§ą¤æą¤¤'; - - @override - String unreadMessagesSeparatorText() => 'ą¤Øą¤ ą¤øą¤‚ą¤¦ą„‡ą¤¶ą„¤'; - - @override - String get enableFileAccessMessage => 'ą¤•ą„ƒą¤Ŗą¤Æą¤¾ ą¤«ą¤¼ą¤¾ą¤‡ą¤²ą„‹ą¤‚ तक ą¤Ŗą¤¹ą„ą¤‚ą¤š ą¤øą¤•ą„ą¤·ą¤® ą¤•ą¤°ą„‡ą¤‚ ताकि' - '\nआप ą¤‰ą¤Øą„ą¤¹ą„‡ą¤‚ ą¤®ą¤æą¤¤ą„ą¤°ą„‹ą¤‚ ą¤•ą„‡ साऄ ą¤øą¤¾ą¤ą¤¾ कर ą¤øą¤•ą„‡ą¤‚ą„¤'; - - @override - String get allowFileAccessMessage => 'ą¤«ą¤¾ą¤‡ą¤²ą„‹ą¤‚ तक ą¤Ŗą¤¹ą„ą¤‚ą¤š ą¤•ą„€ ą¤…ą¤Øą„ą¤®ą¤¤ą¤æ ą¤¦ą„‡ą¤‚'; - - @override - String get markAsUnreadLabel => 'अपठित ą¤šą¤æą¤¹ą„ą¤Øą¤æą¤¤ ą¤•ą¤°ą„‡ą¤‚'; - - @override - String unreadCountIndicatorLabel({required int unreadCount}) { - return '$unreadCount अपठित'; - } - - @override - String get markUnreadError => - 'ą¤øą¤‚ą¤¦ą„‡ą¤¶ ą¤•ą„‹ अपठित ą¤®ą¤¾ą¤°ą„ą¤• ą¤•ą¤°ą¤Øą„‡ ą¤®ą„‡ą¤‚ ą¤¤ą„ą¤°ą„ą¤Ÿą¤æą„¤ ą¤øą¤¬ą¤øą„‡ ą¤Øą¤ 100 ą¤šą„ˆą¤Øą¤² ą¤øą¤‚ą¤¦ą„‡ą¤¶ ą¤øą„‡ ą¤Ŗą¤¹ą¤²ą„‡ ą¤•ą„‡' - ' ą¤øą¤­ą„€ अपठित ą¤øą¤‚ą¤¦ą„‡ą¤¶ą„‹ą¤‚ ą¤•ą„‹ अपठित ą¤®ą¤¾ą¤°ą„ą¤• ą¤Øą¤¹ą„€ą¤‚ किया जा सकता ą¤¹ą„ˆą„¤'; - - @override - String createPollLabel({bool isNew = false}) { - if (isNew) return 'ą¤ą¤• नया ą¤Ŗą„‹ą¤² ą¤¬ą¤Øą¤¾ą¤ą¤'; - return 'ą¤Ŗą„‹ą¤² ą¤¬ą¤Øą¤¾ą¤ą¤'; - } - - @override - String get questionsLabel => 'ą¤Ŗą„ą¤°ą¤¶ą„ą¤Ø'; - - @override - String get askAQuestionLabel => 'ą¤Ŗą„ą¤°ą¤¶ą„ą¤Ø ą¤Ŗą„‚ą¤›ą„‡ą¤‚'; - - @override - String? pollQuestionValidationError(int length, Range range) { - final (:min, :max) = range; - - // Check if the question is too short. - if (min != null && length < min) { - return 'ą¤Ŗą„ą¤°ą¤¶ą„ą¤Ø कम ą¤øą„‡ कम $min ą¤…ą¤•ą„ą¤·ą¤° का ą¤¹ą„‹ą¤Øą¤¾ ą¤šą¤¾ą¤¹ą¤æą¤'; - } - - // Check if the question is too long. - if (max != null && length > max) { - return 'ą¤Ŗą„ą¤°ą¤¶ą„ą¤Ø अधिकतम $max ą¤…ą¤•ą„ą¤·ą¤° का ą¤¹ą„‹ सकता ą¤¹ą„ˆ'; - } - - return null; - } - - @override - String optionLabel({bool isPlural = false}) => 'ą¤µą¤æą¤•ą¤²ą„ą¤Ŗ'; - - @override - String get pollOptionEmptyError => 'ą¤µą¤æą¤•ą¤²ą„ą¤Ŗ ą¤–ą¤¾ą¤²ą„€ ą¤Øą¤¹ą„€ą¤‚ ą¤¹ą„‹ सकता'; - - @override - String get pollOptionDuplicateError => 'यह ą¤Ŗą¤¹ą¤²ą„‡ ą¤øą„‡ ą¤¹ą„€ ą¤ą¤• ą¤µą¤æą¤•ą¤²ą„ą¤Ŗ ą¤¹ą„ˆ'; - - @override - String get addAnOptionLabel => 'ą¤µą¤æą¤•ą¤²ą„ą¤Ŗ ą¤œą„‹ą¤”ą¤¼ą„‡ą¤‚'; - - @override - String get multipleAnswersLabel => 'ą¤ą¤• ą¤øą„‡ अधिक ą¤‰ą¤¤ą„ą¤¤ą¤°'; - - @override - String get maximumVotesPerPersonLabel => 'ą¤Ŗą„ą¤°ą¤¤ą¤æ ą¤µą„ą¤Æą¤•ą„ą¤¤ą¤æ अधिकतम ą¤µą„‹ą¤Ÿ'; - - @override - String? maxVotesPerPersonValidationError(int votes, Range range) { - final (:min, :max) = range; - - if (min != null && votes < min) { - return 'ą¤µą„‹ą¤Ÿą„‹ą¤‚ ą¤•ą„€ ą¤—ą¤æą¤Øą¤¤ą„€ कम ą¤øą„‡ कम $min ą¤¹ą„‹ą¤Øą„€ ą¤šą¤¾ą¤¹ą¤æą¤'; - } - - if (max != null && votes > max) { - return 'ą¤µą„‹ą¤Ÿą„‹ą¤‚ ą¤•ą„€ ą¤—ą¤æą¤Øą¤¤ą„€ ą¤œą„ą¤Æą¤¾ą¤¦ą¤¾ ą¤øą„‡ ą¤œą„ą¤Æą¤¾ą¤¦ą¤¾ $max ą¤¹ą„‹ ą¤øą¤•ą¤¤ą„€ ą¤¹ą„ˆ'; - } - - return null; - } - - @override - String get anonymousPollLabel => 'ą¤…ą¤œą„ą¤žą¤¾ą¤¤ ą¤Ŗą„‹ą¤²'; - - @override - String get suggestAnOptionLabel => 'ą¤µą¤æą¤•ą¤²ą„ą¤Ŗ ą¤øą„ą¤ą¤¾ą¤ą¤‚'; - - @override - String get addACommentLabel => 'ą¤•ą¤®ą„‡ą¤‚ą¤Ÿ ą¤œą„‹ą¤”ą¤¼ą„‡ą¤‚'; - - @override - String get createLabel => 'ą¤•ą„ą¤°ą¤æą¤ą¤Ÿ ą¤•ą¤°ą„‡ą¤‚'; - - @override - String get endVoteLabel => 'ą¤µą„‹ą¤Ÿ ą¤øą¤®ą¤¾ą¤Ŗą„ą¤¤ ą¤•ą¤°ą„‡ą¤‚'; - - @override - String get enterANewOptionLabel => 'ą¤ą¤• नया ą¤µą¤æą¤•ą¤²ą„ą¤Ŗ ą¤¦ą¤°ą„ą¤œ ą¤•ą¤°ą„‡ą¤‚'; - - @override - String get enterYourCommentLabel => 'ą¤…ą¤Ŗą¤Øą„€ ą¤Ÿą¤æą¤Ŗą„ą¤Ŗą¤£ą„€ ą¤¦ą¤°ą„ą¤œ ą¤•ą¤°ą„‡ą¤‚'; - - @override - String get loadingPollVotesError => 'ą¤Ŗą„‹ą¤² ą¤µą„‹ą¤Ÿ ą¤²ą„‹ą¤” ą¤•ą¤°ą¤Øą„‡ ą¤®ą„‡ą¤‚ ą¤¤ą„ą¤°ą„ą¤Ÿą¤æ'; - - @override - String get noPollVotesLabel => 'ą¤•ą„‹ą¤ˆ ą¤Ŗą„‹ą¤² ą¤µą„‹ą¤Ÿ ą¤Øą¤¹ą„€ą¤‚'; - - @override - String get pollCommentsLabel => 'ą¤Ŗą„‹ą¤² ą¤Ÿą¤æą¤Ŗą„ą¤Ŗą¤£ą¤æą¤Æą¤¾ą¤'; - - @override - String get pollOptionsLabel => 'ą¤Ŗą„‹ą¤² ą¤µą¤æą¤•ą¤²ą„ą¤Ŗ'; - - @override - String get pollResultsLabel => 'ą¤Ŗą„‹ą¤² परिणाम'; - - @override - String pollVotingModeLabel(PollVotingMode votingMode) { - return votingMode.when( - disabled: () => 'मतदान ą¤øą¤®ą¤¾ą¤Ŗą„ą¤¤', - unique: () => 'ą¤ą¤• ą¤šą„ą¤Øą„‡ą¤‚', - limited: (count) => '$count तक ą¤šą„ą¤Øą„‡ą¤‚', - all: () => 'ą¤ą¤• या अधिक ą¤šą„ą¤Øą„‡ą¤‚', - ); - } - - @override - String seeAllOptionsLabel({int? count}) { - if (count != null) { - return 'ą¤øą¤­ą„€ $count ą¤µą¤æą¤•ą¤²ą„ą¤Ŗ ą¤¦ą„‡ą¤–ą„‡ą¤‚'; - } - return 'ą¤øą¤­ą„€ ą¤µą¤æą¤•ą¤²ą„ą¤Ŗ ą¤¦ą„‡ą¤–ą„‡ą¤‚'; - } - - @override - String showAllVotesLabel({int? count}) { - if (count != null) { - return 'ą¤øą¤­ą„€ $count ą¤µą„‹ą¤Ÿ ą¤¦ą¤æą¤–ą¤¾ą¤ą¤‚'; - } - return 'ą¤øą¤­ą„€ ą¤µą„‹ą¤Ÿ ą¤¦ą¤æą¤–ą¤¾ą¤ą¤‚'; - } - - @override - String get updateYourCommentLabel => 'ą¤…ą¤Ŗą¤Øą„€ ą¤Ÿą¤æą¤Ŗą„ą¤Ŗą¤£ą„€ ą¤…ą¤Ŗą¤”ą„‡ą¤Ÿ ą¤•ą¤°ą„‡ą¤‚'; - - @override - String get viewCommentsLabel => 'ą¤Ÿą¤æą¤Ŗą„ą¤Ŗą¤£ą¤æą¤Æą¤¾ą¤ ą¤¦ą„‡ą¤–ą„‡ą¤‚'; - - @override - String get viewResultsLabel => 'परिणाम ą¤¦ą„‡ą¤–ą„‡ą¤‚'; - - @override - String voteCountLabel({int? count}) { - if (count != null) { - return '$count ą¤µą„‹ą¤Ÿ'; - } - return 'ą¤µą„‹ą¤Ÿ'; - } - - @override - String get repliedToLabel => 'जवाब दिया:'; - - @override - String newThreadsLabel({required int count}) { - if (count == 1) return '1 नया ą¤„ą„ą¤°ą„‡ą¤”'; - return '$count ą¤Øą¤ ą¤„ą„ą¤°ą„‡ą¤”ą„ą¤ø'; - } - - @override - String get slideToCancelLabel => 'ą¤°ą¤¦ą„ą¤¦ ą¤•ą¤°ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤øą„ą¤²ą¤¾ą¤‡ą¤” ą¤•ą¤°ą„‡ą¤‚'; - - @override - String get holdToRecordLabel => - 'ą¤°ą¤æą¤•ą„‰ą¤°ą„ą¤” ą¤•ą¤°ą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤¦ą¤¬ą¤¾ą¤ ą¤°ą¤–ą„‡ą¤‚, ą¤­ą„‡ą¤œą¤Øą„‡ ą¤•ą„‡ ą¤²ą¤æą¤ ą¤›ą„‹ą¤”ą¤¼ą„‡ą¤‚'; -} diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_it.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_it.dart deleted file mode 100644 index c57577a182..0000000000 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_it.dart +++ /dev/null @@ -1,628 +0,0 @@ -part of 'stream_chat_localizations.dart'; - -/// The translations for Italian (`it`). -class StreamChatLocalizationsIt extends GlobalStreamChatLocalizations { - /// Create an instance of the translation bundle for Italian. - const StreamChatLocalizationsIt({super.localeName = 'it'}); - - @override - String get launchUrlError => "Impossibile aprire l'url"; - - @override - String get loadingUsersError => 'Errore durante il carimento degli utenti'; - - @override - String get noUsersLabel => "Non c'Ć© nessun utente al momento"; - - @override - String get noPhotoOrVideoLabel => 'Non ci sono foto o video'; - - @override - String get retryLabel => 'Riprova'; - - @override - String get userLastOnlineText => 'Ultimo accesso'; - - @override - String get userOnlineText => 'Online'; - - @override - String userTypingText(Iterable users) { - if (users.isEmpty) return ''; - final first = users.first; - if (users.length == 1) { - return '${first.name} sta scrivendo'; - } - return '${first.name} e altri ${users.length - 1} stanno scrivendo'; - } - - @override - String get threadReplyLabel => 'Rispondi nel thread'; - - @override - String get onlyVisibleToYouText => 'Visible solo a te'; - - @override - String threadReplyCountText(int count) { - if (count == 1) { - return '1 risposta al thread'; - } - return '$count risposte al thread'; - } - - @override - String attachmentsUploadProgressText({ - required int remaining, - required int total, - }) => - 'Caricamento $remaining/$total ...'; - - @override - String pinnedByUserText({ - required User pinnedBy, - required User currentUser, - }) { - final pinnedByCurrentUser = currentUser.id == pinnedBy.id; - if (pinnedByCurrentUser) return 'Messo in evidenza da te'; - return 'Messo in evidenza da ${pinnedBy.name}'; - } - - @override - String get sendMessagePermissionError => - "Non hai l'autorizzazione per inviare messaggi"; - - @override - String get emptyMessagesText => "Non c'Ć© nessun messaggio al momento"; - - @override - String get genericErrorText => 'Qualcosa ĆØ andato storto'; - - @override - String get loadingMessagesError => - 'Errore durante il caricamento dei messaggi'; - - @override - String resultCountText(int count) => '$count risultati'; - - @override - String get messageDeletedText => 'Questo messaggio ĆØ stato eliminato'; - - @override - String get messageDeletedLabel => 'Messaggio cancellato'; - - @override - String get editedMessageLabel => 'Modificato'; - - @override - String get messageReactionsLabel => 'Reazioni al messaggio'; - - @override - String get emptyChatMessagesText => 'Nessuna conversazione al momento...'; - - @override - String threadSeparatorText(int replyCount) { - if (replyCount == 1) return '1 risposta'; - return '$replyCount risposte'; - } - - @override - String get connectedLabel => 'Connesso'; - - @override - String get disconnectedLabel => 'Disconnesso'; - - @override - String get reconnectingLabel => 'Riconnessione in corso...'; - - @override - String get alsoSendAsDirectMessageLabel => - 'Manda anche come messaggio diretto'; - - @override - String get addACommentOrSendLabel => 'Aggiungi un commento o invia'; - - @override - String get searchGifLabel => 'Cerca una GIF'; - - @override - String get writeAMessageLabel => 'Scrivi un messaggio'; - - @override - String get instantCommandsLabel => 'Commandi istantanei'; - - @override - String fileTooLargeAfterCompressionError(double limitInMB) => - 'Il file ĆØ troppo grande per essere caricato. ' - 'Il file eccede il limite di $limitInMB MB. ' - 'Abbiamo provato a comprimerlo, ma non ĆØ stato abbastanza.'; - - @override - String fileTooLargeError(double limitInMB) => ''' -Il file ĆØ troppo grande per essere caricato. Il limite ĆØ di $limitInMB MB.'''; - - @override - String get couldNotReadBytesFromFileError => - 'Impossibile leggere i byte dal file.'; - - @override - String get addAFileLabel => 'Aggiungi un file'; - - @override - String get photoFromCameraLabel => 'Immagine dalla fotocamera'; - - @override - String get uploadAFileLabel => 'Carica un file'; - - @override - String get uploadAPhotoLabel => 'Carica una foto'; - - @override - String get uploadAVideoLabel => 'Carica un video'; - - @override - String get videoFromCameraLabel => 'Video dalla fotocamera'; - - @override - String get okLabel => 'Ok'; - - @override - String get somethingWentWrongError => 'Qualcosa ĆØ andato storto'; - - @override - String get addMoreFilesLabel => 'Aggiungi altri file'; - - @override - String get enablePhotoAndVideoAccessMessage => - "Per favore attiva l'accesso alle foto" - '\ne ai video cosĆ­ potrai condividerli con i tuoi amici.'; - - @override - String get allowGalleryAccessMessage => "Permetti l'accesso alla galleria"; - - @override - String get flagMessageLabel => 'Segnala messaggio'; - - @override - String get flagMessageQuestion => 'Vuoi mandare una copia di questo messaggio' - '\nad un moderatore?'; - - @override - String get flagLabel => 'SEGNALA'; - - @override - String get cancelLabel => 'ANNULLA'; - - @override - String get flagMessageSuccessfulLabel => 'Messaggio segnalato'; - - @override - String get flagMessageSuccessfulText => - 'Questo messaggio ĆØ stato segnalato ad un moderatore.'; - - @override - String get deleteLabel => 'CANCELLA'; - - @override - String get deleteMessageLabel => 'Cancella messaggio'; - - @override - String get deleteMessageQuestion => - 'Sei sicuro di voler definitivamente cancellare questo\nmessaggio?'; - - @override - String get operationCouldNotBeCompletedText => - 'Non ĆØ stato possibile completare questa operazione.'; - - @override - String get replyLabel => 'Rispondi'; - - @override - String togglePinUnpinText({required bool pinned}) { - if (pinned) return 'Rimuovi dagli elementi in evidenza'; - return 'Metti in evidenza'; - } - - @override - String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed}) { - if (isDeleteFailed) return 'Riprova a cancellare il messaggio'; - return 'Cancella il messaggio'; - } - - @override - String get copyMessageLabel => 'Copia messaggio'; - - @override - String get editMessageLabel => 'Modifica messaggio'; - - @override - String toggleResendOrResendEditedMessage({required bool isUpdateFailed}) { - if (isUpdateFailed) return 'Riprova modifica messaggio'; - return 'Riprova'; - } - - @override - String get photosLabel => 'Foto'; - - String _getDay(DateTime dateTime) { - final now = DateTime.now(); - final today = DateTime(now.year, now.month, now.day); - final yesterday = DateTime(now.year, now.month, now.day - 1); - - final date = DateTime(dateTime.year, dateTime.month, dateTime.day); - - if (date == today) { - return 'oggi'; - } else if (date == yesterday) { - return 'ieri'; - } else { - return 'il ${Jiffy.parseFromDateTime(date).MMMd}'; - } - } - - @override - String sentAtText({required DateTime date, required DateTime time}) { - final atTime = Jiffy.parseFromDateTime(time.toLocal()); - return 'Inviato ${_getDay(date)} alle ${atTime.jm}'; - } - - @override - String get todayLabel => 'Oggi'; - - @override - String get yesterdayLabel => 'Ieri'; - - @override - String get channelIsMutedText => 'Il canale ĆØ silenziato'; - - @override - String get noTitleText => 'Nessun titolo'; - - @override - String get letsStartChattingLabel => 'Inizia una conversazione!'; - - @override - String get sendingFirstMessageLabel => - 'Che ne dici di mandare il tuo primo messaggio ad un amico?'; - - @override - String get startAChatLabel => 'Inizia una conversazione'; - - @override - String get loadingChannelsError => 'Errore durante il caricamento dei canali'; - - @override - String get deleteConversationLabel => 'Elimina conversazione'; - - @override - String get deleteConversationQuestion => - 'Sei sicuro di voler eliminare questa conversazione?'; - - @override - String get streamChatLabel => 'Stream Chat'; - - @override - String get searchingForNetworkText => 'Cercando una connessione'; - - @override - String get offlineLabel => 'Offline...'; - - @override - String get tryAgainLabel => 'Riprova'; - - @override - String membersCountText(int count) { - if (count == 1) return '1 membro'; - return '$count membri'; - } - - @override - String watchersCountText(int count) { - if (count == 1) return '1 Online'; - return '$count Online'; - } - - @override - String get viewInfoLabel => 'Vedi info'; - - @override - String get leaveGroupLabel => 'Esci dal gruppo'; - - @override - String get leaveLabel => 'ESCI'; - - @override - String get leaveConversationLabel => 'Esci dalla conversazione'; - - @override - String get leaveConversationQuestion => - 'Sei sicuro di voler lasciare questa conversazione?'; - - @override - String get showInChatLabel => 'Mostra nella chat'; - - @override - String get saveImageLabel => 'Salva immagine'; - - @override - String get saveVideoLabel => 'Salva video'; - - @override - String get uploadErrorLabel => 'ERRORE DURANTE IL CARICAMENTO'; - - @override - String get giphyLabel => 'Giphy'; - - @override - String get shuffleLabel => 'Shuffle'; - - @override - String get sendLabel => 'Invia'; - - @override - String get withText => 'con'; - - @override - String get inText => 'in'; - - @override - String get youText => 'te'; - - @override - String galleryPaginationText({ - required int currentPage, - required int totalPages, - }) => - '${currentPage + 1} di $totalPages'; - - @override - String get fileText => 'file'; - - @override - String get replyToMessageLabel => 'Rispondi al messaggio'; - - @override - String attachmentLimitExceedError(int limit) => ''' -Attenzione: il limite massimo di $limit file ĆØ stato superato. - '''; - - @override - String get viewLibrary => 'Vedi la biblioteca'; - - @override - String get slowModeOnLabel => 'Slowmode attiva'; - - @override - String get downloadLabel => 'Scaricamento'; - - @override - String toggleMuteUnmuteUserText({required bool isMuted}) { - if (isMuted) { - return "Attiva l'audio dell'utente"; - } else { - return 'Utente muto'; - } - } - - @override - String toggleMuteUnmuteGroupQuestion({required bool isMuted}) { - if (isMuted) { - return 'Sei sicuro di voler riattivare questo gruppo?'; - } else { - return 'Sei sicuro di voler disattivare questo gruppo?'; - } - } - - @override - String toggleMuteUnmuteUserQuestion({required bool isMuted}) { - if (isMuted) { - return 'Sei sicuro di voler riattivare questo utente?'; - } else { - return 'Sei sicuro di voler silenziare questo utente?'; - } - } - - @override - String toggleMuteUnmuteAction({required bool isMuted}) { - if (isMuted) { - return 'RIATTIVATO'; - } else { - return 'MUTO'; - } - } - - @override - String toggleMuteUnmuteGroupText({required bool isMuted}) { - if (isMuted) { - return 'Riattiva gruppo'; - } else { - return 'Gruppo muto'; - } - } - - @override - String get linkDisabledDetails => - 'Non ĆØ permesso condividere link in questa convesazione.'; - - @override - String get linkDisabledError => 'I links sono disattivati'; - - @override - String unreadMessagesSeparatorText() => 'Nouveaux messages'; - - @override - String get enableFileAccessMessage => "Per favore attiva l'accesso ai file" - '\ncosĆ­ potrai condividerli con i tuoi amici.'; - - @override - String get allowFileAccessMessage => "Consenti l'accesso ai file"; - - @override - String get markAsUnreadLabel => 'Contrassegna come non letto'; - - @override - String unreadCountIndicatorLabel({required int unreadCount}) { - return '$unreadCount non letti'; - } - - @override - String get markUnreadError => - 'Errore durante la marcatura del messaggio come non letto. Impossibile' - ' marcare messaggi non letti più vecchi dei più recenti 100 messaggi' - ' del canale.'; - - @override - String createPollLabel({bool isNew = false}) { - if (isNew) return 'Crea un nuovo sondaggio'; - return 'Crea sondaggio'; - } - - @override - String get questionsLabel => 'Domande'; - - @override - String get askAQuestionLabel => 'Fai una domanda'; - - @override - String? pollQuestionValidationError(int length, Range range) { - final (:min, :max) = range; - - // Check if the question is too short. - if (min != null && length < min) { - return 'La domanda deve essere composta da almeno $min caratteri'; - } - - // Check if the question is too long. - if (max != null && length > max) { - return 'La domanda deve essere lunga al massimo $max caratteri'; - } - - return null; - } - - @override - String optionLabel({bool isPlural = false}) { - if (isPlural) return 'Opzioni'; - return 'Opzione'; - } - - @override - String get pollOptionEmptyError => "L'opzione non può essere vuota"; - - @override - String get pollOptionDuplicateError => "Questa ĆØ giĆ  un'opzione"; - - @override - String get addAnOptionLabel => "Aggiungi un'opzione"; - - @override - String get multipleAnswersLabel => 'Risposte multiple'; - - @override - String get maximumVotesPerPersonLabel => 'Numero massimo di voti per persona'; - - @override - String? maxVotesPerPersonValidationError(int votes, Range range) { - final (:min, :max) = range; - - if (min != null && votes < min) { - return 'Il conteggio dei voti deve essere almeno $min'; - } - - if (max != null && votes > max) { - return 'Il conteggio dei voti deve essere al massimo $max'; - } - - return null; - } - - @override - String get anonymousPollLabel => 'Sondaggio anonimo'; - - @override - String get pollOptionsLabel => 'Opzioni del sondaggio'; - - @override - String get suggestAnOptionLabel => "Suggerisci un'opzione"; - - @override - String get enterANewOptionLabel => 'Inserisci una nuova opzione'; - - @override - String get addACommentLabel => 'Aggiungi un commento'; - - @override - String get pollCommentsLabel => 'Commenti del sondaggio'; - - @override - String get updateYourCommentLabel => 'Aggiorna il tuo commento'; - - @override - String get enterYourCommentLabel => 'Inserisci il tuo commento'; - - @override - String get createLabel => 'Crea'; - - @override - String pollVotingModeLabel(PollVotingMode votingMode) { - return votingMode.when( - disabled: () => 'Votazione terminata', - unique: () => 'Seleziona uno', - limited: (count) => 'Seleziona fino a $count', - all: () => 'Seleziona uno o più', - ); - } - - @override - String seeAllOptionsLabel({int? count}) { - if (count == null) return 'Vedi tutte le opzioni'; - return 'Vedi tutte le $count opzioni'; - } - - @override - String get viewCommentsLabel => 'Visualizza commenti'; - - @override - String get viewResultsLabel => 'Visualizza risultati'; - - @override - String get endVoteLabel => 'Termina votazione'; - - @override - String get pollResultsLabel => 'Risultati del sondaggio'; - - @override - String showAllVotesLabel({int? count}) { - if (count == null) return 'Mostra tutti i voti'; - return 'Mostra tutti i $count voti'; - } - - @override - String voteCountLabel({int? count}) => switch (count) { - null || < 1 => '0 voti', - 1 => '1 voto', - _ => '$count voti', - }; - - @override - String get noPollVotesLabel => 'Attualmente non ci sono voti nel sondaggio'; - - @override - String get loadingPollVotesError => - 'Errore durante il caricamento dei voti del sondaggio'; - - @override - String get repliedToLabel => 'risposto a:'; - - @override - String newThreadsLabel({required int count}) { - if (count == 1) return '1 nuovo thread'; - return '$count nuovi thread'; - } - - @override - String get slideToCancelLabel => 'Scorri per annullare'; - - @override - String get holdToRecordLabel => - 'Tieni premuto per registrare, rilascia per inviare'; -} diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ja.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ja.dart deleted file mode 100644 index 0d384c1817..0000000000 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ja.dart +++ /dev/null @@ -1,599 +0,0 @@ -part of 'stream_chat_localizations.dart'; - -/// The translations for Japanese (`ja`). -class StreamChatLocalizationsJa extends GlobalStreamChatLocalizations { - /// Create an instance of the translation bundle for Japanese. - const StreamChatLocalizationsJa({super.localeName = 'ja'}); - - @override - String get launchUrlError => 'URLć®čµ·å‹•ćŒć§ćć¾ć›ć‚“'; - - @override - String get loadingUsersError => 'ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®čŖ­ćæč¾¼ćæćŒć§ćć¾ć›ć‚“'; - - @override - String get noUsersLabel => 'ē¾åœØć€ćƒ¦ćƒ¼ć‚¶ćƒ¼ćÆć„ć¾ć›ć‚“ć€‚'; - - @override - String get noPhotoOrVideoLabel => 'å†™ēœŸć‚„ćƒ“ćƒ‡ć‚ŖćÆć‚ć‚Šć¾ć›ć‚“'; - - @override - String get retryLabel => 'å†č©¦č”Œ'; - - @override - String get userLastOnlineText => 'å‰å›žć®ć‚Ŗćƒ³ćƒ©ć‚¤ćƒ³'; - - @override - String get userOnlineText => 'ć‚Ŗćƒ³ćƒ©ć‚¤ćƒ³'; - - @override - String userTypingText(Iterable users) { - if (users.isEmpty) return ''; - final first = users.first; - if (users.length == 1) { - return '${first.name}ćŒå…„åŠ›ć—ć¦ć„ć¾ć™'; - } - return '${first.name}と${users.length - 1}äŗŗćŒå…„åŠ›ć—ć¦ć„ć¾ć™'; - } - - @override - String get threadReplyLabel => 'ć‚¹ćƒ¬ćƒƒćƒ‰čæ”äæ”'; - - @override - String get onlyVisibleToYouText => 'č‡Ŗåˆ†ć«ć®ćæč¦‹ćˆć¾ć™'; - - @override - String threadReplyCountText(int count) => '$countć¤ć®ć‚¹ćƒ¬ćƒƒćƒ‰čæ”äæ”'; - - @override - String attachmentsUploadProgressText({ - required int remaining, - required int total, - }) => - '$remaining/${total}mbć®ć‚¢ćƒƒćƒ—ćƒ­ćƒ¼ćƒ‰äø­ā€¦'; - - @override - String pinnedByUserText({ - required User pinnedBy, - required User currentUser, - }) { - final pinnedByCurrentUser = currentUser.id == pinnedBy.id; - if (pinnedByCurrentUser) return 'ć‚ćŖćŸć®ćƒ”ćƒ³'; - return '${pinnedBy.name}ć®ćƒ”ćƒ³'; - } - - @override - String get sendMessagePermissionError => 'ćƒ”ćƒƒć‚»ćƒ¼ć‚øć‚’é€äæ”ć™ć‚‹ęØ©é™ćŒć‚ć‚Šć¾ć›ć‚“'; - - @override - String get emptyMessagesText => 'ē¾åœØć€ćƒ”ćƒƒć‚»ćƒ¼ć‚øćÆć‚ć‚Šć¾ć›ć‚“ć€‚'; - - @override - String get genericErrorText => 'ć‚Øćƒ©ćƒ¼ćŒē™ŗē”Ÿć—ć¾ć—ćŸ'; - - @override - String get loadingMessagesError => 'ćƒ”ćƒƒć‚»ćƒ¼ć‚øć®čŖ­ćæč¾¼ćæć‚Øćƒ©ćƒ¼'; - - @override - String resultCountText(int count) => '$countä»¶ć®ēµęžœ'; - - @override - String get messageDeletedText => 'ć“ć®ćƒ”ćƒƒć‚»ćƒ¼ć‚øćÆå‰Šé™¤ć•ć‚Œć¾ć—ćŸć€‚'; - - @override - String get messageDeletedLabel => 'ćƒ”ćƒƒć‚»ćƒ¼ć‚øå‰Šé™¤'; - - @override - String get editedMessageLabel => 'ē·Øé›†ęøˆćæ'; - - @override - String get messageReactionsLabel => 'ćƒ”ćƒƒć‚»ćƒ¼ć‚øć®ćƒŖć‚¢ć‚Æć‚·ćƒ§ćƒ³'; - - @override - String get emptyChatMessagesText => 'ć¾ć ćƒ”ćƒƒć‚»ćƒ¼ć‚øćÆć‚ć‚Šć¾ć›ć‚“ā€¦'; - - @override - String threadSeparatorText(int replyCount) => '$replyCount件の返俔'; - - @override - String get connectedLabel => 'ęŽ„ē¶šć—ć¦ć„ć¾ć™'; - - @override - String get disconnectedLabel => 'ęŽ„ē¶šåˆ‡ć‚Œ'; - - @override - String get reconnectingLabel => 'å†ęŽ„ē¶šäø­ā€¦'; - - @override - String get alsoSendAsDirectMessageLabel => 'ćƒ€ć‚¤ćƒ¬ć‚Æćƒˆćƒ”ćƒƒć‚»ćƒ¼ć‚øć§ć‚‚é€äæ”'; - - @override - String get addACommentOrSendLabel => 'ć‚³ćƒ”ćƒ³ćƒˆć®čæ½åŠ ć‚„é€äæ”'; - - @override - String get searchGifLabel => 'GIFの検瓢'; - - @override - String get writeAMessageLabel => 'ćƒ”ćƒƒć‚»ćƒ¼ć‚øć‚’ę›øć'; - - @override - String get instantCommandsLabel => 'ć‚¤ćƒ³ć‚¹ć‚æćƒ³ćƒˆć‚³ćƒžćƒ³ćƒ‰'; - - @override - String fileTooLargeAfterCompressionError(double limitInMB) => - 'ćƒ•ć‚”ć‚¤ćƒ«ć®ć‚µć‚¤ć‚ŗćŒå¤§ćć™ćŽć¦ć‚¢ćƒƒćƒ—ćƒ­ćƒ¼ćƒ‰ć§ćć¾ć›ć‚“ć€‚' - 'ćƒ•ć‚”ć‚¤ćƒ«ć‚µć‚¤ć‚ŗć®åˆ¶é™ćÆ${limitInMB}MB恧恙怂' - 'åœ§ēø®ć‚’č©¦ć—ć¾ć—ćŸćŒć‚µć‚¤ć‚ŗć‚’ć‚Ŗćƒ¼ćƒćƒ¼ć—ć¾ć—ćŸ'; - - @override - String fileTooLargeError(double limitInMB) => - 'ćƒ•ć‚”ć‚¤ćƒ«ćŒå¤§ćć™ćŽć¦ć‚¢ćƒƒćƒ—ćƒ­ćƒ¼ćƒ‰ć§ćć¾ć›ć‚“ć€‚ćƒ•ć‚”ć‚¤ćƒ«ć‚µć‚¤ć‚ŗć®åˆ¶é™ćÆ${limitInMB}MB恧恙怂'; - - @override - String get couldNotReadBytesFromFileError => 'ćƒ•ć‚”ć‚¤ćƒ«ć‹ć‚‰ćƒć‚¤ćƒˆć‚’čŖ­ćæå–ć‚Œć¾ć›ć‚“ć§ć—ćŸ'; - - @override - String get addAFileLabel => 'ćƒ•ć‚”ć‚¤ćƒ«ć®čæ½åŠ '; - - @override - String get photoFromCameraLabel => 'ć‚«ćƒ”ćƒ©ć‹ć‚‰ć®å†™ēœŸ'; - - @override - String get uploadAFileLabel => 'ćƒ•ć‚”ć‚¤ćƒ«ć®ć‚¢ćƒƒćƒ—ćƒ­ćƒ¼ćƒ‰'; - - @override - String get uploadAPhotoLabel => 'å†™ēœŸć®ć‚¢ćƒƒćƒ—ćƒ­ćƒ¼ćƒ‰'; - - @override - String get uploadAVideoLabel => 'å‹•ē”»ć®ć‚¢ćƒƒćƒ—ćƒ­ćƒ¼ćƒ‰'; - - @override - String get videoFromCameraLabel => 'ć‚«ćƒ”ćƒ©ć‹ć‚‰ć®å‹•ē”»'; - - @override - String get okLabel => 'OK'; - - @override - String get somethingWentWrongError => 'ć‚Øćƒ©ćƒ¼ćŒē™ŗē”Ÿć—ć¾ć—ćŸ'; - - @override - String get addMoreFilesLabel => 'ćƒ•ć‚”ć‚¤ćƒ«ć®čæ½åŠ '; - - @override - String get enablePhotoAndVideoAccessMessage => 'ćŠå‹é”ćØå…±ęœ‰ć§ćć‚‹ć‚ˆć†ć«ć€å†™ēœŸ' - '\nć‚„ćƒ“ćƒ‡ć‚Ŗćøć®ć‚¢ć‚Æć‚»ć‚¹ć‚’ęœ‰åŠ¹ć«ć—ć¦ćć ć•ć„ć€‚'; - @override - String get allowGalleryAccessMessage => 'ć‚®ćƒ£ćƒ©ćƒŖćƒ¼ćøć®ć‚¢ć‚Æć‚»ć‚¹ć‚’čØ±åÆć™ć‚‹'; - - @override - String get flagMessageLabel => 'ćƒ”ćƒƒć‚»ćƒ¼ć‚øć‚’ćƒ•ćƒ©ć‚°ć™ć‚‹'; - - @override - String get flagMessageQuestion => 'ć“ć®ćƒ”ćƒƒć‚»ćƒ¼ć‚øć®ć‚³ćƒ”ćƒ¼ć‚’' - '\nćƒ¢ćƒ‡ćƒ¬ćƒ¼ć‚æćƒ¼ć«é€ć£ć¦ć€ć•ć‚‰ć«čŖæęŸ»ć—ć¦ć‚‚ć‚‰ć„ć¾ć™ć‹ļ¼Ÿ'; - - @override - String get flagLabel => 'ćƒ•ćƒ©ć‚°ć™ć‚‹'; - - @override - String get cancelLabel => 'ć‚­ćƒ£ćƒ³ć‚»ćƒ«'; - - @override - String get flagMessageSuccessfulLabel => 'ćƒ”ćƒƒć‚»ćƒ¼ć‚øć«ćƒ•ćƒ©ć‚°ćŒä»˜ć‘ć‚‰ć‚Œć¾ć—ćŸ'; - - @override - String get flagMessageSuccessfulText => 'ć“ć®ćƒ”ćƒƒć‚»ćƒ¼ć‚øćÆćƒ¢ćƒ‡ćƒ¬ćƒ¼ć‚æćƒ¼ć«å ±å‘Šć•ć‚Œć¾ć—ćŸć€‚'; - - @override - String get deleteLabel => '削除'; - - @override - String get deleteMessageLabel => 'ćƒ”ćƒƒć‚»ćƒ¼ć‚øć‚’å‰Šé™¤ć™ć‚‹'; - - @override - String get deleteMessageQuestion => 'ć“ć®ćƒ”ćƒƒć‚»ćƒ¼ć‚ø' - '\nć‚’å®Œå…Øć«å‰Šé™¤ć—ć¦ć‚‚ć‚ˆć‚ć—ć„ć§ć™ć‹ļ¼Ÿ'; - - @override - String get operationCouldNotBeCompletedText => 'ę“ä½œć‚’å®Œäŗ†ć§ćć¾ć›ć‚“ć§ć—ćŸć€‚'; - - @override - String get replyLabel => '返俔'; - - @override - String togglePinUnpinText({required bool pinned}) { - if (pinned) return 'ä¼šč©±ć®ćƒ”ćƒ³ć‚’å¤–ć™'; - return 'ä¼šč©±ć‚’ćƒ”ćƒ³ć™ć‚‹'; - } - - @override - String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed}) { - if (isDeleteFailed) return 'ćƒ”ćƒƒć‚»ćƒ¼ć‚øć®å‰Šé™¤ć‚’å†č©¦č”Œć™ć‚‹'; - return 'ćƒ”ćƒƒć‚»ćƒ¼ć‚øć‚’å‰Šé™¤ć™ć‚‹'; - } - - @override - String get copyMessageLabel => 'ćƒ”ćƒƒć‚»ćƒ¼ć‚øć‚’ć‚³ćƒ”ćƒ¼ć™ć‚‹'; - - @override - String get editMessageLabel => 'ćƒ”ćƒƒć‚»ćƒ¼ć‚øć‚’ē·Øé›†ć™ć‚‹'; - - @override - String toggleResendOrResendEditedMessage({required bool isUpdateFailed}) { - if (isUpdateFailed) return 'ē·Øé›†ć—ćŸćƒ”ćƒƒć‚»ćƒ¼ć‚øć‚’å†é€ć™ć‚‹'; - return '再送'; - } - - @override - String get photosLabel => 'å†™ēœŸ'; - - String _getDay(DateTime dateTime) { - final now = DateTime.now(); - final today = DateTime(now.year, now.month, now.day); - final yesterday = DateTime(now.year, now.month, now.day - 1); - - final date = DateTime(dateTime.year, dateTime.month, dateTime.day); - - if (date == today) { - return '今ꗄ'; - } else if (date == yesterday) { - return 'ę˜Øę—„'; - } else { - return '${Jiffy.parseFromDateTime(date).MMMd}恫'; - } - } - - @override - String sentAtText({required DateTime date, required DateTime time}) { - final atTime = Jiffy.parseFromDateTime(time.toLocal()); - return '${_getDay(date)}恮${atTime.jm}ć«é€äæ”ć—ć¾ć—ćŸ '; - } - - @override - String get todayLabel => '今ꗄ'; - - @override - String get yesterdayLabel => 'ę˜Øę—„'; - - @override - String get channelIsMutedText => 'ćƒćƒ£ćƒ³ćƒćƒ«ćŒćƒŸćƒ„ćƒ¼ćƒˆć•ć‚Œć¦ć„ć¾ć™'; - - @override - String get noTitleText => 'ć‚æć‚¤ćƒˆćƒ«ē„”ć—'; - - @override - String get letsStartChattingLabel => 'ćƒćƒ£ćƒƒćƒˆć‚’å§‹ć‚ć‚ˆć†ļ¼'; - - @override - String get sendingFirstMessageLabel => 'ęœ€åˆć®ćƒ”ćƒƒć‚»ćƒ¼ć‚øć‚’é€ć£ć¦ćæć¾ć—ć‚‡ć†'; - - @override - String get startAChatLabel => 'ćƒćƒ£ćƒƒćƒˆć‚’é–‹å§‹ć™ć‚‹'; - - @override - String get loadingChannelsError => 'ćƒćƒ£ćƒćƒ«ć®ćƒ­ćƒ¼ćƒ‰äø­ć«ć‚Øćƒ©ćƒ¼ćŒē™ŗē”Ÿć—ć¾ć—ćŸ'; - - @override - String get deleteConversationLabel => 'ä¼šč©±ć‚’å‰Šé™¤ć™ć‚‹'; - - @override - String get deleteConversationQuestion => 'ęœ¬å½“ć«ä¼šč©±ć‚’å‰Šé™¤ć—ć¾ć™ć‹ļ¼Ÿ'; - - @override - String get streamChatLabel => 'ć‚¹ćƒˆćƒŖćƒ¼ćƒ ćƒćƒ£ćƒƒćƒˆ'; - - @override - String get searchingForNetworkText => 'ćƒćƒƒćƒˆćƒÆćƒ¼ć‚Æć‚’ę¤œē“¢äø­'; - - @override - String get offlineLabel => 'ć‚Ŗćƒ•ćƒ©ć‚¤ćƒ³ā€¦'; - - @override - String get tryAgainLabel => 'å†č©¦č”Œć™ć‚‹'; - - @override - String membersCountText(int count) => '$count人のピンバー'; - - @override - String watchersCountText(int count) => '$countäŗŗćŒć‚Ŗćƒ³ćƒ©ć‚¤ćƒ³'; - - @override - String get viewInfoLabel => 'ęƒ…å ±ć‚’č¦‹ć‚‹'; - - @override - String get leaveGroupLabel => 'ć‚°ćƒ«ćƒ¼ćƒ—ć‹ć‚‰é€€å‡ŗć™ć‚‹'; - - @override - String get leaveLabel => '退出する'; - - @override - String get leaveConversationLabel => 'ä¼šč©±ć‹ć‚‰é€€å‡ŗć™ć‚‹'; - - @override - String get leaveConversationQuestion => 'ęœ¬å½“ć«ä¼šč©±ć‹ć‚‰é€€å‡ŗć—ć¾ć™ć‹ļ¼Ÿ'; - - @override - String get showInChatLabel => 'チャットで蔨示'; - - @override - String get saveImageLabel => 'ē”»åƒć‚’äæå­˜'; - - @override - String get saveVideoLabel => 'ćƒ“ćƒ‡ć‚Ŗć‚’äæå­˜'; - - @override - String get uploadErrorLabel => 'ć‚¢ćƒƒćƒ—ćƒ­ćƒ¼ćƒ‰ć‚Øćƒ©ćƒ¼'; - - @override - String get giphyLabel => 'GIPHY'; - - @override - String get shuffleLabel => 'ćƒŸćƒƒć‚Æć‚¹'; - - @override - String get sendLabel => '送俔'; - - @override - String get withText => 'と'; - - @override - String get inText => '恫'; - - // This is the word for 'customer' or 'user' because saying 'you' directly - //is too informal and rude - @override - String get youText => 'ć‚ćŖćŸ'; - - @override - String galleryPaginationText({ - required int currentPage, - required int totalPages, - }) => - '${currentPage + 1} / $totalPages'; - - @override - String get fileText => 'ćƒ•ć‚”ć‚¤ćƒ«'; - - @override - String get replyToMessageLabel => 'ćƒ”ćƒƒć‚»ćƒ¼ć‚øć«čæ”äæ”'; - - @override - String get slowModeOnLabel => 'ć‚¹ćƒ­ćƒ¼ćƒ¢ćƒ¼ćƒ‰ć‚Ŗćƒ³'; - - @override - String get viewLibrary => 'ćƒ©ć‚¤ćƒ–ćƒ©ćƒŖć‚’č”Øē¤ŗ'; - - @override - String attachmentLimitExceedError(int limit) => ''' -ę·»ä»˜ćƒ•ć‚”ć‚¤ćƒ«ć®åˆ¶é™ć‚’č¶…ćˆć¾ć—ćŸļ¼š$limitå€‹ć®ćƒ•ć‚”ć‚¤ćƒ«ä»„äøŠć‚’ę·»ä»˜ć™ć‚‹ć“ćØćÆć§ćć¾ć›ć‚“ - '''; - - @override - String get downloadLabel => 'ćƒ€ć‚¦ćƒ³ćƒ­ćƒ¼ćƒ‰'; - - @override - String toggleMuteUnmuteUserText({required bool isMuted}) { - if (isMuted) { - return 'ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®ćƒŸćƒ„ćƒ¼ćƒˆć‚’č§£é™¤ć™ć‚‹'; - } else { - return 'ćƒ¦ćƒ¼ć‚¶ćƒ¼ć‚’ćƒŸćƒ„ćƒ¼ćƒˆ'; - } - } - - @override - String toggleMuteUnmuteGroupQuestion({required bool isMuted}) { - if (isMuted) { - return 'ć“ć®ć‚°ćƒ«ćƒ¼ćƒ—ć®ćƒŸćƒ„ćƒ¼ćƒˆć‚’č§£é™¤ć—ć¦ć‚‚ć‚ˆć‚ć—ć„ć§ć™ć‹ļ¼Ÿ'; - } else { - return 'ć“ć®ć‚°ćƒ«ćƒ¼ćƒ—ć‚’ćƒŸćƒ„ćƒ¼ćƒˆć—ć¦ć‚‚ć‚ˆć‚ć—ć„ć§ć™ć‹ļ¼Ÿ'; - } - } - - @override - String toggleMuteUnmuteUserQuestion({required bool isMuted}) { - if (isMuted) { - return 'ć“ć®ćƒ¦ćƒ¼ć‚¶ćƒ¼ć®ćƒŸćƒ„ćƒ¼ćƒˆć‚’č§£é™¤ć—ć¦ć‚‚ć‚ˆć‚ć—ć„ć§ć™ć‹ļ¼Ÿ'; - } else { - return 'ć“ć®ćƒ¦ćƒ¼ć‚¶ćƒ¼ć‚’ćƒŸćƒ„ćƒ¼ćƒˆć—ć¦ć‚‚ć‚ˆć‚ć—ć„ć§ć™ć‹ļ¼Ÿ'; - } - } - - @override - String toggleMuteUnmuteAction({required bool isMuted}) { - if (isMuted) { - return 'ćƒŸćƒ„ćƒ¼ćƒˆć‚’č§£é™¤ć™ć‚‹'; - } else { - return 'ミツート'; - } - } - - @override - String toggleMuteUnmuteGroupText({required bool isMuted}) { - if (isMuted) { - return 'ć‚°ćƒ«ćƒ¼ćƒ—ć®ćƒŸćƒ„ćƒ¼ćƒˆć‚’č§£é™¤'; - } else { - return 'ćƒŸćƒ„ćƒ¼ćƒˆć‚°ćƒ«ćƒ¼ćƒ—'; - } - } - - @override - String get linkDisabledDetails => 'ć“ć®ä¼šč©±ć§ćÆć€ćƒŖćƒ³ć‚Æć®é€äæ”ćÆčØ±åÆć•ć‚Œć¦ć„ć¾ć›ć‚“ć€‚'; - - @override - String get linkDisabledError => 'ćƒŖćƒ³ć‚ÆćŒē„”åŠ¹ć«ćŖć£ć¦ć„ć¾ć™'; - - @override - String unreadMessagesSeparatorText() => 'ę–°ć—ć„ćƒ”ćƒƒć‚»ćƒ¼ć‚øć€‚'; - - @override - String get enableFileAccessMessage => - 'å‹é”ćØå…±ęœ‰ć§ćć‚‹ć‚ˆć†ć«ć€' '\nćƒ•ć‚”ć‚¤ćƒ«ćøć®ć‚¢ć‚Æć‚»ć‚¹ć‚’ęœ‰åŠ¹ć«ć—ć¦ćć ć•ć„ć€‚'; - - @override - String get allowFileAccessMessage => 'ćƒ•ć‚”ć‚¤ćƒ«ćøć®ć‚¢ć‚Æć‚»ć‚¹ć‚’čØ±åÆć™ć‚‹'; - - @override - String get markAsUnreadLabel => 'ęœŖčŖ­ćØć—ć¦ćƒžćƒ¼ć‚Æ'; - - @override - String unreadCountIndicatorLabel({required int unreadCount}) { - return '$unreadCount 未読'; - } - - @override - String get markUnreadError => - 'ćƒ”ćƒƒć‚»ćƒ¼ć‚øć‚’ęœŖčŖ­ć«ć™ć‚‹éš›ć«ć‚Øćƒ©ćƒ¼ćŒē™ŗē”Ÿć—ć¾ć—ćŸć€‚ęœ€ę–°ć®100ä»¶ć®ćƒćƒ£ćƒ³ćƒćƒ«ćƒ”ćƒƒć‚»ćƒ¼ć‚øć‚ˆć‚Šå¤ć„ęœŖčŖ­ćƒ”ćƒƒć‚»ćƒ¼ć‚øćÆćƒžćƒ¼ć‚Æć§ćć¾ć›ć‚“ć€‚'; - - @override - String createPollLabel({bool isNew = false}) { - if (isNew) return 'ę–°ć—ć„ęŠ•ē„Øć‚’ä½œęˆć™ć‚‹'; - return 'ęŠ•ē„Øć®ä½œęˆ'; - } - - @override - String get questionsLabel => '問'; - - @override - String get askAQuestionLabel => 'č³Ŗå•ć™ć‚‹'; - - @override - String? pollQuestionValidationError(int length, Range range) { - final (:min, :max) = range; - - // Check if the question is too short. - if (min != null && length < min) { - return 'č³Ŗå•ćÆ $min ę–‡å­—ä»„äøŠć§ć‚ć‚‹åæ…č¦ćŒć‚ć‚Šć¾ć™'; - } - - // Check if the question is too long. - if (max != null && length > max) { - return 'č³Ŗå•ć®é•·ć•ćÆęœ€å¤§$maxę–‡å­—ć«ć™ć‚‹åæ…č¦ćŒć‚ć‚Šć¾ć™'; - } - - return null; - } - - @override - String optionLabel({bool isPlural = false}) { - if (isPlural) return 'ć‚Ŗćƒ—ć‚·ćƒ§ćƒ³'; - return 'ć‚Ŗćƒ—ć‚·ćƒ§ćƒ³'; - } - - @override - String get pollOptionEmptyError => 'ć‚Ŗćƒ—ć‚·ćƒ§ćƒ³ć‚’ē©ŗć«ć™ć‚‹ć“ćØćÆć§ćć¾ć›ć‚“'; - - @override - String get pollOptionDuplicateError => 'ć“ć‚ŒćÆć™ć§ć«ć‚Ŗćƒ—ć‚·ćƒ§ćƒ³ć§ć™'; - - @override - String get addAnOptionLabel => 'ć‚Ŗćƒ—ć‚·ćƒ§ćƒ³ć‚’čæ½åŠ ć™ć‚‹'; - - @override - String get multipleAnswersLabel => 'č¤‡ę•°ć®å›žē­”'; - - @override - String get maximumVotesPerPersonLabel => 'äø€äŗŗå½“ćŸć‚Šć®ęœ€å¤§ęŠ•ē„Øę•°'; - - @override - String? maxVotesPerPersonValidationError(int votes, Range range) { - final (:min, :max) = range; - - if (min != null && votes < min) { - return 'ęŠ•ē„Øę•°ćÆ$minä»„äøŠć§ć‚ć‚‹åæ…č¦ćŒć‚ć‚Šć¾ć™'; - } - - if (max != null && votes > max) { - return 'ęŠ•ē„Øę•°ćÆęœ€å¤§$maxē„Øć§ćŖć‘ć‚Œć°ćŖć‚Šć¾ć›ć‚“'; - } - - return null; - } - - @override - String get anonymousPollLabel => 'åŒæåęŠ•ē„Ø'; - - @override - String get pollOptionsLabel => 'ęŠ•ē„Øć‚Ŗćƒ—ć‚·ćƒ§ćƒ³'; - - @override - String get suggestAnOptionLabel => 'ć‚Ŗćƒ—ć‚·ćƒ§ćƒ³ć‚’ęę”ˆ'; - - @override - String get enterANewOptionLabel => 'ę–°ć—ć„ć‚Ŗćƒ—ć‚·ćƒ§ćƒ³ć‚’å…„åŠ›'; - - @override - String get addACommentLabel => 'ć‚³ćƒ”ćƒ³ćƒˆć‚’čæ½åŠ '; - - @override - String get pollCommentsLabel => 'ęŠ•ē„Øć‚³ćƒ”ćƒ³ćƒˆ'; - - @override - String get updateYourCommentLabel => 'ć‚³ćƒ”ćƒ³ćƒˆć‚’ę›“ę–°'; - - @override - String get enterYourCommentLabel => 'ć‚³ćƒ”ćƒ³ćƒˆć‚’å…„åŠ›'; - - @override - String get createLabel => '作成'; - - @override - String pollVotingModeLabel(PollVotingMode votingMode) { - return votingMode.when( - disabled: () => 'ęŠ•ē„Øēµ‚äŗ†', - unique: () => '1ć¤ć‚’éøęŠž', - limited: (count) => 'ęœ€å¤§ $count éøęŠž', - all: () => '1ć¤ä»„äøŠć‚’éøęŠž', - ); - } - - @override - String seeAllOptionsLabel({int? count}) { - if (count == null) return 'ć™ć¹ć¦ć®ć‚Ŗćƒ—ć‚·ćƒ§ćƒ³ć‚’č”Øē¤ŗ'; - return 'すべての $count ć‚Ŗćƒ—ć‚·ćƒ§ćƒ³ć‚’č”Øē¤ŗ'; - } - - @override - String get viewCommentsLabel => 'ć‚³ćƒ”ćƒ³ćƒˆć‚’č”Øē¤ŗ'; - - @override - String get viewResultsLabel => 'ēµęžœć‚’č”Øē¤ŗ'; - - @override - String get endVoteLabel => 'ęŠ•ē„Øć‚’ēµ‚äŗ†'; - - @override - String get pollResultsLabel => 'ęŠ•ē„Øēµęžœ'; - - @override - String showAllVotesLabel({int? count}) { - if (count == null) return 'ć™ć¹ć¦ć®ęŠ•ē„Øć‚’č”Øē¤ŗ'; - return 'すべての $count ęŠ•ē„Øć‚’č”Øē¤ŗ'; - } - - @override - String voteCountLabel({int? count}) => switch (count) { - null || < 1 => '0 焨', - 1 => '1 焨', - _ => '$count 焨', - }; - - @override - String get noPollVotesLabel => 'ē¾åœØęŠ•ē„ØćÆć‚ć‚Šć¾ć›ć‚“'; - - @override - String get loadingPollVotesError => 'ęŠ•ē„Øć®čŖ­ćæč¾¼ćæć‚Øćƒ©ćƒ¼'; - - @override - String get repliedToLabel => 'čæ”äæ”å…ˆ:'; - - @override - String newThreadsLabel({required int count}) { - return '$count ä»¶ć®ę–°ć—ć„ć‚¹ćƒ¬ćƒƒćƒ‰'; - } - - @override - String get slideToCancelLabel => 'ć‚¹ćƒ©ć‚¤ćƒ‰ć§ć‚­ćƒ£ćƒ³ć‚»ćƒ«'; - - @override - String get holdToRecordLabel => 'é•·ęŠ¼ć—ć§éŒ²éŸ³ć€é›¢ć™ćØé€äæ”'; -} diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ko.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ko.dart deleted file mode 100644 index 970f5b2450..0000000000 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ko.dart +++ /dev/null @@ -1,600 +0,0 @@ -part of 'stream_chat_localizations.dart'; - -/// The translations for Korean (`ko`). -class StreamChatLocalizationsKo extends GlobalStreamChatLocalizations { - /// Create an instance of the translation bundle for Korean. - const StreamChatLocalizationsKo({super.localeName = 'ko'}); - - @override - String get launchUrlError => 'URLģ„ ģ‹œģž‘ķ•  수 ģ—†ģŠµė‹ˆė‹¤'; - - @override - String get loadingUsersError => 'ģ‚¬ģš©ģžė„¼ ė”œė“œķ•˜ėŠ” 중 오넘 ė°œģƒ'; - - @override - String get noUsersLabel => 'ķ˜„ģž¬ ģ‚¬ģš©ģžź°€ ģ—†ģŠµė‹ˆė‹¤'; - - @override - String get noPhotoOrVideoLabel => 'ģ‚¬ģ§„ģ“ė‚˜ ė™ģ˜ģƒģ“ ģ—†ģŠµė‹ˆė‹¤'; - - @override - String get retryLabel => 'ė‹¤ģ‹œ ģ‹œė„ķ•˜ģ‹­ģ‹œģ˜¤'; - - @override - String get userLastOnlineText => 'ė§ˆģ§€ė§‰ ģ˜Øė¼ģøģž…ė‹ˆė‹¤'; - - @override - String get userOnlineText => 'ģ˜Øė¼ģø'; - - @override - String userTypingText(Iterable users) { - if (users.isEmpty) return ''; - final first = users.first; - if (users.length == 1) { - return '${first.name} ķƒ€ģ“ķ•‘ģ¤‘'; - } - return '${first.name}ķ•˜ź³  ${users.length - 1}ėŖ… ķƒ€ģ“ķ•‘ģ¤‘'; - } - - @override - String get threadReplyLabel => 'ģŠ¤ė ˆė“œ ģ‘ė‹µģž…ė‹ˆė‹¤'; - - @override - String get onlyVisibleToYouText => 'ė‹¹ģ‹ ė§Œ ė³¼ 수 ģžˆģŠµė‹ˆė‹¤'; - - @override - String threadReplyCountText(int count) => '$countģŠ¤ė ˆė“œ ė‹µģž„'; - - @override - String attachmentsUploadProgressText({ - required int remaining, - required int total, - }) => - '$remaining/${total}mb넼 ģ—…ė”œė“œģ¤‘...'; - - @override - String pinnedByUserText({ - required User pinnedBy, - required User currentUser, - }) { - final pinnedByCurrentUser = currentUser.id == pinnedBy.id; - if (pinnedByCurrentUser) return 'ė‹¹ģ‹ ģ˜ ķ•€'; - return '${pinnedBy.name}ģ˜ ķ•€'; - } - - @override - String get sendMessagePermissionError => 'ė©”ģ‹œģ§€ė„¼ 볓낼 수 ģžˆėŠ” ź¶Œķ•œģ“ ģ—†ģŠµė‹ˆė‹¤'; - - @override - String get emptyMessagesText => 'ķ˜„ģž¬ ė©”ģ‹œģ§€ź°€ ģ—†ģŠµė‹ˆė‹¤'; - - @override - String get genericErrorText => '뭔가 ģž˜ėŖ»ėģŠµė‹ˆė‹¤'; - - @override - String get loadingMessagesError => 'ė©”ģ‹œģ§€ė„¼ ė”œė“œķ•˜ėŠ” ė™ģ•ˆ ģ˜¤ė„˜ź°€ ė°œģƒķ–ˆģŠµė‹ˆė‹¤'; - - @override - String resultCountText(int count) => '$countź°œģ˜ ź²°ź³¼'; - - @override - String get messageDeletedText => 'ģ“ ė©”ģ‹œģ§€ėŠ” ģ‚­ģ œė˜ģ—ˆģŠµė‹ˆė‹¤.'; - - @override - String get messageDeletedLabel => 'ė©”ģ‹œģ§€ź°€ ģ‚­ģ œė˜ģ—ˆģŠµė‹ˆė‹¤'; - - @override - String get editedMessageLabel => 'ķŽøģ§‘ėØ'; - - @override - String get messageReactionsLabel => 'ė©”ģ‹œģ§€ģ— ėŒ€ķ•œ ģ‘ė‹µ'; - - @override - String get emptyChatMessagesText => '아직 ģ±„ķŒ…ģ“ ģ—†ģŠµė‹ˆė‹¤...'; - - @override - String threadSeparatorText(int replyCount) => '$replyCountź°œģ˜ ė‹µģž„'; - - @override - String get connectedLabel => '연결중'; - - @override - String get disconnectedLabel => 'ģ—°ź²°ģ“ ėŠź²¼ģŠµė‹ˆė‹¤'; - - @override - String get reconnectingLabel => 'ė‹¤ģ‹œ ģ—°ź²°ķ•˜ėŠ” 중...'; - - @override - String get alsoSendAsDirectMessageLabel => 'ė‹¤ģ“ė ‰ķŠø ė©”ģ‹œģ§€ė”œė„ ė³“ėƒ…ė‹ˆė‹¤'; - - @override - String get addACommentOrSendLabel => 'ģ£¼ģ„ģ„ ģ¶”ź°€ķ•˜ź±°ė‚˜ ė³“ėƒ…ė‹ˆė‹¤'; - - @override - String get searchGifLabel => 'GIF ź²€ģƒ‰'; - - @override - String get writeAMessageLabel => 'ė©”ģ‹œģ§€ ģ“°źø°'; - - @override - String get instantCommandsLabel => 'ģøģŠ¤ķ„“ķŠø 커맨즈'; - - @override - String fileTooLargeAfterCompressionError(double limitInMB) => - 'ķŒŒģ¼ģ“ ė„ˆė¬“ ģ»¤ģ„œ ģ—…ė”œė“œķ•  수 ģ—†ģŠµė‹ˆė‹¤. ' - 'ķŒŒģ¼ 크기 ģ œķ•œģ€ ${limitInMB}MBģž…ė‹ˆė‹¤. ' - 'ģš°ė¦¬ėŠ” ģ••ģ¶•ķ•“ ė³“ģ•˜ģ§€ė§Œ ģ¶©ė¶„ķ•˜ģ§€ ģ•Šģ•˜ģŠµė‹ˆė‹¤.'; - - @override - String fileTooLargeError(double limitInMB) => - 'ķŒŒģ¼ģ“ ė„ˆė¬“ ģ»¤ģ„œ ģ—…ė”œė“œķ•  수 ģ—†ģŠµė‹ˆė‹¤. ķŒŒģ¼ 크기 ģ œķ•œģ€ ${limitInMB}MBģž…ė‹ˆė‹¤.'; - - @override - String get couldNotReadBytesFromFileError => 'ķŒŒģ¼ģ—ģ„œ ė°”ģ“ķŠøė„¼ ģ½ģ„ 수 ģ—†ģŠµė‹ˆė‹¤.'; - - @override - String get addAFileLabel => 'ķŒŒģ¼ģ„ 추가함'; - - @override - String get photoFromCameraLabel => 'ģ¹“ė©”ė¼ģ—ģ„œ ģ°ģ€ 사진'; - - @override - String get uploadAFileLabel => 'ķŒŒģ¼ģ„ ģ—…ė”œė“œķ•Ø'; - - @override - String get uploadAPhotoLabel => 'ģ‚¬ģ§„ģ„ ģ—…ė”œė“œķ•Ø'; - - @override - String get uploadAVideoLabel => 'ė¹„ė””ģ˜¤ė„¼ ģ—…ė”œė“œķ•Ø'; - - @override - String get videoFromCameraLabel => 'ģ¹“ė©”ė¼ģ˜ ė¹„ė””ģ˜¤.'; - - @override - String get okLabel => 'ķ™•ģø'; - - @override - String get somethingWentWrongError => '뭔가 ģž˜ėŖ»ėģŠµėŠė‹¤'; - - @override - String get addMoreFilesLabel => 'ķŒŒģ¼ģ„ 추가함'; - - @override - String get enablePhotoAndVideoAccessMessage => 'ģ¹œźµ¬ģ™€ ź³µģœ ķ•  수 ģžˆė„ė” 사진과' - '\nė™ģ˜ģƒģ— ģ•”ģ„øģŠ¤ķ•  수 ģžˆė„ė” ģ„¤ģ •ķ•˜ģ‹­ģ‹œģ˜¤.'; - - @override - String get allowGalleryAccessMessage => 'ź°¤ėŸ¬ė¦¬ģ— ėŒ€ķ•œ ģ•”ģ„øģŠ¤ė„¼ ķ—ˆģš©ķ•©ė‹ˆė‹¤'; - - @override - String get flagMessageLabel => ' ė©”ģ‹œģ§€ė„¼ ķ”Œėž˜ź·øķ•Ø'; - - @override - String get flagMessageQuestion => '추가 씰사넼 ģœ„ķ•“ ģ§„ķ–‰ģžģ—ź²Œ ģ“ ė©”ģ‹œģ§€ģ˜ ė³µģ‚¬ė³øģ„ ģ „ģ†”ķ•˜ģ‹œź² ģŠµė‹ˆź¹Œ?'; - - @override - String get flagLabel => 'ķ”Œėž˜ź·øķ•Ø'; - - @override - String get cancelLabel => 'ģ·Øģ†Œ'; - - @override - String get flagMessageSuccessfulLabel => 'ė©”ģ‹œģ§€ģ— ķ”Œėž˜ź·øź°€ ģ§€ģ •ė˜ģ—ˆģŠµė‹ˆė‹¤'; - - @override - String get flagMessageSuccessfulText => 'ė©”ģ‹œģ§€ź°€ ģ§„ķ–‰ģžģ—ź²Œ ė³“ź³ ė˜ģ—ˆģŠµė‹ˆė‹¤.'; - - @override - String get deleteLabel => 'ģ‚­ģ œ'; - - @override - String get deleteMessageLabel => 'ė©”ģ‹œģ§€ė„¼ ģ‚­ģ œķ•©ė‹ˆė‹¤.'; - - @override - String get deleteMessageQuestion => 'ģ“ ė©”ģ‹œģ§€ė„¼ ģ™„ģ „ķžˆ ģ‚­ģ œķ•˜ģ‹œź² ģŠµė‹ˆź¹Œ?'; - - @override - String get operationCouldNotBeCompletedText => 'ģž‘ģ—…ģ„ ģ™„ė£Œķ•  수 ģ—†ģŠµė‹ˆė‹¤.'; - - @override - String get replyLabel => '답글'; - - @override - String togglePinUnpinText({required bool pinned}) { - if (pinned) return 'ėŒ€ķ™”ģ˜ ķ•€ģ„ ė¶„ė¦¬ķ•©ė‹ˆė‹¤'; - return 'ėŒ€ķ™”ģ— ź³ ģ •ķ•©ė‹ˆė‹¤'; - } - - @override - String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed}) { - if (isDeleteFailed) return 'ė©”ģ‹œģ§€ ģ‚­ģ œė„¼ ė‹¤ģ‹œ ģ‹œė„ķ•©ė‹ˆė‹¤'; - return 'ė©”ģ‹œģ§€ė„¼ ģ‚­ģ œķ•©ė‹ˆė‹¤'; - } - - @override - String get copyMessageLabel => 'ė©”ģ‹œģ§€ė„¼ ė³µģ‚¬ķ•©ė‹ˆė‹¤.'; - - @override - String get editMessageLabel => 'ė©”ģ‹œģ§€ė„¼ ķŽøģ§‘ķ•©ė‹ˆė‹¤.'; - - @override - String toggleResendOrResendEditedMessage({required bool isUpdateFailed}) { - if (isUpdateFailed) return 'ķŽøģ§‘ėœ ė©”ģ‹œģ§€ė„¼ ė‹¤ģ‹œ ė³“ėƒ…ė‹ˆė‹¤.'; - return 'ė‹¤ģ‹œ ė³“ėƒ…ė‹ˆė‹¤.'; - } - - @override - String get photosLabel => '사진'; - - String _getDay(DateTime dateTime) { - final now = DateTime.now(); - final today = DateTime(now.year, now.month, now.day); - final yesterday = DateTime(now.year, now.month, now.day - 1); - - final date = DateTime(dateTime.year, dateTime.month, dateTime.day); - - if (date == today) { - return '오늘'; - } else if (date == yesterday) { - return 'ģ–“ģ œ'; - } else { - return '${Jiffy.parseFromDateTime(date).MMMd}에'; - } - } - - @override - String sentAtText({required DateTime date, required DateTime time}) { - final atTime = Jiffy.parseFromDateTime(time.toLocal()); - return '${_getDay(date)} ${atTime.jm}에 ė³“ėƒˆģŠµė‹ˆė‹¤'; - } - - @override - String get todayLabel => '오늘'; - - @override - String get yesterdayLabel => 'ģ–“ģ œ'; - - @override - String get channelIsMutedText => 'ģ±„ė„ģ“ ģŒģ†Œź±°ė©ė‹ˆė‹¤.'; - - @override - String get noTitleText => 'ģ œėŖ©ģ“ ģ—†ģŠµė‹ˆė‹¤.'; - - @override - String get letsStartChattingLabel => 'ģ±„ķŒ… ģ‹œģž‘ķ•“ģš”!'; - - @override - String get sendingFirstMessageLabel => 'ģ¹œźµ¬ģ—ź²Œ 첫 ė©”ģ‹œģ§€ė„¼ 볓낓 ė³¼ź¹Œģš”?'; - - @override - String get startAChatLabel => 'ėŒ€ķ™”ė„¼ ģ‹œģž‘ķ•©ė‹ˆė‹¤.'; - - @override - String get loadingChannelsError => 'ģ±„ė„ģ„ ė”œė“œķ•˜ėŠ” ė™ģ•ˆ ģ˜¤ė„˜ź°€ ė°œģƒķ–ˆģŠµė‹ˆė‹¤.'; - - @override - String get deleteConversationLabel => 'ėŒ€ķ™”ė„¼ ģ‚­ģ œķ•©ė‹ˆė‹¤.'; - - @override - String get deleteConversationQuestion => 'ėŒ€ķ™”ė„¼ ģ‚­ģ œķ•˜ģ‹œź² ģŠµė‹ˆź¹Œ?'; - - @override - String get streamChatLabel => '스트림 ģ±„ķŒ…'; - - @override - String get searchingForNetworkText => 'ė„¤ķŠøģ›Œķ¬ė„¼ ź²€ģƒ‰ķ•˜ėŠ” ģ¤‘ģž…ė‹ˆė‹¤.'; - - @override - String get offlineLabel => 'ģ˜¤ķ”„ė¼ģø...'; - - @override - String get tryAgainLabel => 'ė‹¤ģ‹œ ģ‹œė„ķ•©ė‹ˆė‹¤'; - - @override - String membersCountText(int count) => '$countėŖ…'; - - @override - String watchersCountText(int count) => '$countėŖ…ģ“ ģ˜Øė¼ģø'; - - @override - String get viewInfoLabel => '정볓넼 볓기'; - - @override - String get leaveGroupLabel => 'ź·øė£¹ģ„ ė– ė‚©ė‹ˆė‹¤.'; - - @override - String get leaveLabel => 'ė– ė‚˜ė‹¤'; - - @override - String get leaveConversationLabel => 'ėŒ€ķ™”ģ—ģ„œ ė– ė‚©ė‹ˆė‹¤.'; - - @override - String get leaveConversationQuestion => '정말 ģ“ ėŒ€ķ™”ģ—ģ„œ ė‚˜ź°€ģ‹œź² ģŠµė‹ˆź¹Œ?'; - - @override - String get showInChatLabel => 'ģ±„ķŒ…ģ— ķ‘œģ‹œķ•©ė‹ˆė‹¤.'; - - @override - String get saveImageLabel => 'ģ“ėÆøģ§€ė„¼ ģ €ģž„ķ•©ė‹ˆė‹¤.'; - - @override - String get saveVideoLabel => 'ė¹„ė””ģ˜¤ė„¼ ģ €ģž„ķ•©ė‹ˆė‹¤.'; - - @override - String get uploadErrorLabel => 'ģ—…ė”œė“œ 오넘'; - - @override - String get giphyLabel => '지피'; - - @override - String get shuffleLabel => 'ģ„žźø°'; - - @override - String get sendLabel => '볓낓기'; - - @override - String get withText => 'ķ•Øź»˜'; - - @override - String get inText => '에'; - - // This is the word for 'customer' or 'user' because saying 'you' directly - // is too informal and rude - @override - String get youText => '당신'; - - @override - String galleryPaginationText({ - required int currentPage, - required int totalPages, - }) => - '${currentPage + 1} / $totalPages'; - - //3 / 11 - - @override - String get fileText => 'ķŒŒģ¼'; - - @override - String get replyToMessageLabel => 'ė©”ģ‹œģ§€ģ— ķšŒģ‹ ķ•©ė‹ˆė‹¤.'; - - @override - String get slowModeOnLabel => 'ģŠ¬ė”œėŖØė“œ 켜짐'; - - @override - @override - String get viewLibrary => 'ė¼ģ“ėøŒėŸ¬ė¦¬ 볓기'; - - @override - String attachmentLimitExceedError(int limit) => - '첨부 ķŒŒģ¼ ģ œķ•œ 쓈과: $limit ģ“ģƒģ˜ 첨부 ķŒŒģ¼ģ„ 추가할 수 ģ—†ģŠµė‹ˆė‹¤'; - - @override - String get downloadLabel => 'ė‹¤ģš“ė”œė“œ'; - - @override - String toggleMuteUnmuteUserText({required bool isMuted}) { - if (isMuted) { - return 'ģ‚¬ģš©ģž ģŒģ†Œź±° ķ•“ģ œ'; - } else { - return 'ģ‚¬ģš©ģž ģŒģ†Œź±°'; - } - } - - @override - String toggleMuteUnmuteGroupQuestion({required bool isMuted}) { - if (isMuted) { - return 'ģ“ ź·øė£¹ģ˜ ģŒģ†Œź±°ė„¼ ķ•“ģ œķ•˜ģ‹œź² ģŠµė‹ˆź¹Œ?'; - } else { - return 'ģ“ ź·øė£¹ģ„ ģŒģ†Œź±°ķ•˜ģ‹œź² ģŠµė‹ˆź¹Œ?'; - } - } - - @override - String toggleMuteUnmuteUserQuestion({required bool isMuted}) { - if (isMuted) { - return 'ģ“ ģ‚¬ģš©ģžģ˜ ģŒģ†Œź±°ė„¼ ķ•“ģ œķ•˜ģ‹œź² ģŠµė‹ˆź¹Œ?'; - } else { - return 'ģ“ ģ‚¬ģš©ģžė„¼ ģŒģ†Œź±°ķ•˜ģ‹œź² ģŠµė‹ˆź¹Œ?'; - } - } - - @override - String toggleMuteUnmuteAction({required bool isMuted}) { - if (isMuted) { - return 'ģŒģ†Œź±° ķ•“ģ œ'; - } else { - return 'ė¬“ģŒ'; - } - } - - @override - String toggleMuteUnmuteGroupText({required bool isMuted}) { - if (isMuted) { - return '그룹 ģŒģ†Œź±° ķ•“ģ œ'; - } else { - return 'ģŒģ†Œź±° 그룹'; - } - } - - @override - String get linkDisabledDetails => 'ģ“ ėŒ€ķ™”ģ—ģ„œėŠ” 링크넼 볓낼 수 ģ—†ģŠµė‹ˆė‹¤.'; - - @override - String get linkDisabledError => '링크가 ė¹„ķ™œģ„±ķ™”ė˜ģ—ˆģŠµė‹ˆė‹¤.'; - - @override - String unreadMessagesSeparatorText() => '새 ė©”ģ‹œģ§€.'; - - @override - String get enableFileAccessMessage => 'ģ¹œźµ¬ģ™€ ź³µģœ ķ•  수 ģžˆė„ė” ķŒŒģ¼ģ— ėŒ€ķ•œ ģ•”ģ„øģŠ¤ė„¼ ķ—ˆģš©ķ•˜ģ„øģš”.'; - - @override - String get allowFileAccessMessage => 'ķŒŒģ¼ģ— ėŒ€ķ•œ ģ•”ģ„øģŠ¤ ķ—ˆģš©'; - - @override - String get markAsUnreadLabel => 'ģ½ģ§€ ģ•ŠģŒģœ¼ė”œ ķ‘œģ‹œ'; - - @override - String unreadCountIndicatorLabel({required int unreadCount}) { - return '$unreadCount ģ½ģ§€ ģ•ŠģŒ'; - } - - @override - String get markUnreadError => - 'ė©”ģ‹œģ§€ė„¼ ģ½ģ§€ ģ•ŠģŒģœ¼ė”œ ķ‘œģ‹œķ•˜ėŠ” 중 ģ˜¤ė„˜ź°€ ė°œģƒķ–ˆģŠµė‹ˆė‹¤. ź°€ģž„ 최근 100ź°œģ˜ 채널 ė©”ģ‹œģ§€ė³“ė‹¤ ģ˜¤ėž˜ėœ ģ½ģ§€ ģ•Šģ€ ė©”ģ‹œģ§€ėŠ”' - ' ķ‘œģ‹œķ•  수 ģ—†ģŠµė‹ˆė‹¤.'; - - @override - String createPollLabel({bool isNew = false}) { - if (isNew) return '새 ķˆ¬ķ‘œ ė§Œė“¤źø°'; - return 'ķˆ¬ķ‘œ ė§Œė“¤źø°'; - } - - @override - String get questionsLabel => '질문'; - - @override - String get askAQuestionLabel => 'ģ§ˆė¬øķ•˜źø°'; - - @override - String? pollQuestionValidationError(int length, Range range) { - final (:min, :max) = range; - - // Check if the question is too short. - if (min != null && length < min) { - return 'ģ§ˆė¬øģ€ $minģž ģ“ģƒģ“ģ–“ģ•¼ ķ•©ė‹ˆė‹¤.'; - } - - // Check if the question is too long. - if (max != null && length > max) { - return 'ģ§ˆė¬øģ€ ģµœėŒ€ $maxģžģ—¬ģ•¼ ķ•©ė‹ˆė‹¤.'; - } - - return null; - } - - @override - String optionLabel({bool isPlural = false}) { - if (isPlural) return 'ģ˜µģ…˜'; - return 'ģ„ ķƒ'; - } - - @override - String get pollOptionEmptyError => 'ģ˜µģ…˜ģ€ ė¹„ģ›Œ ė‘˜ 수 ģ—†ģŠµė‹ˆė‹¤.'; - - @override - String get pollOptionDuplicateError => 'ģ“ź²ƒģ€ ģ“ėÆø ģ„ ķƒ ģ‚¬ķ•­ģž…ė‹ˆė‹¤'; - - @override - String get addAnOptionLabel => 'ģ˜µģ…˜ 추가'; - - @override - String get multipleAnswersLabel => '복수 답변'; - - @override - String get maximumVotesPerPersonLabel => '1ģøė‹¹ ģµœėŒ€ ķˆ¬ķ‘œ 수'; - - @override - String? maxVotesPerPersonValidationError(int votes, Range range) { - final (:min, :max) = range; - - if (min != null && votes < min) { - return 'ķˆ¬ķ‘œ ģˆ˜ėŠ” $min개 ģ“ģƒģ“ģ–“ģ•¼ ķ•©ė‹ˆė‹¤.'; - } - - if (max != null && votes > max) { - return 'ķˆ¬ķ‘œ ģˆ˜ėŠ” ģµœėŒ€ $maxź°œģ—¬ģ•¼ ķ•©ė‹ˆė‹¤.'; - } - - return null; - } - - @override - String get anonymousPollLabel => 'ģµėŖ… ķˆ¬ķ‘œ'; - - @override - String get pollOptionsLabel => 'ķˆ¬ķ‘œ ģ˜µģ…˜'; - - @override - String get suggestAnOptionLabel => 'ģ˜µģ…˜ ģ œģ•ˆ'; - - @override - String get enterANewOptionLabel => '새 ģ˜µģ…˜ ģž…ė „'; - - @override - String get addACommentLabel => 'ėŒ“źø€ 추가'; - - @override - String get pollCommentsLabel => 'ķˆ¬ķ‘œ ėŒ“źø€'; - - @override - String get updateYourCommentLabel => 'ėŒ“źø€ ģ—…ė°ģ“ķŠø'; - - @override - String get enterYourCommentLabel => 'ėŒ“źø€ ģž…ė „'; - - @override - String get createLabel => 'ģƒģ„±'; - - @override - String pollVotingModeLabel(PollVotingMode votingMode) { - return votingMode.when( - disabled: () => 'ķˆ¬ķ‘œ ģ¢…ė£Œ', - unique: () => 'ķ•˜ė‚˜ ģ„ ķƒ', - limited: (count) => 'ģµœėŒ€ $count ģ„ ķƒ', - all: () => 'ķ•˜ė‚˜ ģ“ģƒ ģ„ ķƒ', - ); - } - - @override - String seeAllOptionsLabel({int? count}) { - if (count == null) return 'ėŖØė“  ģ˜µģ…˜ 볓기'; - return 'ėŖØė“  $count ģ˜µģ…˜ 볓기'; - } - - @override - String get viewCommentsLabel => 'ėŒ“źø€ 볓기'; - - @override - String get viewResultsLabel => 'ź²°ź³¼ 볓기'; - - @override - String get endVoteLabel => 'ķˆ¬ķ‘œ ģ¢…ė£Œ'; - - @override - String get pollResultsLabel => 'ķˆ¬ķ‘œ ź²°ź³¼'; - - @override - String showAllVotesLabel({int? count}) { - if (count == null) return 'ėŖØė“  ķˆ¬ķ‘œ 볓기'; - return 'ėŖØė“  $count ķˆ¬ķ‘œ 볓기'; - } - - @override - String voteCountLabel({int? count}) => switch (count) { - null || < 1 => '0 ķ‘œ', - 1 => '1 ķ‘œ', - _ => '$count ķ‘œ', - }; - - @override - String get noPollVotesLabel => 'ķ˜„ģž¬ ķˆ¬ķ‘œź°€ ģ—†ģŠµė‹ˆė‹¤'; - - @override - String get loadingPollVotesError => 'ķˆ¬ķ‘œ ė”œė”© 오넘'; - - @override - String get repliedToLabel => 'ķšŒģ‹ :'; - - @override - String newThreadsLabel({required int count}) { - return '$countź°œģ˜ 새 ģŠ¤ė ˆė“œ'; - } - - @override - String get slideToCancelLabel => 'ģŠ¬ė¼ģ“ė“œķ•˜ģ—¬ ģ·Øģ†Œ'; - - @override - String get holdToRecordLabel => '길게 ėˆŒėŸ¬ģ„œ ė…¹ģŒ, ė†“ģ•„ģ„œ 전솔'; -} diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_no.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_no.dart deleted file mode 100644 index e584ee8377..0000000000 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_no.dart +++ /dev/null @@ -1,609 +0,0 @@ -part of 'stream_chat_localizations.dart'; - -/// The translations for Norwegian (`no`). -class StreamChatLocalizationsNo extends GlobalStreamChatLocalizations { - /// Create an instance of the translation bundle for Norwegian. - const StreamChatLocalizationsNo({super.localeName = 'no'}); - - @override - String get launchUrlError => 'Kan ikke laste inn url'; - - @override - String get loadingUsersError => 'Problem med Ć„ laste inn brukere'; - - @override - String get noUsersLabel => 'Det er ingen brukere akkurat nĆ„'; - - @override - String get noPhotoOrVideoLabel => 'Det er ingen bilde eller video'; - - @override - String get retryLabel => 'PrĆøv igjen'; - - @override - String get userLastOnlineText => 'Sist pĆ„logget'; - - @override - String get userOnlineText => 'PĆ„logget'; - - @override - String userTypingText(Iterable users) { - if (users.isEmpty) return ''; - final first = users.first; - if (users.length == 1) { - return '${first.name} skriver'; - } - return '${first.name} og ${users.length - 1} flere skriver'; - } - - @override - String get threadReplyLabel => 'Svar pĆ„ trĆ„d'; - - @override - String get onlyVisibleToYouText => 'Kun synlig for deg'; - - @override - String threadReplyCountText(int count) => '$count svar pĆ„ trĆ„d'; - - @override - String attachmentsUploadProgressText({ - required int remaining, - required int total, - }) => - 'Laster opp $remaining/$total ...'; - - @override - String pinnedByUserText({ - required User pinnedBy, - required User currentUser, - }) { - final pinnedByCurrentUser = currentUser.id == pinnedBy.id; - if (pinnedByCurrentUser) return 'Festet av deg'; - return 'Festet av ${pinnedBy.name}'; - } - - @override - String get sendMessagePermissionError => - 'Du har ikke tillatelse til Ć„ sende meldinger'; - - @override - String get emptyMessagesText => 'Det er ingen meldinger akkurat nĆ„'; - - @override - String get genericErrorText => 'Noe gikk galt'; - - @override - String get loadingMessagesError => 'Problem med Ć„ laste inn meldinger'; - - @override - String resultCountText(int count) => '$count resultater'; - - @override - String get messageDeletedText => 'Denne meldingen er slettet.'; - - @override - String get messageDeletedLabel => 'Melding slettet'; - - @override - String get editedMessageLabel => 'Redigert'; - - @override - String get messageReactionsLabel => 'Reaksjoner pĆ„ melding'; - - @override - String get emptyChatMessagesText => 'Ingen meldinger her enda...'; - - @override - String threadSeparatorText(int replyCount) { - if (replyCount == 1) return '1 svar'; - return '$replyCount svar'; - } - - @override - String get connectedLabel => 'Tilkoblet'; - - @override - String get disconnectedLabel => 'Avbrutt'; - - @override - String get reconnectingLabel => 'PrĆøver Ć„ koble til...'; - - @override - String get alsoSendAsDirectMessageLabel => 'OgsĆ„ send som en direktemelding'; - - @override - String get addACommentOrSendLabel => 'Legg til en kommentar eller send'; - - @override - String get searchGifLabel => 'SĆøk GIFs'; - - @override - String get writeAMessageLabel => 'Skriv en melding'; - - @override - String get instantCommandsLabel => 'Direkte kommandoer'; - - @override - String fileTooLargeAfterCompressionError(double limitInMB) => - 'Filen er for stor til Ć„ laste opp. ' - 'Grensen for filopplasting er $limitInMB MB. ' - 'Vi prĆøvde Ć„ komprimere den, men det hjalp ikke.'; - - @override - String fileTooLargeError(double limitInMB) => - 'Filen er for stor til Ć„ laste opp. Filgrense er $limitInMB MB.'; - - @override - String get addAFileLabel => 'Legg til en fil'; - - @override - String get photoFromCameraLabel => 'Bilde fra kamera'; - - @override - String get uploadAFileLabel => 'Last opp en fil'; - - @override - String get uploadAPhotoLabel => 'Last opp et bilde'; - - @override - String get uploadAVideoLabel => 'Last opp en video'; - - @override - String get videoFromCameraLabel => 'Video fra kamera'; - - @override - String get okLabel => 'OK'; - - @override - String get somethingWentWrongError => 'Noe gikk galt'; - - @override - String get addMoreFilesLabel => 'Legg til flere filer'; - - @override - String get enablePhotoAndVideoAccessMessage => - 'Vennligst gi tillatelse til dine bilder' - '\nog videoer sĆ„ du kan dele de med dine venner.'; - - @override - String get allowGalleryAccessMessage => 'Tillat tilgang til galleri'; - - @override - String get flagMessageLabel => 'Rapporter melding'; - - @override - String get flagMessageQuestion => - 'Ƙnsker du Ć„ sende en kopi av denne meldingen til en' - '\nmoderator for videre undersĆøkelser'; - - @override - String get flagLabel => 'RAPPORTER'; - - @override - String get cancelLabel => 'AVBRYT'; - - @override - String get flagMessageSuccessfulLabel => 'Melding rapportert'; - - @override - String get flagMessageSuccessfulText => - 'Meldingen har blitt rapportert til en moderator.'; - - @override - String get deleteLabel => 'SLETT'; - - @override - String get deleteMessageLabel => 'Slett melding'; - - @override - String get deleteMessageQuestion => - 'Er du sikker pĆ„ at du Ćønsker Ć„ slette denne meldingen permanent?'; - - @override - String get operationCouldNotBeCompletedText => - 'Denne handlingen kunne ikke bli gjennomfĆørt.'; - - @override - String get replyLabel => 'Svar'; - - @override - String togglePinUnpinText({required bool pinned}) { - if (pinned) return 'LĆøsne fra samtale'; - return 'Fest til samtale'; - } - - @override - String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed}) { - if (isDeleteFailed) return 'PrĆøv Ć„ slett melding pĆ„ nytt'; - return 'Slett melding'; - } - - @override - String get copyMessageLabel => 'Kopier melding'; - - @override - String get editMessageLabel => 'Rediger melding'; - - @override - String toggleResendOrResendEditedMessage({required bool isUpdateFailed}) { - if (isUpdateFailed) return 'Send redigert melding pĆ„ nytt'; - return 'Send pĆ„ nytt'; - } - - @override - String get photosLabel => 'Foto'; - - String _getDay(DateTime dateTime) { - final now = DateTime.now(); - final today = DateTime(now.year, now.month, now.day); - final yesterday = DateTime(now.year, now.month, now.day - 1); - - final date = DateTime(dateTime.year, dateTime.month, dateTime.day); - - if (date == today) { - return 'i dag'; - } else if (date == yesterday) { - return 'i gĆ„r'; - } else { - return 'pĆ„ ${Jiffy.parseFromDateTime(date).MMMd}'; - } - } - - @override - String sentAtText({required DateTime date, required DateTime time}) { - final atTime = Jiffy.parseFromDateTime(time.toLocal()); - return 'Sent ${_getDay(date)} kl. ${atTime.jm}'; - } - - @override - String get todayLabel => 'I dag'; - - @override - String get yesterdayLabel => 'I gĆ„r'; - - @override - String get channelIsMutedText => 'Kanal er dempet'; - - @override - String get noTitleText => 'Ingen tittel'; - - @override - String get letsStartChattingLabel => 'La oss starte Ć„ chatte!'; - - @override - String get sendingFirstMessageLabel => - 'Hva med Ć„ sende din fĆørste melding til en venn?'; - - @override - String get startAChatLabel => 'Start en chat'; - - @override - String get loadingChannelsError => 'Problemer med Ć„ laste inn kanaler'; - - @override - String get deleteConversationLabel => 'Slett samtale'; - - @override - String get deleteConversationQuestion => - 'Er du sikker pĆ„ at du Ćønsker Ć„ slette denne samtalen?'; - - @override - String get streamChatLabel => 'Stream Chat'; - - @override - String get searchingForNetworkText => 'SĆøker etter nettverk'; - - @override - String get offlineLabel => 'Avlogget...'; - - @override - String get tryAgainLabel => 'PrĆøv igjen'; - - @override - String membersCountText(int count) { - if (count == 1) return '1 medlem'; - return '$count medlemmer'; - } - - @override - String watchersCountText(int count) { - if (count == 1) return '1 pĆ„logget'; - return '$count pĆ„logget'; - } - - @override - String get viewInfoLabel => 'Se info'; - - @override - String get leaveGroupLabel => 'Forlat gruppe'; - - @override - String get leaveLabel => 'FORLAT'; - - @override - String get leaveConversationLabel => 'Forlat samtale'; - - @override - String get leaveConversationQuestion => - 'Er du sikker pĆ„ at du Ćønsker Ć„ forlate denne samtalen?'; - - @override - String get showInChatLabel => 'Se i chat'; - - @override - String get saveImageLabel => 'Lagre bilde'; - - @override - String get saveVideoLabel => 'Lagre video'; - - @override - String get uploadErrorLabel => 'PROBLEM MED OPPLASTNING'; - - @override - String get giphyLabel => 'Giphy'; - - @override - String get shuffleLabel => 'Stokk om'; - - @override - String get sendLabel => 'Send'; - - @override - String get withText => 'med'; - - @override - String get inText => 'i'; - - @override - String get youText => 'Du'; - - @override - String galleryPaginationText({ - required int currentPage, - required int totalPages, - }) => - '${currentPage + 1} of $totalPages'; - - @override - String get fileText => 'Fil'; - - @override - String get replyToMessageLabel => 'Svar pĆ„ melding'; - - @override - String attachmentLimitExceedError(int limit) => - 'Antall vedlegg oversteget, maks antall: $limit'; - - @override - String get slowModeOnLabel => 'Sakte modus Pƅ'; - - @override - String get linkDisabledDetails => - 'Sende lenker er ikke lov i denne samtalen.'; - - @override - String get linkDisabledError => 'Lenker er deaktivert'; - - @override - String get viewLibrary => 'Se bibliotek'; - - @override - String unreadMessagesSeparatorText() => 'Nye meldinger.'; - - @override - String get couldNotReadBytesFromFileError => - 'Kunne ikke lese bytes fra filen.'; - - @override - String get downloadLabel => 'Nedlasting'; - - @override - String toggleMuteUnmuteAction({required bool isMuted}) { - if (isMuted) return 'SlĆ„ pĆ„ lyden for bruker'; - return 'Dempe bruker'; - } - - @override - String toggleMuteUnmuteGroupQuestion({required bool isMuted}) { - if (isMuted) { - return 'Er du sikker pĆ„ at du vil oppheve ignoreringen av denne gruppen?'; - } - return 'Er du sikker pĆ„ at du vil ignorere denne gruppen?'; - } - - @override - String toggleMuteUnmuteGroupText({required bool isMuted}) { - if (isMuted) return 'SlĆ„ pĆ„ lyden for gruppe'; - return 'Mute gruppe'; - } - - @override - String toggleMuteUnmuteUserQuestion({required bool isMuted}) { - if (isMuted) { - // ignore: lines_longer_than_80_chars - return 'Er du sikker pĆ„ at du vil oppheve ignoreringen av denne brukeren?'; - } - return 'Er du sikker pĆ„ at du vil ignorere denne brukeren?'; - } - - @override - String toggleMuteUnmuteUserText({required bool isMuted}) { - if (isMuted) return 'Opphev lyden av brukeren'; - return 'Dempe brukeren'; - } - - @override - String get enableFileAccessMessage => - 'Aktiver tilgang til filer slik' '\nat du kan dele dem med venner.'; - - @override - String get allowFileAccessMessage => 'Gi tilgang til filer'; - - @override - String get markAsUnreadLabel => 'Merk som ulest'; - - @override - String unreadCountIndicatorLabel({required int unreadCount}) { - return '$unreadCount uleste'; - } - - @override - String get markUnreadError => - 'Feil ved merking av melding som ulest. Kan ikke merke meldinger som' - ' uleste som er eldre enn de 100 nyeste kanalmeldingene.'; - - @override - String createPollLabel({bool isNew = false}) { - if (isNew) return 'Opprett en ny avstemning'; - return 'Opprett avstemning'; - } - - @override - String get questionsLabel => 'SpĆørsmĆ„l'; - - @override - String get askAQuestionLabel => 'Still et spĆørsmĆ„l'; - - @override - String? pollQuestionValidationError(int length, Range range) { - final (:min, :max) = range; - - // Check if the question is too short. - if (min != null && length < min) { - return 'SpĆørsmĆ„let mĆ„ vƦre minst $min tegn langt'; - } - - // Check if the question is too long. - if (max != null && length > max) { - return 'SpĆørsmĆ„let mĆ„ vƦre maksimalt $max tegn langt'; - } - - return null; - } - - @override - String optionLabel({bool isPlural = false}) { - if (isPlural) return 'Alternativer'; - return 'Opsjon'; - } - - @override - String get pollOptionEmptyError => 'Alternativet kan ikke vƦre tomt'; - - @override - String get pollOptionDuplicateError => 'Dette er allerede et alternativ'; - - @override - String get addAnOptionLabel => 'Legg til et alternativ'; - - @override - String get multipleAnswersLabel => 'Flere svar'; - - @override - String get maximumVotesPerPersonLabel => - 'Maksimalt antall stemmer per person'; - - @override - String? maxVotesPerPersonValidationError(int votes, Range range) { - final (:min, :max) = range; - - if (min != null && votes < min) { - return 'Stemmetellingen mĆ„ vƦre minst $min'; - } - - if (max != null && votes > max) { - return 'Stemmeopptellingen mĆ„ vƦre pĆ„ maksimalt $max'; - } - - return null; - } - - @override - String get anonymousPollLabel => 'Anonym avstemning'; - - @override - String get pollOptionsLabel => 'Avstemningsalternativer'; - - @override - String get suggestAnOptionLabel => 'ForeslĆ„ et alternativ'; - - @override - String get enterANewOptionLabel => 'Skriv inn et nytt alternativ'; - - @override - String get addACommentLabel => 'Legg til en kommentar'; - - @override - String get pollCommentsLabel => 'Kommentarer til avstemningen'; - - @override - String get updateYourCommentLabel => 'Oppdater kommentaren din'; - - @override - String get enterYourCommentLabel => 'Skriv inn kommentaren din'; - - @override - String get createLabel => 'Opprett'; - - @override - String pollVotingModeLabel(PollVotingMode votingMode) { - return votingMode.when( - disabled: () => 'Avstemning avsluttet', - unique: () => 'Velg Ć©n', - limited: (count) => 'Velg opptil $count', - all: () => 'Velg Ć©n eller flere', - ); - } - - @override - String seeAllOptionsLabel({int? count}) { - if (count == null) return 'Se alle alternativer'; - return 'Se alle $count alternativer'; - } - - @override - String get viewCommentsLabel => 'Vis kommentarer'; - - @override - String get viewResultsLabel => 'Vis resultater'; - - @override - String get endVoteLabel => 'Avslutt avstemning'; - - @override - String get pollResultsLabel => 'Resultater for avstemningen'; - - @override - String showAllVotesLabel({int? count}) { - if (count == null) return 'Vis alle stemmer'; - return 'Vis alle $count stemmer'; - } - - @override - String voteCountLabel({int? count}) => switch (count) { - null || < 1 => '0 stemmer', - 1 => '1 stemme', - _ => '$count stemmer', - }; - - @override - String get noPollVotesLabel => 'Det er ingen stemmer for Ćøyeblikket'; - - @override - String get loadingPollVotesError => 'Feil ved lasting av stemmer'; - - @override - String get repliedToLabel => 'svarte pĆ„:'; - - @override - String newThreadsLabel({required int count}) { - if (count == 1) return '1 ny trĆ„d'; - return '$count nye trĆ„der'; - } - - @override - String get slideToCancelLabel => 'Gli for Ć„ avbryte'; - - @override - String get holdToRecordLabel => 'Hold for Ć„ ta opp, slipp for Ć„ sende'; -} diff --git a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_pt.dart b/packages/stream_chat_localizations/lib/src/stream_chat_localizations_pt.dart deleted file mode 100644 index b5a33f22e8..0000000000 --- a/packages/stream_chat_localizations/lib/src/stream_chat_localizations_pt.dart +++ /dev/null @@ -1,622 +0,0 @@ -part of 'stream_chat_localizations.dart'; - -/// The translations for Portuguese (`pt`). -class StreamChatLocalizationsPt extends GlobalStreamChatLocalizations { - /// Create an instance of the translation bundle for Portuguese. - const StreamChatLocalizationsPt({super.localeName = 'pt'}); - - @override - String get launchUrlError => 'O URL nĆ£o pĆ“de ser aberto'; - - @override - String get loadingUsersError => 'Erro de carregamento do usuĆ”rio'; - - @override - String get noUsersLabel => 'Nenhum usuĆ”rio atualmente'; - - @override - String get noPhotoOrVideoLabel => 'NĆ£o hĆ” fotos ou vĆ­deos'; - - @override - String get retryLabel => 'Tente novamente'; - - @override - String get userLastOnlineText => 'Última vez on-line'; - - @override - String get userOnlineText => 'Online'; - - @override - String userTypingText(Iterable users) { - if (users.isEmpty) return ''; - final first = users.first; - if (users.length == 1) { - return '${first.name} estĆ” digitando'; - } - return '${first.name} e ${users.length - 1} estĆ£o digitando'; - } - - @override - String get threadReplyLabel => 'Responder na conversa'; - - @override - String get onlyVisibleToYouText => 'VisĆ­vel apenas para vocĆŖ'; - - @override - String threadReplyCountText(int count) => '$count respostas na conversa'; - - @override - String attachmentsUploadProgressText({ - required int remaining, - required int total, - }) => - 'TranferĆŖncia em andamento $remaining/$total ...'; - - @override - String pinnedByUserText({ - required User pinnedBy, - required User currentUser, - }) { - final pinnedByCurrentUser = currentUser.id == pinnedBy.id; - if (pinnedByCurrentUser) return 'Definido por vocĆŖ'; - return 'Definido por ${pinnedBy.name}'; - } - - @override - String get emptyMessagesText => 'NĆ£o hĆ” mensagens'; - - @override - String get genericErrorText => 'Ocorreu um problema'; - - @override - String get loadingMessagesError => - 'Ocorreu um problema ao carregar a mensagem'; - - @override - String resultCountText(int count) => '$count resultados'; - - @override - String get messageDeletedText => 'Esta mensagem foi excluĆ­da.'; - - @override - String get messageDeletedLabel => 'Mensagem excluĆ­da'; - - @override - String get editedMessageLabel => 'Editado'; - - @override - String get messageReactionsLabel => 'ReaƧƵes Ć s mensagens'; - - @override - String get emptyChatMessagesText => 'Ainda nĆ£o hĆ” mensagens aqui...'; - - @override - String threadSeparatorText(int replyCount) { - if (replyCount == 1) return '1 resposta'; - return '$replyCount respostas'; - } - - @override - String get connectedLabel => 'Conectado'; - - @override - String get disconnectedLabel => 'Desconectado'; - - @override - String get reconnectingLabel => 'Reconectando...'; - - @override - String get alsoSendAsDirectMessageLabel => - 'Enviar tambĆ©m como mensagem direta'; - - @override - String get addACommentOrSendLabel => 'Adicionar um comnetĆ”rio ou enviar'; - - @override - String get searchGifLabel => 'Pesquisar GIFs'; - - @override - String get writeAMessageLabel => 'Escrever uma mensagem'; - - @override - String get instantCommandsLabel => 'Comandos instantĆ¢neos'; - - @override - String fileTooLargeAfterCompressionError(double limitInMB) => - 'O arquivo Ć© muito grande para carregamento. ' - 'O tamanho mĆ”ximo do arquivo Ć© de $limitInMB MB. ' - 'Tentamos comprimi-lo, mas nĆ£o foi suficiente.'; - - @override - String fileTooLargeError(double limitInMB) => - 'O arquivo Ć© muito grande para carregamento. ' - 'O tamanho mĆ”ximo dos arquivos Ć© de $limitInMB MB.'; - - @override - String get couldNotReadBytesFromFileError => - 'NĆ£o foi possĆ­vel ler os bytes do arquivo.'; - - @override - String get addAFileLabel => 'Adicionar um arquivo'; - - @override - String get photoFromCameraLabel => 'Foto da cĆ¢mera'; - - @override - String get uploadAFileLabel => 'Transferir um arquivo'; - - @override - String get uploadAPhotoLabel => 'Carregar uma foto'; - - @override - String get uploadAVideoLabel => 'Carregar um vĆ­deo'; - - @override - String get videoFromCameraLabel => 'VĆ­deo da cĆ¢mera'; - - @override - String get okLabel => 'OK'; - - @override - String get somethingWentWrongError => 'Algo deu errado'; - - @override - String get addMoreFilesLabel => 'Adicionar mais arquivos'; - - @override - String get enablePhotoAndVideoAccessMessage => - 'Por favor, permita o acesso Ć s suas fotos' - '\ne vĆ­deos para que possa compartilhar com sua rede.'; - - @override - String get allowGalleryAccessMessage => 'Permitir acesso Ć  sua galeria'; - - @override - String get flagMessageLabel => 'Denunciar mensagem'; - - @override - String get flagMessageQuestion => 'Gostaria de enviar esta mensagem ao' - '\nmoderador para maior investigação?'; - - @override - String get flagLabel => 'DENUNCIAR'; - - @override - String get cancelLabel => 'CANCELAR'; - - @override - String get flagMessageSuccessfulLabel => 'Mensagem denunciada'; - - @override - String get flagMessageSuccessfulText => - 'Esta mensagem foi enviada a um moderador.'; - - @override - String get deleteLabel => 'APAGAR'; - - @override - String get deleteMessageLabel => 'Apagar mensagem'; - - @override - String get deleteMessageQuestion => - 'VocĆŖ tem certeza que deseja apagar essa\nmensagem permanentemente?'; - - @override - String get operationCouldNotBeCompletedText => - 'A operação nĆ£o pode ser completada.'; - - @override - String get replyLabel => 'Resposta'; - - @override - String togglePinUnpinText({required bool pinned}) { - if (pinned) return 'Desafixar na conversa'; - return 'Fixar na conversa'; - } - - @override - String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed}) { - if (isDeleteFailed) return 'Repetir apagar mensagem'; - return 'Apagar mensagem'; - } - - @override - String get copyMessageLabel => 'Copiar mensagem'; - - @override - String get editMessageLabel => 'Editar mensagem'; - - @override - String toggleResendOrResendEditedMessage({required bool isUpdateFailed}) { - if (isUpdateFailed) return 'Reenviar mensagem alterada'; - return 'Reenviar'; - } - - @override - String get photosLabel => 'Fotos'; - - String _getDay(DateTime dateTime) { - final now = DateTime.now(); - final today = DateTime(now.year, now.month, now.day); - final yesterday = DateTime(now.year, now.month, now.day - 1); - - final date = DateTime(dateTime.year, dateTime.month, dateTime.day); - - if (date == today) { - return 'Hoje'; - } else if (date == yesterday) { - return 'Ontem'; - } else { - return 'o ${Jiffy.parseFromDateTime(date).MMMd}'; - } - } - - @override - String sentAtText({required DateTime date, required DateTime time}) { - final atTime = Jiffy.parseFromDateTime(time.toLocal()); - return 'Enviado ${_getDay(date)} Ć s ${atTime.jm}'; - } - - @override - String get todayLabel => 'Hoje'; - - @override - String get yesterdayLabel => 'Ontem'; - - @override - String get channelIsMutedText => 'O canal estĆ” silenciado'; - - @override - String get noTitleText => 'Sem tĆ­tulo'; - - @override - String get letsStartChattingLabel => 'Vamos comeƧar a conversar!'; - - @override - String get sendingFirstMessageLabel => - 'Que tal enviar sua primeira mensagem a um amigo?'; - - @override - String get startAChatLabel => 'Iniciar uma conversa'; - - @override - String get loadingChannelsError => 'Erro ao carregar os canais'; - - @override - String get deleteConversationLabel => 'Apagar a conversa'; - - @override - String get deleteConversationQuestion => - 'Tem certeza que deseja apagar essa conversa?'; - - @override - String get streamChatLabel => 'Stream Chat'; - - @override - String get searchingForNetworkText => 'Pesquisando rede'; - - @override - String get offlineLabel => 'Sem conexĆ£o...'; - - @override - String get tryAgainLabel => 'Tente novamente'; - - @override - String membersCountText(int count) { - if (count == 1) return '1 membro'; - return '$count membros'; - } - - @override - String watchersCountText(int count) { - if (count == 1) return '1 online'; - return '$count online'; - } - - @override - String get viewInfoLabel => 'Ver informação'; - - @override - String get leaveGroupLabel => 'Sair do grupo'; - - @override - String get leaveLabel => 'SAIR'; - - @override - String get leaveConversationLabel => 'Sair da conversa'; - - @override - String get leaveConversationQuestion => - 'Tem certeza que deseja sair dessa conversa?'; - - @override - String get showInChatLabel => 'Mostrar no chat'; - - @override - String get saveImageLabel => 'Salvar imagem'; - - @override - String get saveVideoLabel => 'Salvar vĆ­deo'; - - @override - String get uploadErrorLabel => 'ERRO DE TRANSFERÊNCIA'; - - @override - String get giphyLabel => 'Giphy'; - - @override - String get shuffleLabel => 'Misturar'; - - @override - String get sendLabel => 'Enviar'; - - @override - String get withText => 'com'; - - @override - String get inText => 'em'; - - @override - String get youText => 'VocĆŖ'; - - @override - String galleryPaginationText({ - required int currentPage, - required int totalPages, - }) => - '${currentPage + 1} de $totalPages'; - - @override - String get fileText => 'Arquivo'; - - @override - String get replyToMessageLabel => 'Responder Ć  mensagem'; - - @override - String attachmentLimitExceedError(int limit) => ''' -NĆ£o Ć© possĆ­vel adicionar mais de $limit arquivos de uma vez - '''; - - @override - String get slowModeOnLabel => 'Modo lento ativado'; - - @override - String get downloadLabel => 'Download'; - - @override - String toggleMuteUnmuteUserText({required bool isMuted}) { - if (isMuted) { - return 'Ativar o som do usuĆ”rio'; - } else { - return 'Silenciar usuĆ”rio'; - } - } - - @override - String toggleMuteUnmuteGroupQuestion({required bool isMuted}) { - if (isMuted) { - return 'Tem certeza de que deseja ativar o som deste grupo?'; - } else { - return 'Tem certeza de que deseja silenciar este grupo?'; - } - } - - @override - String toggleMuteUnmuteUserQuestion({required bool isMuted}) { - if (isMuted) { - return 'Tem certeza de que deseja ativar o som deste usuĆ”rio?'; - } else { - return 'Tem certeza de que deseja silenciar este usuĆ”rio?'; - } - } - - @override - String toggleMuteUnmuteAction({required bool isMuted}) { - if (isMuted) { - return 'ATIVAR MUDO'; - } else { - return 'MUDO'; - } - } - - @override - String toggleMuteUnmuteGroupText({required bool isMuted}) { - if (isMuted) { - return 'Reativar o som do grupo'; - } else { - return 'Silenciar Grupo'; - } - } - - @override - String get linkDisabledDetails => - 'O envio de links nĆ£o Ć© permitido nesta conversa.'; - - @override - String get linkDisabledError => 'Os links estĆ£o desativados'; - - @override - String get sendMessagePermissionError => - 'VocĆŖ nĆ£o tem permissĆ£o para enviar mensagens'; - - @override - String get viewLibrary => 'Ver biblioteca'; - - @override - String unreadMessagesSeparatorText() => 'Novas mensagens'; - - @override - String get enableFileAccessMessage => - 'Ative o acesso aos arquivos' '\npara poder compartilhĆ”-los com amigos.'; - - @override - String get allowFileAccessMessage => 'Permitir acesso aos arquivos'; - - @override - String get markAsUnreadLabel => 'Marcar como nĆ£o lida'; - - @override - String unreadCountIndicatorLabel({required int unreadCount}) { - return '$unreadCount nĆ£o lidas'; - } - - @override - String get markUnreadError => - 'Erro ao marcar a mensagem como nĆ£o lida. NĆ£o Ć© possĆ­vel marcar mensagens' - ' nĆ£o lidas mais antigas do que as 100 mensagens mais recentes do canal.'; - - @override - String createPollLabel({bool isNew = false}) { - if (isNew) return 'Criar uma nova sondagem'; - return 'Criar sondagem'; - } - - @override - String get questionsLabel => 'Perguntas'; - - @override - String get askAQuestionLabel => 'Fazer uma pergunta'; - - @override - String? pollQuestionValidationError(int length, Range range) { - final (:min, :max) = range; - - // Check if the question is too short. - if (min != null && length < min) { - return 'A pergunta deve ter pelo menos $min caracteres'; - } - - // Check if the question is too long. - if (max != null && length > max) { - return 'A pergunta deve ter, no mĆ”ximo, $max caracteres'; - } - - return null; - } - - @override - String optionLabel({bool isPlural = false}) { - if (isPlural) return 'OpƧƵes'; - return 'Opção'; - } - - @override - String get pollOptionEmptyError => 'A opção nĆ£o pode estar vazia'; - - @override - String get pollOptionDuplicateError => 'Esta jĆ” Ć© uma opção'; - - @override - String get addAnOptionLabel => 'Adicionar uma opção'; - - @override - String get multipleAnswersLabel => 'Respostas mĆŗltiplas'; - - @override - String get maximumVotesPerPersonLabel => 'MĆ”ximo de votos por pessoa'; - - @override - String? maxVotesPerPersonValidationError(int votes, Range range) { - final (:min, :max) = range; - - if (min != null && votes < min) { - return 'A contagem dos votos deve ser de, pelo menos, $min'; - } - - if (max != null && votes > max) { - return 'A contagem dos votos deve ser, no mĆ”ximo, $max'; - } - - return null; - } - - @override - String get anonymousPollLabel => 'Votação anĆ“nima'; - - @override - String get pollOptionsLabel => 'OpƧƵes de votação'; - - @override - String get suggestAnOptionLabel => 'Sugerir uma opção'; - - @override - String get enterANewOptionLabel => 'Inserir uma nova opção'; - - @override - String get addACommentLabel => 'Adicionar um comentĆ”rio'; - - @override - String get pollCommentsLabel => 'ComentĆ”rios da votação'; - - @override - String get updateYourCommentLabel => 'Atualizar seu comentĆ”rio'; - - @override - String get enterYourCommentLabel => 'Inserir seu comentĆ”rio'; - - @override - String get createLabel => 'Criar'; - - @override - String pollVotingModeLabel(PollVotingMode votingMode) { - return votingMode.when( - disabled: () => 'Votação encerrada', - unique: () => 'Selecionar um', - limited: (count) => 'Selecionar atĆ© $count', - all: () => 'Selecionar um ou mais', - ); - } - - @override - String seeAllOptionsLabel({int? count}) { - if (count == null) return 'Ver todas as opƧƵes'; - return 'Ver todas as $count opƧƵes'; - } - - @override - String get viewCommentsLabel => 'Ver comentĆ”rios'; - - @override - String get viewResultsLabel => 'Ver resultados'; - - @override - String get endVoteLabel => 'Encerrar votação'; - - @override - String get pollResultsLabel => 'Resultados da votação'; - - @override - String showAllVotesLabel({int? count}) { - if (count == null) return 'Mostrar todos os votos'; - return 'Mostrar todos os $count votos'; - } - - @override - String voteCountLabel({int? count}) => switch (count) { - null || < 1 => '0 votos', - 1 => '1 voto', - _ => '$count votos', - }; - - @override - String get noPollVotesLabel => 'NĆ£o hĆ” votos no momento'; - - @override - String get loadingPollVotesError => 'Erro ao carregar os votos'; - - @override - String get repliedToLabel => 'respondeu a:'; - - @override - String newThreadsLabel({required int count}) { - if (count == 1) return '1 novo tópico'; - return '$count novos tópicos'; - } - - @override - String get slideToCancelLabel => 'Deslize para cancelar'; - - @override - String get holdToRecordLabel => - 'Mantenha pressionado para gravar, solte para enviar'; -} diff --git a/packages/stream_chat_localizations/lib/stream_chat_localizations.dart b/packages/stream_chat_localizations/lib/stream_chat_localizations.dart deleted file mode 100644 index 27c018806a..0000000000 --- a/packages/stream_chat_localizations/lib/stream_chat_localizations.dart +++ /dev/null @@ -1,9 +0,0 @@ -/// Localizations for the StreamChat Flutter library. -library stream_chat_localizations; - -export 'package:flutter_localizations/flutter_localizations.dart' - show - GlobalCupertinoLocalizations, - GlobalMaterialLocalizations, - GlobalWidgetsLocalizations; -export 'src/stream_chat_localizations.dart' hide getStreamChatTranslation; diff --git a/packages/stream_chat_localizations/pubspec.yaml b/packages/stream_chat_localizations/pubspec.yaml deleted file mode 100644 index 1b9e6e3130..0000000000 --- a/packages/stream_chat_localizations/pubspec.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: stream_chat_localizations -description: The Official localizations for Stream Chat Flutter, a service for building chat applications -version: 9.4.0 -homepage: https://github.com/GetStream/stream-chat-flutter -repository: https://github.com/GetStream/stream-chat-flutter -issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues - -# Note: The environment configuration and dependency versions are managed by Melos. -# -# Do not edit them manually. -# -# Steps to update dependencies: -# 1. Modify the version in the melos.yaml file. -# 2. Run `melos bootstrap` to apply changes. -# -# Steps to add a new dependency: -# 1. Add the dependency to this list. -# 2. Add it to the melos.yaml file for future updates. - -environment: - sdk: ^3.6.2 - flutter: ">=3.27.4" - -dependencies: - flutter: - sdk: flutter - flutter_localizations: - sdk: flutter - stream_chat_flutter: ^9.4.0 - -dev_dependencies: - flutter_test: - sdk: flutter diff --git a/packages/stream_chat_localizations/test/basics_test.dart b/packages/stream_chat_localizations/test/basics_test.dart deleted file mode 100644 index a05877f43c..0000000000 --- a/packages/stream_chat_localizations/test/basics_test.dart +++ /dev/null @@ -1,112 +0,0 @@ -// ignore_for_file: omit_local_variable_types - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_localizations/stream_chat_localizations.dart'; - -void main() { - testWidgets('Nested Localizations', (WidgetTester tester) async { - await tester.pumpWidget(MaterialApp( - theme: ThemeData( - useMaterial3: false, - ), - // Creates the outer Localizations widget. - home: ListView( - children: [ - const LocalizationTracker(key: ValueKey('outer')), - Localizations( - locale: const Locale('hi'), - delegates: GlobalStreamChatLocalizations.delegates, - child: const LocalizationTracker(key: ValueKey('inner')), - ), - ], - ), - )); - - final LocalizationTrackerState outerTracker = tester.state( - find.byKey(const ValueKey('outer'), skipOffstage: false)); - expect(outerTracker.captionFontSize, 12.0); - final LocalizationTrackerState innerTracker = tester.state( - find.byKey(const ValueKey('inner'), skipOffstage: false)); - expect(innerTracker.captionFontSize, 13.0); - }); - - testWidgets( - 'Localizations is compatible with ChangeNotifier.dispose() called ' - 'during didChangeDependencies', - (WidgetTester tester) async { - // PageView calls ScrollPosition.dispose() during didChangeDependencies. - await tester.pumpWidget(MaterialApp( - supportedLocales: const [ - Locale('en', 'US'), - Locale('hi', 'IN'), - ], - localizationsDelegates: const [ - DummyLocalizations.delegate, - GlobalStreamChatLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - home: PageView(), - )); - - await tester.binding.setLocale('hi', 'IN'); - await tester.pump(); - await tester.pumpWidget(Container()); - }, - ); - - testWidgets('Locale without countryCode', (WidgetTester tester) async { - // Regression test for https://github.com/flutter/flutter/pull/16782 - await tester.pumpWidget(MaterialApp( - localizationsDelegates: GlobalStreamChatLocalizations.delegates, - supportedLocales: const [ - Locale('en', 'US'), - Locale('hi'), - ], - home: Container(), - )); - - await tester.binding.setLocale('hi', ''); - await tester.pump(); - await tester.binding.setLocale('en', 'US'); - await tester.pump(); - }); -} - -/// A localizations delegate that does not contain any useful data, and is only -/// used to trigger didChangeDependencies upon locale change. -class _DummyLocalizationsDelegate - extends LocalizationsDelegate { - const _DummyLocalizationsDelegate(); - - @override - Future load(Locale locale) async => DummyLocalizations(); - - @override - bool isSupported(Locale locale) => true; - - @override - bool shouldReload(_DummyLocalizationsDelegate old) => true; -} - -class DummyLocalizations { - static const delegate = _DummyLocalizationsDelegate(); -} - -class LocalizationTracker extends StatefulWidget { - const LocalizationTracker({super.key}); - - @override - State createState() => LocalizationTrackerState(); -} - -class LocalizationTrackerState extends State { - late double captionFontSize; - - @override - Widget build(BuildContext context) { - captionFontSize = Theme.of(context).textTheme.bodySmall!.fontSize!; - return Container(); - } -} diff --git a/packages/stream_chat_localizations/test/override_test.dart b/packages/stream_chat_localizations/test/override_test.dart deleted file mode 100644 index 6b775de167..0000000000 --- a/packages/stream_chat_localizations/test/override_test.dart +++ /dev/null @@ -1,278 +0,0 @@ -// ignore_for_file: prefer_expression_function_bodies - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:stream_chat_localizations/src/stream_chat_localizations.dart'; - -class FooStreamChatLocalizations extends StreamChatLocalizationsEn { - FooStreamChatLocalizations( - Locale localeName, - this.launchUrlError, - ) : super(localeName: localeName.toString()); - - @override - final String launchUrlError; -} - -class FooStreamChatLocalizationsDelegate - extends LocalizationsDelegate { - const FooStreamChatLocalizationsDelegate({ - this.supportedLanguage = 'en', - this.launchUrlError = 'foo', - }); - - final String supportedLanguage; - final String launchUrlError; - - @override - bool isSupported(Locale locale) => - supportedLanguage == 'allLanguages' || - locale.languageCode == supportedLanguage; - - @override - Future load(Locale locale) => - SynchronousFuture( - FooStreamChatLocalizations(locale, launchUrlError), - ); - - @override - bool shouldReload(FooStreamChatLocalizationsDelegate old) => false; -} - -Widget buildFrame({ - Locale? locale, - Iterable delegates = - GlobalStreamChatLocalizations.delegates, - required WidgetBuilder buildContent, - LocaleResolutionCallback? localeResolutionCallback, - Iterable supportedLocales = const [ - Locale('en', 'US'), - Locale('hi', 'IN'), - ], -}) => - MaterialApp( - color: const Color(0xFFFFFFFF), - locale: locale, - supportedLocales: supportedLocales, - localizationsDelegates: delegates, - localeResolutionCallback: localeResolutionCallback, - onGenerateRoute: (RouteSettings settings) => MaterialPageRoute( - builder: (BuildContext context) => buildContent(context)), - ); - -void main() { - testWidgets( - 'Locale fallbacks', - (WidgetTester tester) async { - final Key textKey = UniqueKey(); - - await tester.pumpWidget( - buildFrame( - buildContent: (BuildContext context) => Text( - StreamChatLocalizations.of(context)!.launchUrlError, - key: textKey, - ), - ), - ); - - expect( - tester.widget(find.byKey(textKey)).data, - 'Cannot launch the url', - ); - - // Unrecognized locale falls back to 'en' - await tester.binding.setLocale('foo', 'BAR'); - await tester.pump(); - expect( - tester.widget(find.byKey(textKey)).data, - 'Cannot launch the url', - ); - - // Indian hindi locale, falls back to just 'hi' - await tester.binding.setLocale('hi', 'IN'); - await tester.pump(); - expect( - tester.widget(find.byKey(textKey)).data, - 'ą¤Æą„‚ą¤†ą¤°ą¤ą¤² ą¤²ą„‰ą¤Øą„ą¤š ą¤Øą¤¹ą„€ą¤‚ कर ą¤øą¤•ą¤¤ą„‡', - ); - }, - ); - - testWidgets( - "Localizations.override widget tracks parent's locale", - (WidgetTester tester) async { - Widget buildLocaleFrame(Locale locale) => buildFrame( - locale: locale, - supportedLocales: [locale], - buildContent: (BuildContext context) => Localizations.override( - context: context, - child: Builder( - builder: (BuildContext context) { - // No StreamChatLocalizations are defined for the first - // Localizations ancestor, so we should get the values from - // the default one, i.e. the one created by WidgetsApp via - // the LocalizationsDelegate provided by MaterialApp. - return Text( - StreamChatLocalizations.of(context)!.launchUrlError, - ); - }, - ), - ), - ); - - await tester.pumpWidget(buildLocaleFrame(const Locale('en', 'US'))); - expect(find.text('Cannot launch the url'), findsOneWidget); - - await tester.pumpWidget(buildLocaleFrame(const Locale('hi', 'IN'))); - expect(find.text('ą¤Æą„‚ą¤†ą¤°ą¤ą¤² ą¤²ą„‰ą¤Øą„ą¤š ą¤Øą¤¹ą„€ą¤‚ कर ą¤øą¤•ą¤¤ą„‡'), findsOneWidget); - }, - ); - - testWidgets('Localizations.override widget with hardwired locale', - (WidgetTester tester) async { - Widget buildLocaleFrame(Locale locale) => buildFrame( - locale: locale, - buildContent: (BuildContext context) { - return Localizations.override( - context: context, - locale: const Locale('en', 'US'), - child: Builder( - builder: (BuildContext context) { - // No StreamChatLocalizations are defined for the first - // Localizations ancestor, so we should get the values from - // the default one, i.e. the one created by WidgetsApp via - // the LocalizationsDelegate provided by MaterialApp. - return Text( - StreamChatLocalizations.of(context)!.launchUrlError, - ); - }, - ), - ); - }, - ); - - await tester.pumpWidget(buildLocaleFrame(const Locale('en', 'US'))); - expect(find.text('Cannot launch the url'), findsOneWidget); - - await tester.pumpWidget(buildLocaleFrame(const Locale('hi', 'IN'))); - expect(find.text('Cannot launch the url'), findsOneWidget); - }); - - testWidgets( - 'MaterialApp adds StreamChatLocalizations for additional languages', - (WidgetTester tester) async { - final Key textKey = UniqueKey(); - - await tester.pumpWidget(buildFrame( - delegates: [ - ...GlobalStreamChatLocalizations.delegates, - const FooStreamChatLocalizationsDelegate( - supportedLanguage: 'fr', - launchUrlError: "Impossible de lancer l'url", - ), - const FooStreamChatLocalizationsDelegate( - supportedLanguage: 'uz', - launchUrlError: 'test', - ), - ], - supportedLocales: const [ - Locale('en'), - Locale('hi'), - Locale('fr'), - Locale('de'), - Locale('uz'), - ], - buildContent: (BuildContext context) => Text( - StreamChatLocalizations.of(context)!.launchUrlError, - key: textKey, - ), - )); - - expect( - tester.widget(find.byKey(textKey)).data, - 'Cannot launch the url', - ); - - await tester.binding.setLocale('hi', 'IN'); - await tester.pump(); - expect(find.text('ą¤Æą„‚ą¤†ą¤°ą¤ą¤² ą¤²ą„‰ą¤Øą„ą¤š ą¤Øą¤¹ą„€ą¤‚ कर ą¤øą¤•ą¤¤ą„‡'), findsOneWidget); - - await tester.binding.setLocale('fr', 'CA'); - await tester.pump(); - expect(find.text("Impossible de lancer l'url"), findsOneWidget); - - await tester.binding.setLocale('uz', 'UZ'); - await tester.pump(); - expect(find.text('test'), findsOneWidget); - }, - ); - - testWidgets( - 'MaterialApp overrides MaterialLocalizations for all locales', - (WidgetTester tester) async { - final Key textKey = UniqueKey(); - - await tester.pumpWidget(buildFrame( - // Accept whatever locale we're given - localeResolutionCallback: - (Locale? locale, Iterable supportedLocales) => locale, - delegates: [ - const FooStreamChatLocalizationsDelegate( - supportedLanguage: 'allLanguages', - ), - ...GlobalStreamChatLocalizations.delegates, - ], - buildContent: (BuildContext context) { - // Should always be 'foo', no matter what the locale is - return Text( - StreamChatLocalizations.of(context)!.launchUrlError, - key: textKey, - ); - }, - )); - - expect(tester.widget(find.byKey(textKey)).data, 'foo'); - - await tester.binding.setLocale('zh', 'CN'); - await tester.pump(); - expect(find.text('foo'), findsOneWidget); - - await tester.binding.setLocale('de', 'DE'); - await tester.pump(); - expect(find.text('foo'), findsOneWidget); - }, - ); - - testWidgets( - 'MaterialApp overrides MaterialLocalizations for default locale', - (WidgetTester tester) async { - final Key textKey = UniqueKey(); - - await tester.pumpWidget(buildFrame( - delegates: [ - const FooStreamChatLocalizationsDelegate(), - ], - // supportedLocales not specified, so all locales resolve to 'en' - buildContent: (BuildContext context) => Text( - StreamChatLocalizations.of(context)!.launchUrlError, - key: textKey, - ), - )); - - // Unsupported locale '_' (the widget tester's default) resolves to 'en'. - expect(tester.widget(find.byKey(textKey)).data, 'foo'); - - // Unsupported locale 'zh' resolves to 'en'. - await tester.binding.setLocale('zh', 'CN'); - await tester.pump(); - expect(find.text('foo'), findsOneWidget); - - // Unsupported locale 'de' resolves to 'en'. - await tester.binding.setLocale('de', 'DE'); - await tester.pump(); - expect(find.text('foo'), findsOneWidget); - }, - ); -} diff --git a/packages/stream_chat_localizations/test/translations_test.dart b/packages/stream_chat_localizations/test/translations_test.dart deleted file mode 100644 index 935b1b7f81..0000000000 --- a/packages/stream_chat_localizations/test/translations_test.dart +++ /dev/null @@ -1,313 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:stream_chat_localizations/src/stream_chat_localizations.dart'; - -void main() { - for (final language in kStreamChatSupportedLanguages) { - test('translations exist for $language', () async { - final locale = Locale(language); - expect( - GlobalStreamChatLocalizations.delegate.isSupported(locale), isTrue); - final localizations = - await GlobalStreamChatLocalizations.delegate.load(locale); - expect(localizations.launchUrlError, isNotNull); - expect(localizations.loadingUsersError, isNotNull); - expect(localizations.noUsersLabel, isNotNull); - expect(localizations.noPhotoOrVideoLabel, isNotNull); - expect(localizations.retryLabel, isNotNull); - expect(localizations.userLastOnlineText, isNotNull); - expect(localizations.userOnlineText, isNotNull); - expect(localizations.userOnlineText, isNotNull); - // no users - expect(localizations.userTypingText([]), isNotNull); - // single user - expect(localizations.userTypingText([User(id: 'test-id')]), isNotNull); - // multiple users - expect( - localizations.userTypingText([ - User(id: 'test-id-1'), - User(id: 'test-id-2'), - ]), - isNotNull, - ); - expect(localizations.threadReplyLabel, isNotNull); - expect(localizations.onlyVisibleToYouText, isNotNull); - expect(localizations.editedMessageLabel, isNotNull); - expect(localizations.threadReplyCountText(3), isNotNull); - expect( - localizations.attachmentsUploadProgressText(remaining: 3, total: 10), - isNotNull, - ); - expect( - localizations.pinnedByUserText( - pinnedBy: User(id: 'pinned-by-user-id'), - currentUser: OwnUser(id: 'current-user-id'), - ), - isNotNull, - ); - expect(localizations.emptyMessagesText, isNotNull); - expect(localizations.genericErrorText, isNotNull); - expect(localizations.loadingMessagesError, isNotNull); - expect(localizations.resultCountText(3), isNotNull); - expect(localizations.messageDeletedText, isNotNull); - expect(localizations.messageDeletedLabel, isNotNull); - expect(localizations.messageReactionsLabel, isNotNull); - expect(localizations.emptyChatMessagesText, isNotNull); - expect(localizations.threadSeparatorText(3), isNotNull); - expect(localizations.connectedLabel, isNotNull); - expect(localizations.disconnectedLabel, isNotNull); - expect(localizations.reconnectingLabel, isNotNull); - expect(localizations.alsoSendAsDirectMessageLabel, isNotNull); - expect(localizations.addACommentOrSendLabel, isNotNull); - expect(localizations.searchGifLabel, isNotNull); - expect(localizations.writeAMessageLabel, isNotNull); - expect(localizations.instantCommandsLabel, isNotNull); - expect(localizations.fileTooLargeAfterCompressionError(33), isNotNull); - expect(localizations.fileTooLargeError(33), isNotNull); - expect(localizations.addAFileLabel, isNotNull); - expect(localizations.photoFromCameraLabel, isNotNull); - expect(localizations.uploadAFileLabel, isNotNull); - expect(localizations.uploadAPhotoLabel, isNotNull); - expect(localizations.uploadAVideoLabel, isNotNull); - expect(localizations.videoFromCameraLabel, isNotNull); - expect(localizations.okLabel, isNotNull); - expect(localizations.somethingWentWrongError, isNotNull); - expect(localizations.addMoreFilesLabel, isNotNull); - expect(localizations.enablePhotoAndVideoAccessMessage, isNotNull); - expect(localizations.allowGalleryAccessMessage, isNotNull); - expect(localizations.flagMessageLabel, isNotNull); - expect(localizations.flagMessageQuestion, isNotNull); - expect(localizations.flagLabel, isNotNull); - expect(localizations.cancelLabel, isNotNull); - expect(localizations.flagMessageSuccessfulLabel, isNotNull); - expect(localizations.flagMessageSuccessfulText, isNotNull); - expect(localizations.deleteLabel, isNotNull); - expect(localizations.deleteMessageLabel, isNotNull); - expect(localizations.deleteMessageQuestion, isNotNull); - expect(localizations.operationCouldNotBeCompletedText, isNotNull); - expect(localizations.replyLabel, isNotNull); - // pinned - expect(localizations.togglePinUnpinText(pinned: true), isNotNull); - // un-pinned - expect(localizations.togglePinUnpinText(pinned: false), isNotNull); - // delete-failed - expect( - localizations.toggleDeleteRetryDeleteMessageText(isDeleteFailed: true), - isNotNull, - ); - // first-delete - expect( - localizations.toggleDeleteRetryDeleteMessageText(isDeleteFailed: false), - isNotNull, - ); - expect(localizations.copyMessageLabel, isNotNull); - expect(localizations.editMessageLabel, isNotNull); - // resend-failed - expect( - localizations.toggleResendOrResendEditedMessage(isUpdateFailed: true), - isNotNull, - ); - // first resend - expect( - localizations.toggleResendOrResendEditedMessage(isUpdateFailed: false), - isNotNull, - ); - expect(localizations.photosLabel, isNotNull); - // today - expect( - localizations.sentAtText( - date: DateTime.now(), - time: DateTime.now(), - ), - isNotNull, - ); - // yesterday - expect( - localizations.sentAtText( - date: DateTime.now().subtract(const Duration(days: 1)), - time: DateTime.now(), - ), - isNotNull, - ); - // any other day - expect( - localizations.sentAtText( - date: DateTime.now().subtract(const Duration(days: 3)), - time: DateTime.now(), - ), - isNotNull, - ); - expect(localizations.todayLabel, isNotNull); - expect(localizations.yesterdayLabel, isNotNull); - expect(localizations.channelIsMutedText, isNotNull); - expect(localizations.noTitleText, isNotNull); - expect(localizations.letsStartChattingLabel, isNotNull); - expect(localizations.sendingFirstMessageLabel, isNotNull); - expect(localizations.startAChatLabel, isNotNull); - expect(localizations.loadingChannelsError, isNotNull); - expect(localizations.deleteConversationLabel, isNotNull); - expect(localizations.deleteConversationQuestion, isNotNull); - expect(localizations.streamChatLabel, isNotNull); - expect(localizations.searchingForNetworkText, isNotNull); - expect(localizations.offlineLabel, isNotNull); - expect(localizations.tryAgainLabel, isNotNull); - // 1 member - expect(localizations.membersCountText(1), isNotNull); - // 3 members - expect(localizations.membersCountText(3), isNotNull); - // 1 member - expect(localizations.watchersCountText(1), isNotNull); - // 3 members - expect(localizations.watchersCountText(3), isNotNull); - expect(localizations.viewInfoLabel, isNotNull); - expect(localizations.leaveGroupLabel, isNotNull); - expect(localizations.leaveLabel, isNotNull); - expect(localizations.leaveConversationLabel, isNotNull); - expect(localizations.leaveConversationQuestion, isNotNull); - expect(localizations.showInChatLabel, isNotNull); - expect(localizations.saveImageLabel, isNotNull); - expect(localizations.saveVideoLabel, isNotNull); - expect(localizations.uploadErrorLabel, isNotNull); - expect(localizations.giphyLabel, isNotNull); - expect(localizations.shuffleLabel, isNotNull); - expect(localizations.sendLabel, isNotNull); - expect(localizations.withText, isNotNull); - expect(localizations.inText, isNotNull); - expect(localizations.youText, isNotNull); - expect(localizations.galleryPaginationText, isNotNull); - expect(localizations.fileText, isNotNull); - expect(localizations.replyToMessageLabel, isNotNull); - expect(localizations.attachmentLimitExceedError(3), isNotNull); - expect( - localizations.galleryPaginationText(currentPage: 1, totalPages: 2), - isNotNull, - ); - expect(localizations.slowModeOnLabel, isNotNull); - expect(localizations.linkDisabledDetails, isNotNull); - expect(localizations.linkDisabledError, isNotNull); - expect(localizations.sendMessagePermissionError, isNotNull); - expect(localizations.couldNotReadBytesFromFileError, isNotNull); - expect(localizations.toggleMuteUnmuteAction(isMuted: false), isNotNull); - expect(localizations.downloadLabel, isNotNull); - expect(localizations.toggleMuteUnmuteGroupQuestion(isMuted: true), - isNotNull); - expect(localizations.toggleMuteUnmuteGroupText(isMuted: true), isNotNull); - expect( - localizations.toggleMuteUnmuteUserQuestion(isMuted: true), isNotNull); - expect(localizations.toggleMuteUnmuteUserText(isMuted: true), isNotNull); - expect(localizations.viewLibrary, isNotNull); - expect(localizations.unreadMessagesSeparatorText(), isNotNull); - expect(localizations.enableFileAccessMessage, isNotNull); - expect(localizations.allowFileAccessMessage, isNotNull); - expect( - localizations.unreadCountIndicatorLabel(unreadCount: 2), isNotNull); - expect(localizations.unreadMessagesSeparatorText(), isNotNull); - expect(localizations.markUnreadError, isNotNull); - expect(localizations.markAsUnreadLabel, isNotNull); - // Create poll - expect(localizations.createPollLabel(), isNotNull); - // Create a new poll - expect(localizations.createPollLabel(isNew: true), isNotNull); - expect(localizations.questionsLabel, isNotNull); - expect(localizations.askAQuestionLabel, isNotNull); - // Question must be at least 5 characters long - expect( - localizations.pollQuestionValidationError(3, const (min: 5, max: 10)), - isNotNull, - ); - // Question must be at most 10 characters long - expect( - localizations.pollQuestionValidationError(11, const (min: 5, max: 10)), - isNotNull, - ); - // Option - expect(localizations.optionLabel(), isNotNull); - // Options - expect(localizations.optionLabel(isPlural: true), isNotNull); - expect(localizations.pollOptionEmptyError, isNotNull); - expect(localizations.pollOptionDuplicateError, isNotNull); - expect(localizations.addAnOptionLabel, isNotNull); - expect(localizations.multipleAnswersLabel, isNotNull); - expect(localizations.maximumVotesPerPersonLabel, isNotNull); - // Vote count must be at least 1 - expect( - localizations.maxVotesPerPersonValidationError( - 0, - const (min: 1, max: 10), - ), - isNotNull, - ); - // Vote count must be at most 10 - expect( - localizations.maxVotesPerPersonValidationError( - 11, - const (min: 1, max: 10), - ), - isNotNull, - ); - expect(localizations.anonymousPollLabel, isNotNull); - expect(localizations.suggestAnOptionLabel, isNotNull); - expect(localizations.addACommentLabel, isNotNull); - expect(localizations.createLabel, isNotNull); - expect(localizations.endVoteLabel, isNotNull); - expect(localizations.enterANewOptionLabel, isNotNull); - expect(localizations.enterYourCommentLabel, isNotNull); - expect(localizations.loadingPollVotesError, isNotNull); - expect(localizations.noPollVotesLabel, isNotNull); - expect(localizations.pollCommentsLabel, isNotNull); - expect(localizations.pollOptionsLabel, isNotNull); - expect(localizations.pollResultsLabel, isNotNull); - // Voting mode - expect( - localizations.pollVotingModeLabel(const PollVotingMode.disabled()), - isNotNull, - ); - expect( - localizations.pollVotingModeLabel(const PollVotingMode.unique()), - isNotNull, - ); - expect( - localizations.pollVotingModeLabel( - const PollVotingMode.limited(count: 3), - ), - isNotNull, - ); - expect( - localizations.pollVotingModeLabel(const PollVotingMode.all()), - isNotNull, - ); - expect(localizations.seeAllOptionsLabel(), isNotNull); - expect(localizations.seeAllOptionsLabel(count: 3), isNotNull); - expect(localizations.showAllVotesLabel(), isNotNull); - expect(localizations.showAllVotesLabel(count: 3), isNotNull); - expect(localizations.updateYourCommentLabel, isNotNull); - expect(localizations.viewCommentsLabel, isNotNull); - expect(localizations.viewResultsLabel, isNotNull); - // Vote count - expect(localizations.voteCountLabel(), isNotNull); - expect(localizations.voteCountLabel(count: 3), isNotNull); - expect(localizations.repliedToLabel, isNotNull); - expect(localizations.newThreadsLabel(count: 3), isNotNull); - expect(localizations.slideToCancelLabel, isNotNull); - expect(localizations.holdToRecordLabel, isNotNull); - }); - } - - test('should throw if try to load locale which is not supported', () async { - const locale = Locale('not-supported-locale'); - try { - getStreamChatTranslation(locale); - } catch (e) { - expect(e, isA()); - } - }); - - test('`.toString`', () { - final supportedLocales = kStreamChatSupportedLanguages.length; - expect( - GlobalStreamChatLocalizations.delegate.toString(), - 'GlobalStreamChatLocalizations.delegate($supportedLocales locales)', - ); - }); -} diff --git a/packages/stream_chat_persistence/.metadata b/packages/stream_chat_persistence/.metadata deleted file mode 100644 index 5eb5034781..0000000000 --- a/packages/stream_chat_persistence/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: 78910062997c3a836feee883712c241a5fd22983 - channel: stable - -project_type: package diff --git a/packages/stream_chat_persistence/CHANGELOG.md b/packages/stream_chat_persistence/CHANGELOG.md deleted file mode 100644 index 2a32634fc4..0000000000 --- a/packages/stream_chat_persistence/CHANGELOG.md +++ /dev/null @@ -1,279 +0,0 @@ -## 9.4.0 - -- Updated minimum Flutter version to 3.27.4 for the SDK. - -## 9.3.0 - -- Updated `stream_chat` dependency to [`9.3.0`](https://pub.dev/packages/stream_chat/changelog). - -## 9.2.0 - -- Updated `stream_chat` dependency to [`9.2.0`](https://pub.dev/packages/stream_chat/changelog). - -## 9.1.0 - -- Updated `stream_chat` dependency to [`9.1.0`](https://pub.dev/packages/stream_chat/changelog). - -## 9.0.0 - -- Added support for `Poll` and `PollVote` entities in the database. -- Updated minimum Flutter version to 3.24.5 for the SDK. - -## 8.3.0 - -- Updated `stream_chat` dependency to [`8.3.0`](https://pub.dev/packages/stream_chat/changelog). - -## 8.2.0 - -- Updated `stream_chat` dependency to [`8.2.0`](https://pub.dev/packages/stream_chat/changelog). - -## 8.1.0 - -- Updated `stream_chat` dependency to [`8.1.0`](https://pub.dev/packages/stream_chat/changelog). - -## 8.0.0 - -- Updated `stream_chat` dependency to [`8.0.0`](https://pub.dev/packages/stream_chat/changelog). - -## 7.3.0 - -šŸ”„ Changed - -- Changed minimum Flutter version to 3.19 for the SDK. -- Updated `stream_chat` dependency to [`7.3.0`](https://pub.dev/packages/stream_chat/changelog). - -## 7.2.2 - -- Updated `stream_chat` dependency to [`7.2.2`](https://pub.dev/packages/stream_chat/changelog). - -## 7.2.1 - -- Updated `stream_chat` dependency to [`7.2.1`](https://pub.dev/packages/stream_chat/changelog). - -## 7.2.0-hotfix.1 - -- Updated `stream_chat` dependency to [`7.2.0-hotfix.1`](https://pub.dev/packages/stream_chat/changelog). - -## 7.2.0 - -- Updated `stream_chat` dependency to [`7.2.0`](https://pub.dev/packages/stream_chat/changelog). - -## 7.1.0 - -- Updated `stream_chat` dependency to [`7.1.0`](https://pub.dev/packages/stream_chat/changelog). - -## 7.0.2 - -- Updated `stream_chat` dependency to [`7.0.2`](https://pub.dev/packages/stream_chat/changelog). - -## 7.0.1 - -- Updated `stream_chat` dependency to [`7.0.1`](https://pub.dev/packages/stream_chat/changelog). - -## 7.0.0 - -- Updated minimum supported `SDK` version to Flutter 3.13/Dart 3.1 -- šŸ›‘ **BREAKING** Removed deprecated `getChannelStates.sort` parameter. Use `getChannelStates.channelStateSort` instead. - -## 6.10.0 - -- Updated `stream_chat` dependency to [`6.10.0`](https://pub.dev/packages/stream_chat/changelog). - -## 6.9.0 - -- Updated `stream_chat` dependency to [`6.9.0`](https://pub.dev/packages/stream_chat/changelog). - -## 6.8.0 - -- Updated minimum supported `SDK` version to Flutter 3.10/Dart 3.0 -- Updated `stream_chat` dependency to [`6.8.0`](https://pub.dev/packages/stream_chat/changelog). - -## 6.7.0 - -- [[#1683]](https://github.com/GetStream/stream-chat-flutter/issues/1683) Fixed SqliteException no such column `messages.state`. -- Updated `stream_chat` dependency to [`6.7.0`](https://pub.dev/packages/stream_chat/changelog). - -## 6.6.0 - -- Updated `stream_chat` dependency to [`6.6.0`](https://pub.dev/packages/stream_chat/changelog). - -## 6.5.0 - -- Updated minimum supported `SDK` version to Flutter 3.7/Dart 2.19 - -## 6.4.0 - -- Updated `stream_chat` dependency to [`6.4.0`](https://pub.dev/packages/stream_chat/changelog). - -## 6.3.0 - -- Updated `stream_chat` dependency to [`6.3.0`](https://pub.dev/packages/stream_chat/changelog). - -## 6.2.0 - -- Added support for `StreamChatPersistenceClient.isConnected` for checking if the client is connected to the database. -- [[#1422]](https://github.com/GetStream/stream-chat-flutter/issues/1422) Removed default values - from `UserEntity` `createdAt` and `updatedAt` fields. -- Updated `stream_chat` dependency to [`6.2.0`](https://pub.dev/packages/stream_chat/changelog). -- Added support for `StreamChatPersistenceClient.openPersistenceConnection` - and `StreamChatPersistenceClient.closePersistenceConnection` for opening and closing the database connection. - -## 6.1.0 - -- Updated `dart` sdk environment range to support `3.0.0`. -- Updated `stream_chat` dependency to [`6.1.0`](https://pub.dev/packages/stream_chat/changelog). - -## 6.0.0 - -- Updated `drift` to `^2.7.0`. -- Updated dependencies to resolvable versions. - -## 5.1.0 - -- Reintroduce support for experimental indexedDB on Web. -- Deprecated the `sort` parameter in the getChannelStates method in favor of `channelStateSort`. -- Use the comparator function to sort the channel states and not the channel models. - -šŸž Fixed - -- Fix offline message pagination. - -## 5.0.0 - -- Included the changes from version [4.3.0](#430) and [4.4.0](#440). - -## 5.0.0-beta.1 - -- Updated `stream_chat` dependency to [`5.0.0-beta.1`](https://pub.dev/packages/stream_chat/changelog). - -## 4.4.0 - -- Allowed experimental use of indexedDb on web with `webUseExperimentalIndexedDb` parameter - on `StreamChatPersistenceClient`. - Thanks [geweald](https://github.com/geweald). - -## 4.3.0 - -- Updated `stream_chat` dependency to [`4.4.0`](https://pub.dev/packages/stream_chat/changelog). - -## 4.2.0 - -- Added support for `Channel.ownCapabilities` - -## 4.1.0 - -šŸ”„ Changed - -- Deprecated `role` field in `Member` table in favor of `channelRole` - -## 4.0.1 - -- Updated `stream_chat` dependency to [`4.0.1`](https://pub.dev/packages/stream_chat/changelog). - -## 4.0.0 - -- Updated `stream_chat` dependency to [`4.0.0`](https://pub.dev/packages/stream_chat/changelog). - -## 4.0.0-beta.0 - -- Updated `stream_chat` dependency to [`4.0.0-beta.0`](https://pub.dev/packages/stream_chat/changelog). - -## 3.1.0 - -- Bump `drift` to `1.3.0`. - -## 3.0.0 - -- Updated `stream_chat` dependency to [`3.0.0`](https://pub.dev/packages/stream_chat/changelog). -- [[#604]](https://github.com/GetStream/stream-chat-flutter/issues/604) Fix cascade deletion by enabling `pragma foreign_keys`. -- Added a new table `PinnedMessageReactions` and dao `PinnedMessageReactionDao` specifically for pinned messages. - -## 2.2.0 - -- Updated llc dependency -- Added support for message.i18n -- Added support for user.language - -## 2.1.1 - -- Updated llc dependency - -## 2.1.0 - -āœ… Added - -- Added support for `Message.i18n` -- Added support for `User.language` - -## 2.0.0 - -* Migrate this package to null safety -* Minor fixes and improvements - -## 2.0.0-nullsafety.8 - -* Updated llc dependency -* Upgraded moor dependencies and generated files with the latest dependency - -## 2.0.0-nullsafety.7 - -* Update llc dependency -* Minor fixes and improvements - -## 2.0.0-nullsafety.5 - -* Update llc dependency -* Minor fixes and improvements - -## 2.0.0-nullsafety.2 - -* Update llc dependency -* Minor fixes and improvements -* Fixed bug not saving message.mentioned_users - -## 2.0.0-nullsafety.1 - -* Migrate this package to null safety -* Update llc dependency - -## 1.5.2 - -* Fix sorting by last_updated - -## 1.5.1 - -* Improved test coverage to > 95% -* Minor fixes and improvements - -## 1.5.0 - -* Update llc dependency -* Wait for all operations to finish before disconnecting - -## 1.4.0-beta - -* Update llc dependency - -## 1.3.0-beta - -* Update llc dependency - -## 1.2.0-beta - -* Update llc dependency - -## 1.1.0-beta - -* Update llc dependency - -## 1.0.2-beta - -* Update llc dependency - -## 1.0.1-beta - -* Update llc dependency - -## 1.0.0-beta - -* Initial release \ No newline at end of file diff --git a/packages/stream_chat_persistence/LICENSE b/packages/stream_chat_persistence/LICENSE deleted file mode 100644 index 49088d477f..0000000000 --- a/packages/stream_chat_persistence/LICENSE +++ /dev/null @@ -1,219 +0,0 @@ -SOURCE CODE LICENSE AGREEMENT - -IMPORTANT - READ THIS CAREFULLY BEFORE DOWNLOADING, INSTALLING, USING OR -ELECTRONICALLY ACCESSING THIS PROPRIETARY PRODUCT. - -THIS IS A LEGAL AGREEMENT BETWEEN STREAM.IO, INC. (ā€œSTREAM.IOā€) AND THE -BUSINESS ENTITY OR PERSON FOR WHOM YOU (ā€œYOUā€) ARE ACTING (ā€œCUSTOMERā€) AS THE -LICENSEE OF THE PROPRIETARY SOFTWARE INTO WHICH THIS AGREEMENT HAS BEEN -INCLUDED (THE ā€œAGREEMENTā€). YOU AGREE THAT YOU ARE THE CUSTOMER, OR YOU ARE AN -EMPLOYEE OR AGENT OF CUSTOMER AND ARE ENTERING INTO THIS AGREEMENT FOR LICENSE -OF THE SOFTWARE BY CUSTOMER FOR CUSTOMER’S BUSINESS PURPOSES AS DESCRIBED IN -AND IN ACCORDANCE WITH THIS AGREEMENT. YOU HEREBY AGREE THAT YOU ENTER INTO -THIS AGREEMENT ON BEHALF OF CUSTOMER AND THAT YOU HAVE THE AUTHORITY TO BIND -CUSTOMER TO THIS AGREEMENT. - -STREAM.IO IS WILLING TO LICENSE THE SOFTWARE TO CUSTOMER ONLY ON THE FOLLOWING -CONDITIONS: (1) YOU ARE A CURRENT CUSTOMER OF STREAM.IO; (2) YOU ARE NOT A -COMPETITOR OF STREAM.IO; AND (3) THAT YOU ACCEPT ALL THE TERMS IN THIS -AGREEMENT. BY DOWNLOADING, INSTALLING, CONFIGURING, ACCESSING OR OTHERWISE -USING THE SOFTWARE, INCLUDING ANY UPDATES, UPGRADES, OR NEWER VERSIONS, YOU -REPRESENT, WARRANT AND ACKNOWLEDGE THAT (A) CUSTOMER IS A CURRENT CUSTOMER OF -STREAM.IO; (B) CUSTOMER IS NOT A COMPETITOR OF STREAM.IO; AND THAT (C) YOU HAVE -READ THIS AGREEMENT, UNDERSTAND THIS AGREEMENT, AND THAT CUSTOMER AGREES TO BE -BOUND BY ALL THE TERMS OF THIS AGREEMENT. - -IF YOU DO NOT AGREE TO ALL THE TERMS AND CONDITIONS OF THIS AGREEMENT, -STREAM.IO IS UNWILLING TO LICENSE THE SOFTWARE TO CUSTOMER, AND THEREFORE, DO -NOT COMPLETE THE DOWNLOAD PROCESS, ACCESS OR OTHERWISE USE THE SOFTWARE, AND -CUSTOMER SHOULD IMMEDIATELY RETURN THE SOFTWARE AND CEASE ANY USE OF THE -SOFTWARE. - -1. SOFTWARE. The Stream.io software accompanying this Agreement, may include -Source Code, Executable Object Code, associated media, printed materials and -documentation (collectively, the ā€œSoftwareā€). The Software also includes any -updates or upgrades to or new versions of the original Software, if and when -made available to you by Stream.io. ā€œSource Codeā€ means computer programming -code in human readable form that is not suitable for machine execution without -the intervening steps of interpretation or compilation. ā€œExecutable Object -Code" means the computer programming code in any other form than Source Code -that is not readily perceivable by humans and suitable for machine execution -without the intervening steps of interpretation or compilation. ā€œSiteā€ means a -Customer location controlled by Customer. ā€œAuthorized Userā€ means any employee -or contractor of Customer working at the Site, who has signed a written -confidentiality agreement with Customer or is otherwise bound in writing by -confidentiality and use obligations at least as restrictive as those imposed -under this Agreement. - -2. LICENSE GRANT. Subject to the terms and conditions of this Agreement, in -consideration for the representations, warranties, and covenants made by -Customer in this Agreement, Stream.io grants to Customer, during the term of -this Agreement, a personal, non-exclusive, non-transferable, non-sublicensable -license to: - -a. install and use Software Source Code on password protected computers at a Site, -restricted to Authorized Users; - -b. create derivative works, improvements (whether or not patentable), extensions -and other modifications to the Software Source Code (ā€œModificationsā€) to build -unique scalable newsfeeds, activity streams, and in-app messaging via Stream’s -application program interface (ā€œAPIā€); - -c. compile the Software Source Code to create Executable Object Code versions of -the Software Source Code and Modifications to build such newsfeeds, activity -streams, and in-app messaging via the API; - -d. install, execute and use such Executable Object Code versions solely for -Customer’s internal business use (including development of websites through -which data generated by Stream services will be streamed (ā€œAppsā€)); - -e. use and distribute such Executable Object Code as part of Customer’s Apps; and - -f. make electronic copies of the Software and Modifications as required for backup -or archival purposes. - -3. RESTRICTIONS. Customer is responsible for all activities that occur in -connection with the Software. Customer will not, and will not attempt to: (a) -sublicense or transfer the Software or any Source Code related to the Software -or any of Customer’s rights under this Agreement, except as otherwise provided -in this Agreement, (b) use the Software Source Code for the benefit of a third -party or to operate a service; (c) allow any third party to access or use the -Software Source Code; (d) sublicense or distribute the Software Source Code or -any Modifications in Source Code or other derivative works based on any part of -the Software Source Code; (e) use the Software in any manner that competes with -Stream.io or its business; or (e) otherwise use the Software in any manner that -exceeds the scope of use permitted in this Agreement. Customer shall use the -Software in compliance with any accompanying documentation any laws applicable -to Customer. - -4. OPEN SOURCE. Customer and its Authorized Users shall not use any software or -software components that are open source in conjunction with the Software -Source Code or any Modifications in Source Code or in any way that could -subject the Software to any open source licenses. - -5. CONTRACTORS. Under the rights granted to Customer under this Agreement, -Customer may permit its employees, contractors, and agencies of Customer to -become Authorized Users to exercise the rights to the Software granted to -Customer in accordance with this Agreement solely on behalf of Customer to -provide services to Customer; provided that Customer shall be liable for the -acts and omissions of all Authorized Users to the extent any of such acts or -omissions, if performed by Customer, would constitute a breach of, or otherwise -give rise to liability to Customer under, this Agreement. Customer shall not -and shall not permit any Authorized User to use the Software except as -expressly permitted in this Agreement. - -6. COMPETITIVE PRODUCT DEVELOPMENT. Customer shall not use the Software in any way -to engage in the development of products or services which could be reasonably -construed to provide a complete or partial functional or commercial alternative -to Stream.io’s products or services (a ā€œCompetitive Productā€). Customer shall -ensure that there is no direct or indirect use of, or sharing of, Software -source code, or other information based upon or derived from the Software to -develop such products or services. Without derogating from the generality of -the foregoing, development of Competitive Products shall include having direct -or indirect access to, supervising, consulting or assisting in the development -of, or producing any specifications, documentation, object code or source code -for, all or part of a Competitive Product. - -7. LIMITATION ON MODIFICATIONS. Notwithstanding any provision in this Agreement, -Modifications may only be created and used by Customer as permitted by this -Agreement and Modification Source Code may not be distributed to third parties. -Customer will not assert against Stream.io, its affiliates, or their customers, -direct or indirect, agents and contractors, in any way, any patent rights that -Customer may obtain relating to any Modifications for Stream.io, its -affiliates’, or their customers’, direct or indirect, agents’ and contractors’ -manufacture, use, import, offer for sale or sale of any Stream.io products or -services. - -8. DELIVERY AND ACCEPTANCE. The Software will be delivered electronically pursuant -to Stream.io standard download procedures. The Software is deemed accepted upon -delivery. - -9. IMPLEMENTATION AND SUPPORT. Stream.io has no obligation under this Agreement to -provide any support or consultation concerning the Software. - -10. TERM AND TERMINATION. The term of this Agreement begins when the Software is -downloaded or accessed and shall continue until terminated. Either party may -terminate this Agreement upon written notice. This Agreement shall -automatically terminate if Customer is or becomes a competitor of Stream.io or -makes or sells any Competitive Products. Upon termination of this Agreement for -any reason, (a) all rights granted to Customer in this Agreement immediately -cease to exist, (b) Customer must promptly discontinue all use of the Software -and return to Stream.io or destroy all copies of the Software in Customer’s -possession or control. Any continued use of the Software by Customer or attempt -by Customer to exercise any rights under this Agreement after this Agreement -has terminated shall be considered copyright infringement and subject Customer -to applicable remedies for copyright infringement. Sections 2, 5, 6, 8 and 9 -shall survive expiration or termination of this Agreement for any reason. - -11. OWNERSHIP. As between the parties, the Software and all worldwide intellectual -property rights and proprietary rights relating thereto or embodied therein, -are the exclusive property of Stream.io and its suppliers. Stream.io and its -suppliers reserve all rights in and to the Software not expressly granted to -Customer in this Agreement, and no other licenses or rights are granted by -implication, estoppel or otherwise. - -12. WARRANTY DISCLAIMER. USE OF THIS SOFTWARE IS ENTIRELY AT YOURS AND CUSTOMER’S -OWN RISK. THE SOFTWARE IS PROVIDED ā€œAS ISā€ WITHOUT ANY WARRANTY OF ANY KIND -WHATSOEVER. STREAM.IO DOES NOT MAKE, AND HEREBY DISCLAIMS, ANY WARRANTY OF ANY -KIND, WHETHER EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING WITHOUT -LIMITATION, THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -PURPOSE, TITLE, NON-INFRINGEMENT OF THIRD-PARTY RIGHTS, RESULTS, EFFORTS, -QUALITY OR QUIET ENJOYMENT. STREAM.IO DOES NOT WARRANT THAT THE SOFTWARE IS -ERROR-FREE, WILL FUNCTION WITHOUT INTERRUPTION, WILL MEET ANY SPECIFIC NEED -THAT CUSTOMER HAS, THAT ALL DEFECTS WILL BE CORRECTED OR THAT IT IS -SUFFICIENTLY DOCUMENTED TO BE USABLE BY CUSTOMER. TO THE EXTENT THAT STREAM.IO -MAY NOT DISCLAIM ANY WARRANTY AS A MATTER OF APPLICABLE LAW, THE SCOPE AND -DURATION OF SUCH WARRANTY WILL BE THE MINIMUM PERMITTED UNDER SUCH LAW. -CUSTOMER ACKNOWLEDGES THAT IT HAS RELIED ON NO WARRANTIES OTHER THAN THE -EXPRESS WARRANTIES IN THIS AGREEMENT. - -13. LIMITATION OF LIABILITY. TO THE FULLEST EXTENT PERMISSIBLE BY LAW, STREAM.IO’S -TOTAL LIABILITY FOR ALL DAMAGES ARISING OUT OF OR RELATED TO THE SOFTWARE OR -THIS AGREEMENT, WHETHER IN CONTRACT, TORT (INCLUDING NEGLIGENCE) OR OTHERWISE, -SHALL NOT EXCEED $100. IN NO EVENT WILL STREAM.IO BE LIABLE FOR ANY INDIRECT, -CONSEQUENTIAL, EXEMPLARY, PUNITIVE, SPECIAL OR INCIDENTAL DAMAGES OF ANY KIND -WHATSOEVER, INCLUDING ANY LOST DATA AND LOST PROFITS, ARISING FROM OR RELATING -TO THE SOFTWARE EVEN IF STREAM.IO HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. CUSTOMER ACKNOWLEDGES THAT THIS PROVISION REFLECTS THE AGREED UPON -ALLOCATION OF RISK FOR THIS AGREEMENT AND THAT STREAM.IO WOULD NOT ENTER INTO -THIS AGREEMENT WITHOUT THESE LIMITATIONS ON ITS LIABILITY. - -14. General. Customer may not assign or transfer this Agreement, by operation of -law or otherwise, or any of its rights under this Agreement (including the -license rights granted to Customer) to any third party without Stream.io’s -prior written consent, which consent will not be unreasonably withheld or -delayed. Stream.io may assign this Agreement, without consent, including, but -limited to, affiliate or any successor to all or substantially all its business -or assets to which this Agreement relates, whether by merger, sale of assets, -sale of stock, reorganization or otherwise. Any attempted assignment or -transfer in violation of the foregoing will be null and void. Stream.io shall -not be liable hereunder by reason of any failure or delay in the performance of -its obligations hereunder for any cause which is beyond the reasonable control. -All notices, consents, and approvals under this Agreement must be delivered in -writing by courier, by electronic mail, or by certified or registered mail, -(postage prepaid and return receipt requested) to the other party at the -address set forth in the customer agreement between Stream.io and Customer and -will be effective upon receipt or when delivery is refused. This Agreement will -be governed by and interpreted in accordance with the laws of the State of -Colorado, without reference to its choice of laws rules. The United Nations -Convention on Contracts for the International Sale of Goods does not apply to -this Agreement. Any action or proceeding arising from or relating to this -Agreement shall be brought in a federal or state court in Denver, Colorado, and -each party irrevocably submits to the jurisdiction and venue of any such court -in any such action or proceeding. All waivers must be in writing. Any waiver or -failure to enforce any provision of this Agreement on one occasion will not be -deemed a waiver of any other provision or of such provision on any other -occasion. If any provision of this Agreement is unenforceable, such provision -will be changed and interpreted to accomplish the objectives of such provision -to the greatest extent possible under applicable law and the remaining -provisions will continue in full force and effect. Customer shall not violate -any applicable law, rule or regulation, including those regarding the export of -technical data. The headings of Sections of this Agreement are for convenience -and are not to be used in interpreting this Agreement. As used in this -Agreement, the word ā€œincludingā€ means ā€œincluding but not limited to.ā€ This -Agreement (including all exhibits and attachments) constitutes the entire -agreement between the parties regarding the subject hereof and supersedes all -prior or contemporaneous agreements, understandings and communication, whether -written or oral. This Agreement may be amended only by a written document -signed by both parties. The terms of any purchase order or similar document -submitted by Customer to Stream.io will have no effect. diff --git a/packages/stream_chat_persistence/README.md b/packages/stream_chat_persistence/README.md deleted file mode 100644 index 1d7f8bafe7..0000000000 --- a/packages/stream_chat_persistence/README.md +++ /dev/null @@ -1,77 +0,0 @@ -# Official Chat Persistence Client for [Stream Chat](https://getstream.io/chat/) - -> The official Chat Persistence Client for Stream Chat, a service for -> building chat applications. - -[![Pub](https://img.shields.io/pub/v/stream_chat_persistence.svg)](https://pub.dartlang.org/packages/stream_chat_persistence) -![](https://img.shields.io/badge/platform-flutter%20%7C%20flutter%20web-ff69b4.svg?style=flat-square) -![CI](https://github.com/GetStream/stream-chat-flutter/workflows/stream_flutter_workflow/badge.svg?branch=master) - - -**Quick Links** - -- [Register](https://getstream.io/chat/trial/) to get an API key for Stream Chat -- [Flutter Chat Tutorial](https://getstream.io/chat/flutter/tutorial/) -- [Chat UI Kit](https://getstream.io/chat/ui-kit/) -- [Flutter Offline Docs](https://getstream.io/chat/docs/flutter-dart/flutter_offline/) - -This package provides a persistence client for fetching and saving chat data locally. -Stream Chat Persistence uses [Moor](https://github.com/simolus3/moor) as a disk cache. - -### Changelog - -Check out the [changelog on pub.dev](https://pub.dev/packages/stream_chat_persistence/changelog) to see the latest changes in the package. - -## Add dependency -Add this to your package's pubspec.yaml file, use the latest version [![Pub](https://img.shields.io/pub/v/stream_chat_persistence.svg)](https://pub.dartlang.org/packages/stream_chat_persistence) -```yaml -dependencies: - stream_chat_persistence: ^latest_version -``` - -You should then run `flutter packages get` - -## Usage -The usage is pretty simple. -1. Create a new instance of StreamChatPersistenceClient providing `logLevel` and `connectionMode`. -```dart -final chatPersistentClient = StreamChatPersistenceClient( - logLevel: Level.INFO, - connectionMode: ConnectionMode.regular, -); -``` -2. Pass the instance to the official Stream chat client. -```dart - final client = StreamChatClient( - apiKey ?? kDefaultStreamApiKey, - logLevel: Level.INFO, - )..chatPersistenceClient = chatPersistentClient; -``` - -And you are ready to go... - -## Flutter Web - -Due to Moor web (for offline storage) you need to include the sql.js library: - -```html - - - - - - - - - -``` - -You can grab the latest version of sql-wasm.js and sql-wasm.wasm [here](https://github.com/sql-js/sql.js/releases) and copy them into your `/web` folder. - -## Contributing - -We welcome code changes that improve this library or fix a problem, -please make sure to follow all best practices and add tests if applicable before submitting a Pull Request on Github. -We are pleased to merge your code into the official repository. -Make sure to sign our [Contributor License Agreement (CLA)](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) first. -See our license file for more details. diff --git a/packages/stream_chat_persistence/build.yaml b/packages/stream_chat_persistence/build.yaml deleted file mode 100644 index 1688f11b0d..0000000000 --- a/packages/stream_chat_persistence/build.yaml +++ /dev/null @@ -1,8 +0,0 @@ -targets: - $default: - builders: - drift_dev: - options: - data_class_to_companions: false - apply_converters_on_variables: true - generate_values_in_copy_with: true diff --git a/packages/stream_chat_persistence/drift_schemas/drift_schema_v1.json b/packages/stream_chat_persistence/drift_schemas/drift_schema_v1.json deleted file mode 100644 index 1a1b39a9ad..0000000000 --- a/packages/stream_chat_persistence/drift_schemas/drift_schema_v1.json +++ /dev/null @@ -1,798 +0,0 @@ -{ - "_meta": { - "description": "This file contains a serialized version of schema entities for moor.", - "version": "0.1.0-dev-preview" - }, - "entities": [ - { - "id": 0, - "references": [], - "type": "table", - "data": { - "name": "channels", - "was_declared_in_moor": false, - "columns": [ - { - "name": "id", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "type", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "cid", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "config", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "MapConverter()", - "dart_type_name": "Map" - } - }, - { - "name": "frozen", - "moor_type": "ColumnType.boolean", - "nullable": false, - "customConstraints": null, - "default_dart": "Constant(false)", - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "last_message_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "created_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "updated_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "deleted_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "member_count", - "moor_type": "ColumnType.integer", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "created_by_id", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "extra_data", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "MapConverter()", - "dart_type_name": "Map" - } - } - ], - "is_virtual": false, - "explicit_pk": [ - "cid" - ] - } - }, - { - "id": 1, - "references": [], - "type": "table", - "data": { - "name": "messages", - "was_declared_in_moor": false, - "columns": [ - { - "name": "id", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "message_text", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "attachments", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "ListConverter()", - "dart_type_name": "List" - } - }, - { - "name": "status", - "moor_type": "ColumnType.integer", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "MessageSendingStatusConverter()", - "dart_type_name": "MessageSendingStatus" - } - }, - { - "name": "type", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "mentioned_users", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "ListConverter()", - "dart_type_name": "List" - } - }, - { - "name": "reaction_counts", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "MapConverter()", - "dart_type_name": "Map" - } - }, - { - "name": "reaction_scores", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "MapConverter()", - "dart_type_name": "Map" - } - }, - { - "name": "parent_id", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "quoted_message_id", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "reply_count", - "moor_type": "ColumnType.integer", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "show_in_channel", - "moor_type": "ColumnType.boolean", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "shadowed", - "moor_type": "ColumnType.boolean", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "command", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "created_at", - "moor_type": "ColumnType.datetime", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "updated_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "deleted_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "user_id", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "channel_cid", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": "NULLABLE REFERENCES channels(cid) ON DELETE CASCADE", - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "extra_data", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "MapConverter()", - "dart_type_name": "Map" - } - } - ], - "is_virtual": false, - "explicit_pk": [ - "id" - ] - } - }, - { - "id": 2, - "references": [], - "type": "table", - "data": { - "name": "reactions", - "was_declared_in_moor": false, - "columns": [ - { - "name": "user_id", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "message_id", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": "REFERENCES messages(id) ON DELETE CASCADE", - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "type", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "created_at", - "moor_type": "ColumnType.datetime", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "score", - "moor_type": "ColumnType.integer", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "extra_data", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "MapConverter()", - "dart_type_name": "Map" - } - } - ], - "is_virtual": false, - "explicit_pk": [ - "message_id", - "type", - "user_id" - ] - } - }, - { - "id": 3, - "references": [], - "type": "table", - "data": { - "name": "users", - "was_declared_in_moor": false, - "columns": [ - { - "name": "id", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "role", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "created_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "updated_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "last_active", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "online", - "moor_type": "ColumnType.boolean", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "banned", - "moor_type": "ColumnType.boolean", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "extra_data", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "MapConverter()", - "dart_type_name": "Map" - } - } - ], - "is_virtual": false, - "explicit_pk": [ - "id" - ] - } - }, - { - "id": 4, - "references": [], - "type": "table", - "data": { - "name": "members", - "was_declared_in_moor": false, - "columns": [ - { - "name": "user_id", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "channel_cid", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": "REFERENCES channels(cid) ON DELETE CASCADE", - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "role", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "invite_accepted_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "invite_rejected_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "invited", - "moor_type": "ColumnType.boolean", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "banned", - "moor_type": "ColumnType.boolean", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "shadow_banned", - "moor_type": "ColumnType.boolean", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "is_moderator", - "moor_type": "ColumnType.boolean", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "created_at", - "moor_type": "ColumnType.datetime", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "updated_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - } - ], - "is_virtual": false, - "explicit_pk": [ - "user_id", - "channel_cid" - ] - } - }, - { - "id": 5, - "references": [], - "type": "table", - "data": { - "name": "reads", - "was_declared_in_moor": false, - "columns": [ - { - "name": "last_read", - "moor_type": "ColumnType.datetime", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "user_id", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "channel_cid", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": "REFERENCES channels(cid) ON DELETE CASCADE", - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "unread_messages", - "moor_type": "ColumnType.integer", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - } - ], - "is_virtual": false, - "explicit_pk": [ - "user_id", - "channel_cid" - ] - } - }, - { - "id": 6, - "references": [], - "type": "table", - "data": { - "name": "channel_queries", - "was_declared_in_moor": false, - "columns": [ - { - "name": "query_hash", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "channel_cid", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - } - ], - "is_virtual": false, - "explicit_pk": [ - "query_hash", - "channel_cid" - ] - } - }, - { - "id": 7, - "references": [], - "type": "table", - "data": { - "name": "connection_events", - "was_declared_in_moor": false, - "columns": [ - { - "name": "id", - "moor_type": "ColumnType.integer", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "own_user", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "MapConverter()", - "dart_type_name": "Map" - } - }, - { - "name": "total_unread_count", - "moor_type": "ColumnType.integer", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "unread_channels", - "moor_type": "ColumnType.integer", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "last_event_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "last_sync_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - } - ], - "is_virtual": false, - "explicit_pk": [ - "id" - ] - } - } - ] -} \ No newline at end of file diff --git a/packages/stream_chat_persistence/drift_schemas/drift_schema_v2.json b/packages/stream_chat_persistence/drift_schemas/drift_schema_v2.json deleted file mode 100644 index 0f1caee528..0000000000 --- a/packages/stream_chat_persistence/drift_schemas/drift_schema_v2.json +++ /dev/null @@ -1,1089 +0,0 @@ -{ - "_meta": { - "description": "This file contains a serialized version of schema entities for moor.", - "version": "0.1.0-dev-preview" - }, - "entities": [ - { - "id": 0, - "references": [], - "type": "table", - "data": { - "name": "channels", - "was_declared_in_moor": false, - "columns": [ - { - "name": "id", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "type", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "cid", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "config", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "MapConverter()", - "dart_type_name": "Map" - } - }, - { - "name": "frozen", - "moor_type": "ColumnType.boolean", - "nullable": false, - "customConstraints": null, - "default_dart": "Constant(false)", - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "last_message_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "created_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "updated_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "deleted_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "member_count", - "moor_type": "ColumnType.integer", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "created_by_id", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "extra_data", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "MapConverter()", - "dart_type_name": "Map" - } - } - ], - "is_virtual": false, - "explicit_pk": [ - "cid" - ] - } - }, - { - "id": 1, - "references": [], - "type": "table", - "data": { - "name": "messages", - "was_declared_in_moor": false, - "columns": [ - { - "name": "id", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "message_text", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "attachments", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "ListConverter()", - "dart_type_name": "List" - } - }, - { - "name": "status", - "moor_type": "ColumnType.integer", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "MessageSendingStatusConverter()", - "dart_type_name": "MessageSendingStatus" - } - }, - { - "name": "type", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "mentioned_users", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "ListConverter()", - "dart_type_name": "List" - } - }, - { - "name": "reaction_counts", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "MapConverter()", - "dart_type_name": "Map" - } - }, - { - "name": "reaction_scores", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "MapConverter()", - "dart_type_name": "Map" - } - }, - { - "name": "parent_id", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "quoted_message_id", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "reply_count", - "moor_type": "ColumnType.integer", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "show_in_channel", - "moor_type": "ColumnType.boolean", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "shadowed", - "moor_type": "ColumnType.boolean", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "command", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "created_at", - "moor_type": "ColumnType.datetime", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "updated_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "deleted_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "user_id", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "pinned", - "moor_type": "ColumnType.boolean", - "nullable": false, - "customConstraints": null, - "default_dart": "const Constant(false)", - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "pinned_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "pin_expires", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "pinned_by_user_id", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "channel_cid", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": "NULLABLE REFERENCES channels(cid) ON DELETE CASCADE", - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "extra_data", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "MapConverter()", - "dart_type_name": "Map" - } - } - ], - "is_virtual": false, - "explicit_pk": [ - "id" - ] - } - }, - { - "id": 2, - "references": [], - "type": "table", - "data": { - "name": "pinned_messages", - "was_declared_in_moor": false, - "columns": [ - { - "name": "id", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "message_text", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "attachments", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "ListConverter()", - "dart_type_name": "List" - } - }, - { - "name": "status", - "moor_type": "ColumnType.integer", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "MessageSendingStatusConverter()", - "dart_type_name": "MessageSendingStatus" - } - }, - { - "name": "type", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "mentioned_users", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "ListConverter()", - "dart_type_name": "List" - } - }, - { - "name": "reaction_counts", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "MapConverter()", - "dart_type_name": "Map" - } - }, - { - "name": "reaction_scores", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "MapConverter()", - "dart_type_name": "Map" - } - }, - { - "name": "parent_id", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "quoted_message_id", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "reply_count", - "moor_type": "ColumnType.integer", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "show_in_channel", - "moor_type": "ColumnType.boolean", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "shadowed", - "moor_type": "ColumnType.boolean", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "command", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "created_at", - "moor_type": "ColumnType.datetime", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "updated_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "deleted_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "user_id", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "pinned", - "moor_type": "ColumnType.boolean", - "nullable": false, - "customConstraints": null, - "default_dart": "const Constant(false)", - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "pinned_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "pin_expires", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "pinned_by_user_id", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "channel_cid", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": "NULLABLE REFERENCES channels(cid) ON DELETE CASCADE", - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "extra_data", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "MapConverter()", - "dart_type_name": "Map" - } - } - ], - "is_virtual": false, - "explicit_pk": [ - "id" - ] - } - }, - { - "id": 3, - "references": [], - "type": "table", - "data": { - "name": "reactions", - "was_declared_in_moor": false, - "columns": [ - { - "name": "user_id", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "message_id", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": "REFERENCES messages(id) ON DELETE CASCADE", - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "type", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "created_at", - "moor_type": "ColumnType.datetime", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "score", - "moor_type": "ColumnType.integer", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "extra_data", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "MapConverter()", - "dart_type_name": "Map" - } - } - ], - "is_virtual": false, - "explicit_pk": [ - "message_id", - "type", - "user_id" - ] - } - }, - { - "id": 4, - "references": [], - "type": "table", - "data": { - "name": "users", - "was_declared_in_moor": false, - "columns": [ - { - "name": "id", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "role", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "created_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "updated_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "last_active", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "online", - "moor_type": "ColumnType.boolean", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "banned", - "moor_type": "ColumnType.boolean", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "extra_data", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "MapConverter()", - "dart_type_name": "Map" - } - } - ], - "is_virtual": false, - "explicit_pk": [ - "id" - ] - } - }, - { - "id": 5, - "references": [], - "type": "table", - "data": { - "name": "members", - "was_declared_in_moor": false, - "columns": [ - { - "name": "user_id", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "channel_cid", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": "REFERENCES channels(cid) ON DELETE CASCADE", - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "role", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "invite_accepted_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "invite_rejected_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "invited", - "moor_type": "ColumnType.boolean", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "banned", - "moor_type": "ColumnType.boolean", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "shadow_banned", - "moor_type": "ColumnType.boolean", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "is_moderator", - "moor_type": "ColumnType.boolean", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "created_at", - "moor_type": "ColumnType.datetime", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "updated_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - } - ], - "is_virtual": false, - "explicit_pk": [ - "user_id", - "channel_cid" - ] - } - }, - { - "id": 6, - "references": [], - "type": "table", - "data": { - "name": "reads", - "was_declared_in_moor": false, - "columns": [ - { - "name": "last_read", - "moor_type": "ColumnType.datetime", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "user_id", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "channel_cid", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": "REFERENCES channels(cid) ON DELETE CASCADE", - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "unread_messages", - "moor_type": "ColumnType.integer", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - } - ], - "is_virtual": false, - "explicit_pk": [ - "user_id", - "channel_cid" - ] - } - }, - { - "id": 7, - "references": [], - "type": "table", - "data": { - "name": "channel_queries", - "was_declared_in_moor": false, - "columns": [ - { - "name": "query_hash", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "channel_cid", - "moor_type": "ColumnType.text", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - } - ], - "is_virtual": false, - "explicit_pk": [ - "query_hash", - "channel_cid" - ] - } - }, - { - "id": 8, - "references": [], - "type": "table", - "data": { - "name": "connection_events", - "was_declared_in_moor": false, - "columns": [ - { - "name": "id", - "moor_type": "ColumnType.integer", - "nullable": false, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "own_user", - "moor_type": "ColumnType.text", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [], - "type_converter": { - "dart_expr": "MapConverter()", - "dart_type_name": "Map" - } - }, - { - "name": "total_unread_count", - "moor_type": "ColumnType.integer", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "unread_channels", - "moor_type": "ColumnType.integer", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "last_event_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - }, - { - "name": "last_sync_at", - "moor_type": "ColumnType.datetime", - "nullable": true, - "customConstraints": null, - "default_dart": null, - "default_client_dart": null, - "dsl_features": [] - } - ], - "is_virtual": false, - "explicit_pk": [ - "id" - ] - } - } - ] -} \ No newline at end of file diff --git a/packages/stream_chat_persistence/example/.gitignore b/packages/stream_chat_persistence/example/.gitignore deleted file mode 100644 index 9d532b18a0..0000000000 --- a/packages/stream_chat_persistence/example/.gitignore +++ /dev/null @@ -1,41 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -**/doc/api/ -**/ios/Flutter/.last_build_id -.dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies -.packages -.pub-cache/ -.pub/ -/build/ - -# Web related -lib/generated_plugin_registrant.dart - -# Symbolication related -app.*.symbols - -# Obfuscation related -app.*.map.json diff --git a/packages/stream_chat_persistence/example/.metadata b/packages/stream_chat_persistence/example/.metadata deleted file mode 100644 index 182cccafd9..0000000000 --- a/packages/stream_chat_persistence/example/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: 78910062997c3a836feee883712c241a5fd22983 - channel: stable - -project_type: app diff --git a/packages/stream_chat_persistence/example/README.md b/packages/stream_chat_persistence/example/README.md deleted file mode 100644 index 07e5ac1877..0000000000 --- a/packages/stream_chat_persistence/example/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# Stream Chat Persistence Example -Please see `lib/` for example code. \ No newline at end of file diff --git a/packages/stream_chat_persistence/example/android/.gitignore b/packages/stream_chat_persistence/example/android/.gitignore deleted file mode 100644 index 6f568019d3..0000000000 --- a/packages/stream_chat_persistence/example/android/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -gradle-wrapper.jar -/.gradle -/captures/ -/gradlew -/gradlew.bat -/local.properties -GeneratedPluginRegistrant.java - -# Remember to never publicly share your keystore. -# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app -key.properties -**/*.keystore -**/*.jks diff --git a/packages/stream_chat_persistence/example/android/app/build.gradle b/packages/stream_chat_persistence/example/android/app/build.gradle deleted file mode 100644 index 3ae24303d6..0000000000 --- a/packages/stream_chat_persistence/example/android/app/build.gradle +++ /dev/null @@ -1,68 +0,0 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -android { - compileSdkVersion 33 - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.example" - minSdkVersion flutter.minSdkVersion - targetSdkVersion 33 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/packages/stream_chat_persistence/example/android/app/src/debug/AndroidManifest.xml b/packages/stream_chat_persistence/example/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index c208884f30..0000000000 --- a/packages/stream_chat_persistence/example/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/packages/stream_chat_persistence/example/android/app/src/main/AndroidManifest.xml b/packages/stream_chat_persistence/example/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index 3841432394..0000000000 --- a/packages/stream_chat_persistence/example/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/packages/stream_chat_persistence/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/packages/stream_chat_persistence/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt deleted file mode 100644 index e793a000d6..0000000000 --- a/packages/stream_chat_persistence/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.example.example - -import io.flutter.embedding.android.FlutterActivity - -class MainActivity: FlutterActivity() { -} diff --git a/packages/stream_chat_persistence/example/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/stream_chat_persistence/example/android/app/src/main/res/drawable-v21/launch_background.xml deleted file mode 100644 index f74085f3f6..0000000000 --- a/packages/stream_chat_persistence/example/android/app/src/main/res/drawable-v21/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/packages/stream_chat_persistence/example/android/app/src/main/res/drawable/launch_background.xml b/packages/stream_chat_persistence/example/android/app/src/main/res/drawable/launch_background.xml deleted file mode 100644 index 304732f884..0000000000 --- a/packages/stream_chat_persistence/example/android/app/src/main/res/drawable/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/packages/stream_chat_persistence/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/stream_chat_persistence/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index db77bb4b7b..0000000000 Binary files a/packages/stream_chat_persistence/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/stream_chat_persistence/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 17987b79bb..0000000000 Binary files a/packages/stream_chat_persistence/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/stream_chat_persistence/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 09d4391482..0000000000 Binary files a/packages/stream_chat_persistence/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/stream_chat_persistence/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index d5f1c8d34e..0000000000 Binary files a/packages/stream_chat_persistence/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/stream_chat_persistence/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 4d6372eebd..0000000000 Binary files a/packages/stream_chat_persistence/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/android/app/src/main/res/values-night/styles.xml b/packages/stream_chat_persistence/example/android/app/src/main/res/values-night/styles.xml deleted file mode 100644 index 3db14bb539..0000000000 --- a/packages/stream_chat_persistence/example/android/app/src/main/res/values-night/styles.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/packages/stream_chat_persistence/example/android/app/src/main/res/values/styles.xml b/packages/stream_chat_persistence/example/android/app/src/main/res/values/styles.xml deleted file mode 100644 index d460d1e921..0000000000 --- a/packages/stream_chat_persistence/example/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/packages/stream_chat_persistence/example/android/app/src/profile/AndroidManifest.xml b/packages/stream_chat_persistence/example/android/app/src/profile/AndroidManifest.xml deleted file mode 100644 index c208884f30..0000000000 --- a/packages/stream_chat_persistence/example/android/app/src/profile/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/packages/stream_chat_persistence/example/android/build.gradle b/packages/stream_chat_persistence/example/android/build.gradle deleted file mode 100644 index 6571f4e39c..0000000000 --- a/packages/stream_chat_persistence/example/android/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -buildscript { - ext.kotlin_version = '1.7.21' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.3.1' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -tasks.register("clean", Delete) { - delete rootProject.buildDir -} diff --git a/packages/stream_chat_persistence/example/android/gradle.properties b/packages/stream_chat_persistence/example/android/gradle.properties deleted file mode 100644 index 94adc3a3f9..0000000000 --- a/packages/stream_chat_persistence/example/android/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M -android.useAndroidX=true -android.enableJetifier=true diff --git a/packages/stream_chat_persistence/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/stream_chat_persistence/example/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 02e5f58171..0000000000 --- a/packages/stream_chat_persistence/example/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Fri Jun 23 08:50:38 CEST 2017 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip diff --git a/packages/stream_chat_persistence/example/android/settings.gradle b/packages/stream_chat_persistence/example/android/settings.gradle deleted file mode 100644 index 44e62bcf06..0000000000 --- a/packages/stream_chat_persistence/example/android/settings.gradle +++ /dev/null @@ -1,11 +0,0 @@ -include ':app' - -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() - -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } - -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/packages/stream_chat_persistence/example/ios/.gitignore b/packages/stream_chat_persistence/example/ios/.gitignore deleted file mode 100644 index e96ef602b8..0000000000 --- a/packages/stream_chat_persistence/example/ios/.gitignore +++ /dev/null @@ -1,32 +0,0 @@ -*.mode1v3 -*.mode2v3 -*.moved-aside -*.pbxuser -*.perspectivev3 -**/*sync/ -.sconsign.dblite -.tags* -**/.vagrant/ -**/DerivedData/ -Icon? -**/Pods/ -**/.symlinks/ -profile -xcuserdata -**/.generated/ -Flutter/App.framework -Flutter/Flutter.framework -Flutter/Flutter.podspec -Flutter/Generated.xcconfig -Flutter/app.flx -Flutter/app.zip -Flutter/flutter_assets/ -Flutter/flutter_export_environment.sh -ServiceDefinitions.json -Runner/GeneratedPluginRegistrant.* - -# Exceptions to above rules. -!default.mode1v3 -!default.mode2v3 -!default.pbxuser -!default.perspectivev3 diff --git a/packages/stream_chat_persistence/example/ios/Flutter/AppFrameworkInfo.plist b/packages/stream_chat_persistence/example/ios/Flutter/AppFrameworkInfo.plist deleted file mode 100644 index 4f8d4d2456..0000000000 --- a/packages/stream_chat_persistence/example/ios/Flutter/AppFrameworkInfo.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - MinimumOSVersion - 11.0 - - diff --git a/packages/stream_chat_persistence/example/ios/Flutter/Debug.xcconfig b/packages/stream_chat_persistence/example/ios/Flutter/Debug.xcconfig deleted file mode 100644 index e8efba1146..0000000000 --- a/packages/stream_chat_persistence/example/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "Generated.xcconfig" diff --git a/packages/stream_chat_persistence/example/ios/Flutter/Release.xcconfig b/packages/stream_chat_persistence/example/ios/Flutter/Release.xcconfig deleted file mode 100644 index 399e9340e6..0000000000 --- a/packages/stream_chat_persistence/example/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "Generated.xcconfig" diff --git a/packages/stream_chat_persistence/example/ios/Podfile b/packages/stream_chat_persistence/example/ios/Podfile deleted file mode 100644 index d97f17e223..0000000000 --- a/packages/stream_chat_persistence/example/ios/Podfile +++ /dev/null @@ -1,44 +0,0 @@ -# Uncomment this line to define a global platform for your project -# platform :ios, '12.0' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_ios_podfile_setup - -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) - target 'RunnerTests' do - inherit! :search_paths - end -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_ios_build_settings(target) - end -end diff --git a/packages/stream_chat_persistence/example/ios/Runner.xcodeproj/project.pbxproj b/packages/stream_chat_persistence/example/ios/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index 6a1bdfa487..0000000000 --- a/packages/stream_chat_persistence/example/ios/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,566 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXBuildFile section */ - 0E9B23A7BA08E142FA5000CF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D76F8024ABE1070895D659BA /* Pods_Runner.framework */; }; - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 3F6A054EEDAF06BCF649C130 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 6FE1ECA061EBB001F6BCE8B7 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D76F8024ABE1070895D659BA /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - EC2A45E9198C1011BED23834 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 97C146EB1CF9000F007C117D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 0E9B23A7BA08E142FA5000CF /* Pods_Runner.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 04AAB960E493BD92262BBF82 /* Frameworks */ = { - isa = PBXGroup; - children = ( - D76F8024ABE1070895D659BA /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - 8559384DCD98ED6067CEF8CB /* Pods */ = { - isa = PBXGroup; - children = ( - 3F6A054EEDAF06BCF649C130 /* Pods-Runner.debug.xcconfig */, - EC2A45E9198C1011BED23834 /* Pods-Runner.release.xcconfig */, - 6FE1ECA061EBB001F6BCE8B7 /* Pods-Runner.profile.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; - 9740EEB11CF90186004384FC /* Flutter */ = { - isa = PBXGroup; - children = ( - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 9740EEB31CF90195004384FC /* Generated.xcconfig */, - ); - name = Flutter; - sourceTree = ""; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - 9740EEB11CF90186004384FC /* Flutter */, - 97C146F01CF9000F007C117D /* Runner */, - 97C146EF1CF9000F007C117D /* Products */, - 8559384DCD98ED6067CEF8CB /* Pods */, - 04AAB960E493BD92262BBF82 /* Frameworks */, - ); - sourceTree = ""; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - ); - name = Products; - sourceTree = ""; - }; - 97C146F01CF9000F007C117D /* Runner */ = { - isa = PBXGroup; - children = ( - 97C146FA1CF9000F007C117D /* Main.storyboard */, - 97C146FD1CF9000F007C117D /* Assets.xcassets */, - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 97C147021CF9000F007C117D /* Info.plist */, - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, - ); - path = Runner; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 97C146ED1CF9000F007C117D /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - C1EE41B94EADE099F7AF3A1C /* [CP] Check Pods Manifest.lock */, - 9740EEB61CF901F6004384FC /* Run Script */, - 97C146EA1CF9000F007C117D /* Sources */, - 97C146EB1CF9000F007C117D /* Frameworks */, - 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - BD38DACC9A0AD429D9ED9939 /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Runner; - productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* Runner.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 97C146E61CF9000F007C117D /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1430; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 97C146ED1CF9000F007C117D = { - CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 1100; - }; - }; - }; - buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 97C146E51CF9000F007C117D; - productRefGroup = 97C146EF1CF9000F007C117D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 97C146ED1CF9000F007C117D /* Runner */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 97C146EC1CF9000F007C117D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; - BD38DACC9A0AD429D9ED9939 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - C1EE41B94EADE099F7AF3A1C /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 97C146EA1CF9000F007C117D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 97C146FA1CF9000F007C117D /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C146FB1CF9000F007C117D /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 249021D3217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Profile; - }; - 249021D4217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.example; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Profile; - }; - 97C147031CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 97C147041CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 97C147061CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.example; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - 97C147071CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.example; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147031CF9000F007C117D /* Debug */, - 97C147041CF9000F007C117D /* Release */, - 249021D3217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147061CF9000F007C117D /* Debug */, - 97C147071CF9000F007C117D /* Release */, - 249021D4217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 97C146E61CF9000F007C117D /* Project object */; -} diff --git a/packages/stream_chat_persistence/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/stream_chat_persistence/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a625..0000000000 --- a/packages/stream_chat_persistence/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/packages/stream_chat_persistence/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/stream_chat_persistence/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/packages/stream_chat_persistence/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/packages/stream_chat_persistence/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/stream_chat_persistence/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5ea..0000000000 --- a/packages/stream_chat_persistence/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/packages/stream_chat_persistence/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/stream_chat_persistence/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index b52b2e698b..0000000000 --- a/packages/stream_chat_persistence/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/stream_chat_persistence/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/stream_chat_persistence/example/ios/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 21a3cc14c7..0000000000 --- a/packages/stream_chat_persistence/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/packages/stream_chat_persistence/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/stream_chat_persistence/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/packages/stream_chat_persistence/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/packages/stream_chat_persistence/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/stream_chat_persistence/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5ea..0000000000 --- a/packages/stream_chat_persistence/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/packages/stream_chat_persistence/example/ios/Runner/AppDelegate.swift b/packages/stream_chat_persistence/example/ios/Runner/AppDelegate.swift deleted file mode 100644 index 70693e4a8c..0000000000 --- a/packages/stream_chat_persistence/example/ios/Runner/AppDelegate.swift +++ /dev/null @@ -1,13 +0,0 @@ -import UIKit -import Flutter - -@UIApplicationMain -@objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } -} diff --git a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d36b1fab2d..0000000000 --- a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - }, - { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "Icon-App-1024x1024@1x.png", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png deleted file mode 100644 index dc9ada4725..0000000000 Binary files a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png deleted file mode 100644 index 28c6bf0301..0000000000 Binary files a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png deleted file mode 100644 index 2ccbfd967d..0000000000 Binary files a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index f091b6b0bc..0000000000 Binary files a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png deleted file mode 100644 index 4cde12118d..0000000000 Binary files a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index d0ef06e7ed..0000000000 Binary files a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png deleted file mode 100644 index dcdc2306c2..0000000000 Binary files a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png deleted file mode 100644 index 2ccbfd967d..0000000000 Binary files a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index c8f9ed8f5c..0000000000 Binary files a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png deleted file mode 100644 index a6d6b8609d..0000000000 Binary files a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index a6d6b8609d..0000000000 Binary files a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index 75b2d164a5..0000000000 Binary files a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png deleted file mode 100644 index c4df70d39d..0000000000 Binary files a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png deleted file mode 100644 index 6a84f41e14..0000000000 Binary files a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png deleted file mode 100644 index d0e1f58536..0000000000 Binary files a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json deleted file mode 100644 index 0bedcf2fd4..0000000000 --- a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "LaunchImage.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png deleted file mode 100644 index 9da19eacad..0000000000 Binary files a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png deleted file mode 100644 index 9da19eacad..0000000000 Binary files a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png deleted file mode 100644 index 9da19eacad..0000000000 Binary files a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md deleted file mode 100644 index 89c2725b70..0000000000 --- a/packages/stream_chat_persistence/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Launch Screen Assets - -You can customize the launch screen with your own desired assets by replacing the image files in this directory. - -You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/packages/stream_chat_persistence/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/stream_chat_persistence/example/ios/Runner/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index f2e259c7c9..0000000000 --- a/packages/stream_chat_persistence/example/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/stream_chat_persistence/example/ios/Runner/Base.lproj/Main.storyboard b/packages/stream_chat_persistence/example/ios/Runner/Base.lproj/Main.storyboard deleted file mode 100644 index f3c28516fb..0000000000 --- a/packages/stream_chat_persistence/example/ios/Runner/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/stream_chat_persistence/example/ios/Runner/Info.plist b/packages/stream_chat_persistence/example/ios/Runner/Info.plist deleted file mode 100644 index 4f68a2cee1..0000000000 --- a/packages/stream_chat_persistence/example/ios/Runner/Info.plist +++ /dev/null @@ -1,49 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - example - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - CADisableMinimumFrameDurationOnPhone - - UIApplicationSupportsIndirectInputEvents - - - diff --git a/packages/stream_chat_persistence/example/ios/Runner/Runner-Bridging-Header.h b/packages/stream_chat_persistence/example/ios/Runner/Runner-Bridging-Header.h deleted file mode 100644 index 308a2a560b..0000000000 --- a/packages/stream_chat_persistence/example/ios/Runner/Runner-Bridging-Header.h +++ /dev/null @@ -1 +0,0 @@ -#import "GeneratedPluginRegistrant.h" diff --git a/packages/stream_chat_persistence/example/lib/main.dart b/packages/stream_chat_persistence/example/lib/main.dart deleted file mode 100644 index 6e71a555da..0000000000 --- a/packages/stream_chat_persistence/example/lib/main.dart +++ /dev/null @@ -1,262 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/stream_chat_persistence.dart'; - -Future main() async { - /// Create a new instance of [StreamChatClient] passing the apikey obtained - /// from your project dashboard. - final client = StreamChatClient('b67pax5b2wdq'); - - WidgetsFlutterBinding.ensureInitialized(); - - /// Set the chatPersistenceClient for offline support - client.chatPersistenceClient = StreamChatPersistenceClient( - logLevel: Level.INFO, - connectionMode: ConnectionMode.background, - ); - - /// Set the current user. In a production scenario, this should be done using - /// a backend to generate a user token using our server SDK. - /// Please see the following for more information: - /// https://getstream.io/chat/docs/ios_user_setup_and_tokens/ - await client.connectUser( - User( - id: 'cool-shadow-7', - image: - 'https://getstream.io/random_png/?id=cool-shadow-7&name=Cool+shadow', - ), - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1zaGFkb3ctNyJ9.' - 'gkOlCRb1qgy4joHPaxFwPOdXcGvSPvp6QY0S4mpRkVo', - ); - - /// Creates a channel using the type `messaging` and `godevs`. - /// Channels are containers for holding messages between different members. To - /// learn more about channels and some of our predefined types, checkout our - /// our channel docs: https://getstream.io/chat/docs/initialize_channel/?language=dart - final channel = client.channel('messaging', id: 'godevs'); - - /// `.watch()` is used to create and listen to the channel for updates. If the - /// channel already exists, it will simply listen for new events. - await channel.watch(); - - runApp( - StreamExample( - client: client, - channel: channel, - ), - ); -} - -/// Example using Stream's Low Level Dart client. -class StreamExample extends StatelessWidget { - /// To initialize this example, an instance of - /// [client] and [channel] is required. - const StreamExample({ - super.key, - required this.client, - required this.channel, - }); - - /// Instance of [StreamChatClient] we created earlier. - /// This contains information about our application and connection state. - final StreamChatClient client; - - /// The channel we'd like to observe and participate. - final Channel channel; - - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Stream Chat Dart Example', - home: HomeScreen(channel: channel), - ); - } -} - -/// Main screen of our application. The layout is comprised of an [AppBar] -/// containing the channel name and a [MessageView] displaying recent messages. -class HomeScreen extends StatelessWidget { - /// [HomeScreen] is constructed using the [Channel] we defined earlier. - const HomeScreen({ - super.key, - required this.channel, - }); - - /// Channel object containing the [Channel.id] we'd like to observe. - final Channel channel; - - @override - Widget build(BuildContext context) { - final messages = channel.state!.channelStateStream; - return Scaffold( - appBar: AppBar( - title: Text('Channel: ${channel.id}'), - ), - body: SafeArea( - child: StreamBuilder( - stream: messages, - builder: ( - BuildContext context, - AsyncSnapshot snapshot, - ) { - if (snapshot.hasData && snapshot.data != null) { - final _messages = snapshot.data!.messages ?? []; - return MessageView( - messages: _messages.reversed.toList(), - channel: channel, - ); - } else if (snapshot.hasError) { - return const Center( - child: Text( - 'There was an error loading messages. Please see logs.', - ), - ); - } - return const Center( - child: SizedBox( - width: 100, - height: 100, - child: CircularProgressIndicator(), - ), - ); - }, - ), - ), - ); - } -} - -/// UI used to display a list of recent messages and a [TextField] for sending -/// new messages. -class MessageView extends StatefulWidget { - /// Message takes the latest list of messages and the current channel. - const MessageView({ - super.key, - required this.messages, - required this.channel, - }); - - /// List of messages sent in the given channel. - final List messages; - - /// Current channel being observed. - final Channel channel; - - @override - _MessageViewState createState() => _MessageViewState(); -} - -class _MessageViewState extends State { - late final TextEditingController _controller; - late final ScrollController _scrollController; - - List get _messages => widget.messages; - - @override - void initState() { - super.initState(); - _controller = TextEditingController(); - _scrollController = ScrollController(); - } - - @override - void dispose() { - _controller.dispose(); - _scrollController.dispose(); - super.dispose(); - } - - /// Convenience method for scrolling the list view when a new message is sent. - void _updateList() { - _scrollController.animateTo( - 0, - duration: const Duration(milliseconds: 200), - curve: Curves.easeOut, - ); - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Expanded( - child: ListView.builder( - controller: _scrollController, - itemCount: _messages.length, - reverse: true, - itemBuilder: (BuildContext context, int index) { - final item = _messages[index]; - if (item.user?.id == widget.channel.client.uid) { - return Align( - alignment: Alignment.centerRight, - child: Padding( - padding: const EdgeInsets.all(8), - child: Text(item.text ?? ''), - ), - ); - } else { - return Align( - alignment: Alignment.centerLeft, - child: Padding( - padding: const EdgeInsets.all(8), - child: Text(item.text ?? ''), - ), - ); - } - }, - ), - ), - Padding( - padding: const EdgeInsets.all(8), - child: Row( - children: [ - Expanded( - child: TextField( - controller: _controller, - decoration: const InputDecoration( - hintText: 'Enter your message', - ), - ), - ), - Material( - type: MaterialType.circle, - color: Colors.blue, - clipBehavior: Clip.hardEdge, - child: InkWell( - onTap: () async { - // We can send a new message by calling `sendMessage` on - // the current channel. After sending a message, the - // TextField is cleared and the list view is scrolled - // to show the new item. - if (_controller.value.text.isNotEmpty) { - await widget.channel.sendMessage( - Message(text: _controller.value.text), - ); - _controller.clear(); - _updateList(); - } - }, - child: const Padding( - padding: EdgeInsets.all(8), - child: Center( - child: Icon( - Icons.send, - color: Colors.white, - ), - ), - ), - ), - ), - ], - ), - ), - ], - ); - } -} - -/// Helper extension for quickly retrieving -/// the current user id from a [StreamChatClient]. -extension on StreamChatClient { - String get uid => state.currentUser!.id; -} diff --git a/packages/stream_chat_persistence/example/linux/.gitignore b/packages/stream_chat_persistence/example/linux/.gitignore deleted file mode 100644 index d3896c9844..0000000000 --- a/packages/stream_chat_persistence/example/linux/.gitignore +++ /dev/null @@ -1 +0,0 @@ -flutter/ephemeral diff --git a/packages/stream_chat_persistence/example/linux/CMakeLists.txt b/packages/stream_chat_persistence/example/linux/CMakeLists.txt deleted file mode 100644 index a558bc458a..0000000000 --- a/packages/stream_chat_persistence/example/linux/CMakeLists.txt +++ /dev/null @@ -1,116 +0,0 @@ -cmake_minimum_required(VERSION 3.10) -project(runner LANGUAGES CXX) - -set(BINARY_NAME "example") -set(APPLICATION_ID "com.example.example") - -cmake_policy(SET CMP0063 NEW) - -set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") - -# Root filesystem for cross-building. -if(FLUTTER_TARGET_PLATFORM_SYSROOT) - set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) - set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) - set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) - set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) - set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) - set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -endif() - -# Configure build options. -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE "Debug" CACHE - STRING "Flutter build mode" FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Profile" "Release") -endif() - -# Compilation settings that should be applied to most targets. -function(APPLY_STANDARD_SETTINGS TARGET) - target_compile_features(${TARGET} PUBLIC cxx_std_14) - target_compile_options(${TARGET} PRIVATE -Wall -Werror) - target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") - target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") -endfunction() - -set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") - -# Flutter library and tool build rules. -add_subdirectory(${FLUTTER_MANAGED_DIR}) - -# System-level dependencies. -find_package(PkgConfig REQUIRED) -pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) - -add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") - -# Application build -add_executable(${BINARY_NAME} - "main.cc" - "my_application.cc" - "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" -) -apply_standard_settings(${BINARY_NAME}) -target_link_libraries(${BINARY_NAME} PRIVATE flutter) -target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) -add_dependencies(${BINARY_NAME} flutter_assemble) -# Only the install-generated bundle's copy of the executable will launch -# correctly, since the resources must in the right relative locations. To avoid -# people trying to run the unbundled copy, put it in a subdirectory instead of -# the default top-level location. -set_target_properties(${BINARY_NAME} - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" -) - -# Generated plugin build rules, which manage building the plugins and adding -# them to the application. -include(flutter/generated_plugins.cmake) - - -# === Installation === -# By default, "installing" just makes a relocatable bundle in the build -# directory. -set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) -endif() - -# Start with a clean build bundle directory every time. -install(CODE " - file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") - " COMPONENT Runtime) - -set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") -set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") - -install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) - -if(PLUGIN_BUNDLED_LIBRARIES) - install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" - DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endif() - -# Fully re-copy the assets directory on each build to avoid having stale files -# from a previous install. -set(FLUTTER_ASSET_DIR_NAME "flutter_assets") -install(CODE " - file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") - " COMPONENT Runtime) -install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" - DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) - -# Install the AOT library on non-Debug builds only. -if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") - install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endif() diff --git a/packages/stream_chat_persistence/example/linux/flutter/CMakeLists.txt b/packages/stream_chat_persistence/example/linux/flutter/CMakeLists.txt deleted file mode 100644 index 33fd5801e7..0000000000 --- a/packages/stream_chat_persistence/example/linux/flutter/CMakeLists.txt +++ /dev/null @@ -1,87 +0,0 @@ -cmake_minimum_required(VERSION 3.10) - -set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") - -# Configuration provided via flutter tool. -include(${EPHEMERAL_DIR}/generated_config.cmake) - -# TODO: Move the rest of this into files in ephemeral. See -# https://github.com/flutter/flutter/issues/57146. - -# Serves the same purpose as list(TRANSFORM ... PREPEND ...), -# which isn't available in 3.10. -function(list_prepend LIST_NAME PREFIX) - set(NEW_LIST "") - foreach(element ${${LIST_NAME}}) - list(APPEND NEW_LIST "${PREFIX}${element}") - endforeach(element) - set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) -endfunction() - -# === Flutter Library === -# System-level dependencies. -find_package(PkgConfig REQUIRED) -pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) -pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) -pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) - -set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") - -# Published to parent scope for install step. -set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) -set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) -set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) -set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) - -list(APPEND FLUTTER_LIBRARY_HEADERS - "fl_basic_message_channel.h" - "fl_binary_codec.h" - "fl_binary_messenger.h" - "fl_dart_project.h" - "fl_engine.h" - "fl_json_message_codec.h" - "fl_json_method_codec.h" - "fl_message_codec.h" - "fl_method_call.h" - "fl_method_channel.h" - "fl_method_codec.h" - "fl_method_response.h" - "fl_plugin_registrar.h" - "fl_plugin_registry.h" - "fl_standard_message_codec.h" - "fl_standard_method_codec.h" - "fl_string_codec.h" - "fl_value.h" - "fl_view.h" - "flutter_linux.h" -) -list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") -add_library(flutter INTERFACE) -target_include_directories(flutter INTERFACE - "${EPHEMERAL_DIR}" -) -target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") -target_link_libraries(flutter INTERFACE - PkgConfig::GTK - PkgConfig::GLIB - PkgConfig::GIO -) -add_dependencies(flutter flutter_assemble) - -# === Flutter tool backend === -# _phony_ is a non-existent file to force this command to run every time, -# since currently there's no way to get a full input/output list from the -# flutter tool. -add_custom_command( - OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} - ${CMAKE_CURRENT_BINARY_DIR}/_phony_ - COMMAND ${CMAKE_COMMAND} -E env - ${FLUTTER_TOOL_ENVIRONMENT} - "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" - ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} - VERBATIM -) -add_custom_target(flutter_assemble DEPENDS - "${FLUTTER_LIBRARY}" - ${FLUTTER_LIBRARY_HEADERS} -) diff --git a/packages/stream_chat_persistence/example/linux/flutter/generated_plugins.cmake b/packages/stream_chat_persistence/example/linux/flutter/generated_plugins.cmake deleted file mode 100644 index 7ea2a80150..0000000000 --- a/packages/stream_chat_persistence/example/linux/flutter/generated_plugins.cmake +++ /dev/null @@ -1,24 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST - sqlite3_flutter_libs -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/packages/stream_chat_persistence/example/linux/main.cc b/packages/stream_chat_persistence/example/linux/main.cc deleted file mode 100644 index e7c5c54370..0000000000 --- a/packages/stream_chat_persistence/example/linux/main.cc +++ /dev/null @@ -1,6 +0,0 @@ -#include "my_application.h" - -int main(int argc, char** argv) { - g_autoptr(MyApplication) app = my_application_new(); - return g_application_run(G_APPLICATION(app), argc, argv); -} diff --git a/packages/stream_chat_persistence/example/linux/my_application.cc b/packages/stream_chat_persistence/example/linux/my_application.cc deleted file mode 100644 index 0ba8f43096..0000000000 --- a/packages/stream_chat_persistence/example/linux/my_application.cc +++ /dev/null @@ -1,104 +0,0 @@ -#include "my_application.h" - -#include -#ifdef GDK_WINDOWING_X11 -#include -#endif - -#include "flutter/generated_plugin_registrant.h" - -struct _MyApplication { - GtkApplication parent_instance; - char** dart_entrypoint_arguments; -}; - -G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) - -// Implements GApplication::activate. -static void my_application_activate(GApplication* application) { - MyApplication* self = MY_APPLICATION(application); - GtkWindow* window = - GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); - - // Use a header bar when running in GNOME as this is the common style used - // by applications and is the setup most users will be using (e.g. Ubuntu - // desktop). - // If running on X and not using GNOME then just use a traditional title bar - // in case the window manager does more exotic layout, e.g. tiling. - // If running on Wayland assume the header bar will work (may need changing - // if future cases occur). - gboolean use_header_bar = TRUE; -#ifdef GDK_WINDOWING_X11 - GdkScreen* screen = gtk_window_get_screen(window); - if (GDK_IS_X11_SCREEN(screen)) { - const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); - if (g_strcmp0(wm_name, "GNOME Shell") != 0) { - use_header_bar = FALSE; - } - } -#endif - if (use_header_bar) { - GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); - gtk_widget_show(GTK_WIDGET(header_bar)); - gtk_header_bar_set_title(header_bar, "example"); - gtk_header_bar_set_show_close_button(header_bar, TRUE); - gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); - } else { - gtk_window_set_title(window, "example"); - } - - gtk_window_set_default_size(window, 1280, 720); - gtk_widget_show(GTK_WIDGET(window)); - - g_autoptr(FlDartProject) project = fl_dart_project_new(); - fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); - - FlView* view = fl_view_new(project); - gtk_widget_show(GTK_WIDGET(view)); - gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); - - fl_register_plugins(FL_PLUGIN_REGISTRY(view)); - - gtk_widget_grab_focus(GTK_WIDGET(view)); -} - -// Implements GApplication::local_command_line. -static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { - MyApplication* self = MY_APPLICATION(application); - // Strip out the first argument as it is the binary name. - self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); - - g_autoptr(GError) error = nullptr; - if (!g_application_register(application, nullptr, &error)) { - g_warning("Failed to register: %s", error->message); - *exit_status = 1; - return TRUE; - } - - g_application_activate(application); - *exit_status = 0; - - return TRUE; -} - -// Implements GObject::dispose. -static void my_application_dispose(GObject* object) { - MyApplication* self = MY_APPLICATION(object); - g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); - G_OBJECT_CLASS(my_application_parent_class)->dispose(object); -} - -static void my_application_class_init(MyApplicationClass* klass) { - G_APPLICATION_CLASS(klass)->activate = my_application_activate; - G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; - G_OBJECT_CLASS(klass)->dispose = my_application_dispose; -} - -static void my_application_init(MyApplication* self) {} - -MyApplication* my_application_new() { - return MY_APPLICATION(g_object_new(my_application_get_type(), - "application-id", APPLICATION_ID, - "flags", G_APPLICATION_NON_UNIQUE, - nullptr)); -} diff --git a/packages/stream_chat_persistence/example/linux/my_application.h b/packages/stream_chat_persistence/example/linux/my_application.h deleted file mode 100644 index 72271d5e41..0000000000 --- a/packages/stream_chat_persistence/example/linux/my_application.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef FLUTTER_MY_APPLICATION_H_ -#define FLUTTER_MY_APPLICATION_H_ - -#include - -G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, - GtkApplication) - -/** - * my_application_new: - * - * Creates a new Flutter-based application. - * - * Returns: a new #MyApplication. - */ -MyApplication* my_application_new(); - -#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/packages/stream_chat_persistence/example/macos/.gitignore b/packages/stream_chat_persistence/example/macos/.gitignore deleted file mode 100644 index 746adbb6b9..0000000000 --- a/packages/stream_chat_persistence/example/macos/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Flutter-related -**/Flutter/ephemeral/ -**/Pods/ - -# Xcode-related -**/dgph -**/xcuserdata/ diff --git a/packages/stream_chat_persistence/example/macos/Flutter/Flutter-Debug.xcconfig b/packages/stream_chat_persistence/example/macos/Flutter/Flutter-Debug.xcconfig deleted file mode 100644 index 4b81f9b2d2..0000000000 --- a/packages/stream_chat_persistence/example/macos/Flutter/Flutter-Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/stream_chat_persistence/example/macos/Flutter/Flutter-Release.xcconfig b/packages/stream_chat_persistence/example/macos/Flutter/Flutter-Release.xcconfig deleted file mode 100644 index 5caa9d1579..0000000000 --- a/packages/stream_chat_persistence/example/macos/Flutter/Flutter-Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/stream_chat_persistence/example/macos/Podfile b/packages/stream_chat_persistence/example/macos/Podfile deleted file mode 100644 index c795730db8..0000000000 --- a/packages/stream_chat_persistence/example/macos/Podfile +++ /dev/null @@ -1,43 +0,0 @@ -platform :osx, '10.14' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_macos_podfile_setup - -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) - target 'RunnerTests' do - inherit! :search_paths - end -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_macos_build_settings(target) - end -end diff --git a/packages/stream_chat_persistence/example/macos/Runner.xcodeproj/project.pbxproj b/packages/stream_chat_persistence/example/macos/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index 4c2fe3c306..0000000000 --- a/packages/stream_chat_persistence/example/macos/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,632 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 51; - objects = { - -/* Begin PBXAggregateTarget section */ - 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { - isa = PBXAggregateTarget; - buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; - buildPhases = ( - 33CC111E2044C6BF0003C045 /* ShellScript */, - ); - dependencies = ( - ); - name = "Flutter Assemble"; - productName = FLX; - }; -/* End PBXAggregateTarget section */ - -/* Begin PBXBuildFile section */ - 2B7C563A37741A3C9EF92B31 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B1A5184EBFC1CF12D8F0FED /* Pods_Runner.framework */; }; - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 33CC10E52044A3C60003C045 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 33CC111A2044C6BA0003C045; - remoteInfo = FLX; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 33CC110E2044A8840003C045 /* Bundle Framework */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Bundle Framework"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 0FF0B7F379F1614C9729FAA8 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; - 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; - 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; - 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; - 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; - 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 4A1304048C006F772B1A0FD8 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - 67593663D5F7E4E722209E5F /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 7B1A5184EBFC1CF12D8F0FED /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 33CC10EA2044A3C60003C045 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 2B7C563A37741A3C9EF92B31 /* Pods_Runner.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 33BA886A226E78AF003329D5 /* Configs */ = { - isa = PBXGroup; - children = ( - 33E5194F232828860026EE4D /* AppInfo.xcconfig */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, - ); - path = Configs; - sourceTree = ""; - }; - 33CC10E42044A3C60003C045 = { - isa = PBXGroup; - children = ( - 33FAB671232836740065AC1E /* Runner */, - 33CEB47122A05771004F2AC0 /* Flutter */, - 33CC10EE2044A3C60003C045 /* Products */, - D73912EC22F37F3D000D13A0 /* Frameworks */, - 920F14AC24D3A10BED75FEA8 /* Pods */, - ); - sourceTree = ""; - }; - 33CC10EE2044A3C60003C045 /* Products */ = { - isa = PBXGroup; - children = ( - 33CC10ED2044A3C60003C045 /* example.app */, - ); - name = Products; - sourceTree = ""; - }; - 33CC11242044D66E0003C045 /* Resources */ = { - isa = PBXGroup; - children = ( - 33CC10F22044A3C60003C045 /* Assets.xcassets */, - 33CC10F42044A3C60003C045 /* MainMenu.xib */, - 33CC10F72044A3C60003C045 /* Info.plist */, - ); - name = Resources; - path = ..; - sourceTree = ""; - }; - 33CEB47122A05771004F2AC0 /* Flutter */ = { - isa = PBXGroup; - children = ( - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, - ); - path = Flutter; - sourceTree = ""; - }; - 33FAB671232836740065AC1E /* Runner */ = { - isa = PBXGroup; - children = ( - 33CC10F02044A3C60003C045 /* AppDelegate.swift */, - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, - 33E51913231747F40026EE4D /* DebugProfile.entitlements */, - 33E51914231749380026EE4D /* Release.entitlements */, - 33CC11242044D66E0003C045 /* Resources */, - 33BA886A226E78AF003329D5 /* Configs */, - ); - path = Runner; - sourceTree = ""; - }; - 920F14AC24D3A10BED75FEA8 /* Pods */ = { - isa = PBXGroup; - children = ( - 0FF0B7F379F1614C9729FAA8 /* Pods-Runner.debug.xcconfig */, - 4A1304048C006F772B1A0FD8 /* Pods-Runner.release.xcconfig */, - 67593663D5F7E4E722209E5F /* Pods-Runner.profile.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; - D73912EC22F37F3D000D13A0 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 7B1A5184EBFC1CF12D8F0FED /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 33CC10EC2044A3C60003C045 /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - BEC283DD873799EEEA283BD1 /* [CP] Check Pods Manifest.lock */, - 33CC10E92044A3C60003C045 /* Sources */, - 33CC10EA2044A3C60003C045 /* Frameworks */, - 33CC10EB2044A3C60003C045 /* Resources */, - 33CC110E2044A8840003C045 /* Bundle Framework */, - 3399D490228B24CF009A79C7 /* ShellScript */, - B9255738A59BE48A11F25A1C /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 33CC11202044C79F0003C045 /* PBXTargetDependency */, - ); - name = Runner; - productName = Runner; - productReference = 33CC10ED2044A3C60003C045 /* example.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 33CC10E52044A3C60003C045 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 33CC10EC2044A3C60003C045 = { - CreatedOnToolsVersion = 9.2; - LastSwiftMigration = 1100; - ProvisioningStyle = Automatic; - SystemCapabilities = { - com.apple.Sandbox = { - enabled = 1; - }; - }; - }; - 33CC111A2044C6BA0003C045 = { - CreatedOnToolsVersion = 9.2; - ProvisioningStyle = Manual; - }; - }; - }; - buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 33CC10E42044A3C60003C045; - productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 33CC10EC2044A3C60003C045 /* Runner */, - 33CC111A2044C6BA0003C045 /* Flutter Assemble */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 33CC10EB2044A3C60003C045 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3399D490228B24CF009A79C7 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; - }; - 33CC111E2044C6BF0003C045 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - Flutter/ephemeral/FlutterInputs.xcfilelist, - ); - inputPaths = ( - Flutter/ephemeral/tripwire, - ); - outputFileListPaths = ( - Flutter/ephemeral/FlutterOutputs.xcfilelist, - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; - }; - B9255738A59BE48A11F25A1C /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - BEC283DD873799EEEA283BD1 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 33CC10E92044A3C60003C045 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; - targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { - isa = PBXVariantGroup; - children = ( - 33CC10F52044A3C60003C045 /* Base */, - ); - name = MainMenu.xib; - path = Runner; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 338D0CE9231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Profile; - }; - 338D0CEA231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Profile; - }; - 338D0CEB231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Profile; - }; - 33CC10F92044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 33CC10FA2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Release; - }; - 33CC10FC2044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 33CC10FD2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - 33CC111C2044C6BA0003C045 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 33CC111D2044C6BA0003C045 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10F92044A3C60003C045 /* Debug */, - 33CC10FA2044A3C60003C045 /* Release */, - 338D0CE9231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10FC2044A3C60003C045 /* Debug */, - 33CC10FD2044A3C60003C045 /* Release */, - 338D0CEA231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC111C2044C6BA0003C045 /* Debug */, - 33CC111D2044C6BA0003C045 /* Release */, - 338D0CEB231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 33CC10E52044A3C60003C045 /* Project object */; -} diff --git a/packages/stream_chat_persistence/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/stream_chat_persistence/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/packages/stream_chat_persistence/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/packages/stream_chat_persistence/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/stream_chat_persistence/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index fb7259e177..0000000000 --- a/packages/stream_chat_persistence/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/stream_chat_persistence/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/stream_chat_persistence/example/macos/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 21a3cc14c7..0000000000 --- a/packages/stream_chat_persistence/example/macos/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/packages/stream_chat_persistence/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/stream_chat_persistence/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/packages/stream_chat_persistence/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/packages/stream_chat_persistence/example/macos/Runner/AppDelegate.swift b/packages/stream_chat_persistence/example/macos/Runner/AppDelegate.swift deleted file mode 100644 index d53ef64377..0000000000 --- a/packages/stream_chat_persistence/example/macos/Runner/AppDelegate.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Cocoa -import FlutterMacOS - -@NSApplicationMain -class AppDelegate: FlutterAppDelegate { - override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { - return true - } -} diff --git a/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index a2ec33f19f..0000000000 --- a/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "images" : [ - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_16.png", - "scale" : "1x" - }, - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "2x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "1x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_64.png", - "scale" : "2x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_128.png", - "scale" : "1x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "2x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "1x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "2x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "1x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_1024.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png deleted file mode 100644 index 3c4935a7ca..0000000000 Binary files a/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png deleted file mode 100644 index ed4cc16421..0000000000 Binary files a/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png deleted file mode 100644 index 483be61389..0000000000 Binary files a/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png deleted file mode 100644 index bcbf36df2f..0000000000 Binary files a/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png deleted file mode 100644 index 9c0a652864..0000000000 Binary files a/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png deleted file mode 100644 index e71a726136..0000000000 Binary files a/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png deleted file mode 100644 index 8a31fe2dd3..0000000000 Binary files a/packages/stream_chat_persistence/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/macos/Runner/Base.lproj/MainMenu.xib b/packages/stream_chat_persistence/example/macos/Runner/Base.lproj/MainMenu.xib deleted file mode 100644 index 80e867a4e0..0000000000 --- a/packages/stream_chat_persistence/example/macos/Runner/Base.lproj/MainMenu.xib +++ /dev/null @@ -1,343 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/stream_chat_persistence/example/macos/Runner/Configs/AppInfo.xcconfig b/packages/stream_chat_persistence/example/macos/Runner/Configs/AppInfo.xcconfig deleted file mode 100644 index 8b42559e87..0000000000 --- a/packages/stream_chat_persistence/example/macos/Runner/Configs/AppInfo.xcconfig +++ /dev/null @@ -1,14 +0,0 @@ -// Application-level settings for the Runner target. -// -// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the -// future. If not, the values below would default to using the project name when this becomes a -// 'flutter create' template. - -// The application's name. By default this is also the title of the Flutter window. -PRODUCT_NAME = example - -// The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = com.example.example - -// The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright Ā© 2022 com.example. All rights reserved. diff --git a/packages/stream_chat_persistence/example/macos/Runner/Configs/Debug.xcconfig b/packages/stream_chat_persistence/example/macos/Runner/Configs/Debug.xcconfig deleted file mode 100644 index 36b0fd9464..0000000000 --- a/packages/stream_chat_persistence/example/macos/Runner/Configs/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Debug.xcconfig" -#include "Warnings.xcconfig" diff --git a/packages/stream_chat_persistence/example/macos/Runner/Configs/Release.xcconfig b/packages/stream_chat_persistence/example/macos/Runner/Configs/Release.xcconfig deleted file mode 100644 index dff4f49561..0000000000 --- a/packages/stream_chat_persistence/example/macos/Runner/Configs/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Release.xcconfig" -#include "Warnings.xcconfig" diff --git a/packages/stream_chat_persistence/example/macos/Runner/Configs/Warnings.xcconfig b/packages/stream_chat_persistence/example/macos/Runner/Configs/Warnings.xcconfig deleted file mode 100644 index 42bcbf4780..0000000000 --- a/packages/stream_chat_persistence/example/macos/Runner/Configs/Warnings.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings -GCC_WARN_UNDECLARED_SELECTOR = YES -CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES -CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE -CLANG_WARN__DUPLICATE_METHOD_MATCH = YES -CLANG_WARN_PRAGMA_PACK = YES -CLANG_WARN_STRICT_PROTOTYPES = YES -CLANG_WARN_COMMA = YES -GCC_WARN_STRICT_SELECTOR_MATCH = YES -CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES -CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES -GCC_WARN_SHADOW = YES -CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/packages/stream_chat_persistence/example/macos/Runner/DebugProfile.entitlements b/packages/stream_chat_persistence/example/macos/Runner/DebugProfile.entitlements deleted file mode 100644 index 08c3ab17cc..0000000000 --- a/packages/stream_chat_persistence/example/macos/Runner/DebugProfile.entitlements +++ /dev/null @@ -1,14 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.cs.allow-jit - - com.apple.security.network.server - - com.apple.security.network.client - - - diff --git a/packages/stream_chat_persistence/example/macos/Runner/Info.plist b/packages/stream_chat_persistence/example/macos/Runner/Info.plist deleted file mode 100644 index 4789daa6a4..0000000000 --- a/packages/stream_chat_persistence/example/macos/Runner/Info.plist +++ /dev/null @@ -1,32 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIconFile - - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSMinimumSystemVersion - $(MACOSX_DEPLOYMENT_TARGET) - NSHumanReadableCopyright - $(PRODUCT_COPYRIGHT) - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - - diff --git a/packages/stream_chat_persistence/example/macos/Runner/MainFlutterWindow.swift b/packages/stream_chat_persistence/example/macos/Runner/MainFlutterWindow.swift deleted file mode 100644 index 2722837ec9..0000000000 --- a/packages/stream_chat_persistence/example/macos/Runner/MainFlutterWindow.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Cocoa -import FlutterMacOS - -class MainFlutterWindow: NSWindow { - override func awakeFromNib() { - let flutterViewController = FlutterViewController.init() - let windowFrame = self.frame - self.contentViewController = flutterViewController - self.setFrame(windowFrame, display: true) - - RegisterGeneratedPlugins(registry: flutterViewController) - - super.awakeFromNib() - } -} diff --git a/packages/stream_chat_persistence/example/macos/Runner/Release.entitlements b/packages/stream_chat_persistence/example/macos/Runner/Release.entitlements deleted file mode 100644 index 852fa1a472..0000000000 --- a/packages/stream_chat_persistence/example/macos/Runner/Release.entitlements +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.security.app-sandbox - - - diff --git a/packages/stream_chat_persistence/example/pubspec.yaml b/packages/stream_chat_persistence/example/pubspec.yaml deleted file mode 100644 index 2bfc1afb30..0000000000 --- a/packages/stream_chat_persistence/example/pubspec.yaml +++ /dev/null @@ -1,30 +0,0 @@ -name: stream_chat_persistence_example -description: A new Flutter project. -publish_to: 'none' -version: 1.0.0+1 - -# Note: The environment configuration and dependency versions are managed by Melos. -# -# Do not edit them manually. -# -# Steps to update dependencies: -# 1. Modify the version in the melos.yaml file. -# 2. Run `melos bootstrap` to apply changes. -# -# Steps to add a new dependency: -# 1. Add the dependency to this list. -# 2. Add it to the melos.yaml file for future updates. - -environment: - sdk: ^3.6.2 - flutter: ">=3.27.4" - -dependencies: - cupertino_icons: ^1.0.3 - flutter: - sdk: flutter - stream_chat: ^9.4.0 - stream_chat_persistence: ^9.4.0 - -flutter: - uses-material-design: true \ No newline at end of file diff --git a/packages/stream_chat_persistence/example/web/favicon.png b/packages/stream_chat_persistence/example/web/favicon.png deleted file mode 100644 index 8aaa46ac1a..0000000000 Binary files a/packages/stream_chat_persistence/example/web/favicon.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/web/icons/Icon-192.png b/packages/stream_chat_persistence/example/web/icons/Icon-192.png deleted file mode 100644 index b749bfef07..0000000000 Binary files a/packages/stream_chat_persistence/example/web/icons/Icon-192.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/web/icons/Icon-512.png b/packages/stream_chat_persistence/example/web/icons/Icon-512.png deleted file mode 100644 index 88cfd48dff..0000000000 Binary files a/packages/stream_chat_persistence/example/web/icons/Icon-512.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/web/icons/Icon-maskable-192.png b/packages/stream_chat_persistence/example/web/icons/Icon-maskable-192.png deleted file mode 100644 index eb9b4d76e5..0000000000 Binary files a/packages/stream_chat_persistence/example/web/icons/Icon-maskable-192.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/web/icons/Icon-maskable-512.png b/packages/stream_chat_persistence/example/web/icons/Icon-maskable-512.png deleted file mode 100644 index d69c56691f..0000000000 Binary files a/packages/stream_chat_persistence/example/web/icons/Icon-maskable-512.png and /dev/null differ diff --git a/packages/stream_chat_persistence/example/web/index.html b/packages/stream_chat_persistence/example/web/index.html deleted file mode 100644 index 20772446a5..0000000000 --- a/packages/stream_chat_persistence/example/web/index.html +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - - - - - - - - - - - example - - - - - - - - diff --git a/packages/stream_chat_persistence/example/web/manifest.json b/packages/stream_chat_persistence/example/web/manifest.json deleted file mode 100644 index 096edf8fe4..0000000000 --- a/packages/stream_chat_persistence/example/web/manifest.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "example", - "short_name": "example", - "start_url": ".", - "display": "standalone", - "background_color": "#0175C2", - "theme_color": "#0175C2", - "description": "A new Flutter project.", - "orientation": "portrait-primary", - "prefer_related_applications": false, - "icons": [ - { - "src": "icons/Icon-192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "icons/Icon-512.png", - "sizes": "512x512", - "type": "image/png" - }, - { - "src": "icons/Icon-maskable-192.png", - "sizes": "192x192", - "type": "image/png", - "purpose": "maskable" - }, - { - "src": "icons/Icon-maskable-512.png", - "sizes": "512x512", - "type": "image/png", - "purpose": "maskable" - } - ] -} diff --git a/packages/stream_chat_persistence/example/web/sql-wasm.js b/packages/stream_chat_persistence/example/web/sql-wasm.js deleted file mode 100644 index 980a959a68..0000000000 --- a/packages/stream_chat_persistence/example/web/sql-wasm.js +++ /dev/null @@ -1,201 +0,0 @@ - -// We are modularizing this manually because the current modularize setting in Emscripten has some issues: -// https://github.com/kripken/emscripten/issues/5820 -// In addition, When you use emcc's modularization, it still expects to export a global object called `Module`, -// which is able to be used/called before the WASM is loaded. -// The modularization below exports a promise that loads and resolves to the actual sql.js module. -// That way, this module can't be used before the WASM is finished loading. - -// We are going to define a function that a user will call to start loading initializing our Sql.js library -// However, that function might be called multiple times, and on subsequent calls, we don't actually want it to instantiate a new instance of the Module -// Instead, we want to return the previously loaded module - -// TODO: Make this not declare a global if used in the browser -var initSqlJsPromise = undefined; - -var initSqlJs = function (moduleConfig) { - - if (initSqlJsPromise){ - return initSqlJsPromise; - } - // If we're here, we've never called this function before - initSqlJsPromise = new Promise(function (resolveModule, reject) { - - // We are modularizing this manually because the current modularize setting in Emscripten has some issues: - // https://github.com/kripken/emscripten/issues/5820 - - // The way to affect the loading of emcc compiled modules is to create a variable called `Module` and add - // properties to it, like `preRun`, `postRun`, etc - // We are using that to get notified when the WASM has finished loading. - // Only then will we return our promise - - // If they passed in a moduleConfig object, use that - // Otherwise, initialize Module to the empty object - var Module = typeof moduleConfig !== 'undefined' ? moduleConfig : {}; - - // EMCC only allows for a single onAbort function (not an array of functions) - // So if the user defined their own onAbort function, we remember it and call it - var originalOnAbortFunction = Module['onAbort']; - Module['onAbort'] = function (errorThatCausedAbort) { - reject(new Error(errorThatCausedAbort)); - if (originalOnAbortFunction){ - originalOnAbortFunction(errorThatCausedAbort); - } - }; - - Module['postRun'] = Module['postRun'] || []; - Module['postRun'].push(function () { - // When Emscripted calls postRun, this promise resolves with the built Module - resolveModule(Module); - }); - - // There is a section of code in the emcc-generated code below that looks like this: - // (Note that this is lowercase `module`) - // if (typeof module !== 'undefined') { - // module['exports'] = Module; - // } - // When that runs, it's going to overwrite our own modularization export efforts in shell-post.js! - // The only way to tell emcc not to emit it is to pass the MODULARIZE=1 or MODULARIZE_INSTANCE=1 flags, - // but that carries with it additional unnecessary baggage/bugs we don't want either. - // So, we have three options: - // 1) We undefine `module` - // 2) We remember what `module['exports']` was at the beginning of this function and we restore it later - // 3) We write a script to remove those lines of code as part of the Make process. - // - // Since those are the only lines of code that care about module, we will undefine it. It's the most straightforward - // of the options, and has the side effect of reducing emcc's efforts to modify the module if its output were to change in the future. - // That's a nice side effect since we're handling the modularization efforts ourselves - module = undefined; - - // The emcc-generated code and shell-post.js code goes below, - // meaning that all of it runs inside of this promise. If anything throws an exception, our promise will abort - -var e;e||(e=typeof Module !== 'undefined' ? Module : {});null; -e.onRuntimeInitialized=function(){function a(h,l){this.Qa=h;this.db=l;this.Oa=1;this.kb=[]}function b(h,l){this.db=l;l=aa(h)+1;this.cb=da(l);if(null===this.cb)throw Error("Unable to allocate memory for the SQL string");k(h,n,this.cb,l);this.ib=this.cb;this.Za=this.ob=null}function c(h){this.filename="dbfile_"+(4294967295*Math.random()>>>0);if(null!=h){var l=this.filename,q=l?r("//"+l):"/";l=ea(!0,!0);q=fa(q,(void 0!==l?l:438)&4095|32768,0);if(h){if("string"===typeof h){for(var p=Array(h.length),z= -0,N=h.length;zc;++c)f.parameters.push(d["viii"[c]]); -c=new WebAssembly.Function(f,a)}else{d=[1,0,1,96];f={i:127,j:126,f:125,d:124};d.push(3);for(c=0;3>c;++c)d.push(f["iii"[c]]);d.push(0);d[1]=d.length-2;c=new Uint8Array([0,97,115,109,1,0,0,0].concat(d,[2,7,1,1,101,1,102,0,0,7,5,1,1,102,0,0]));c=new WebAssembly.Module(c);c=(new WebAssembly.Instance(c,{e:{f:a}})).exports.f}J.set(b,c)}Ja.set(a,b);a=b}return a}var Ka;e.wasmBinary&&(Ka=e.wasmBinary);var noExitRuntime=e.noExitRuntime||!0;"object"!==typeof WebAssembly&&F("no native wasm support detected"); -function qa(a){var b="i32";"*"===b.charAt(b.length-1)&&(b="i32");switch(b){case "i1":y[a>>0]=0;break;case "i8":y[a>>0]=0;break;case "i16":La[a>>1]=0;break;case "i32":K[a>>2]=0;break;case "i64":L=[0,(M=0,1<=+Math.abs(M)?0>>0:~~+Math.ceil((M-+(~~M>>>0))/4294967296)>>>0:0)];K[a>>2]=L[0];K[a+4>>2]=L[1];break;case "float":Ma[a>>2]=0;break;case "double":Na[a>>3]=0;break;default:F("invalid type for setValue: "+b)}} -function v(a,b){b=b||"i8";"*"===b.charAt(b.length-1)&&(b="i32");switch(b){case "i1":return y[a>>0];case "i8":return y[a>>0];case "i16":return La[a>>1];case "i32":return K[a>>2];case "i64":return K[a>>2];case "float":return Ma[a>>2];case "double":return Na[a>>3];default:F("invalid type for getValue: "+b)}return null}var Oa,Pa=!1;function Qa(a){var b=e["_"+a];b||F("Assertion failed: Cannot call unknown function "+(a+", make sure it is exported"));return b} -function Ra(a,b,c,d){var f={string:function(u){var C=0;if(null!==u&&void 0!==u&&0!==u){var I=(u.length<<2)+1;C=x(I);k(u,n,C,I)}return C},array:function(u){var C=x(u.length);y.set(u,C);return C}};a=Qa(a);var g=[],m=0;if(d)for(var t=0;t=d);)++c;if(16f?d+=String.fromCharCode(f):(f-=65536,d+=String.fromCharCode(55296|f>>10,56320|f&1023))}}else d+=String.fromCharCode(f)}return d}function A(a,b){return a?Va(n,a,b):""} -function k(a,b,c,d){if(!(0=m){var t=a.charCodeAt(++g);m=65536+((m&1023)<<10)|t&1023}if(127>=m){if(c>=d)break;b[c++]=m}else{if(2047>=m){if(c+1>=d)break;b[c++]=192|m>>6}else{if(65535>=m){if(c+2>=d)break;b[c++]=224|m>>12}else{if(c+3>=d)break;b[c++]=240|m>>18;b[c++]=128|m>>12&63}b[c++]=128|m>>6&63}b[c++]=128|m&63}}b[c]=0;return c-f} -function aa(a){for(var b=0,c=0;c=d&&(d=65536+((d&1023)<<10)|a.charCodeAt(++c)&1023);127>=d?++b:b=2047>=d?b+2:65535>=d?b+3:b+4}return b}function Wa(a){var b=aa(a)+1,c=da(b);c&&k(a,y,c,b);return c}var Xa,y,n,La,K,Ma,Na; -function Ya(){var a=Oa.buffer;Xa=a;e.HEAP8=y=new Int8Array(a);e.HEAP16=La=new Int16Array(a);e.HEAP32=K=new Int32Array(a);e.HEAPU8=n=new Uint8Array(a);e.HEAPU16=new Uint16Array(a);e.HEAPU32=new Uint32Array(a);e.HEAPF32=Ma=new Float32Array(a);e.HEAPF64=Na=new Float64Array(a)}var J,Za=[],$a=[],ab=[];function bb(){var a=e.preRun.shift();Za.unshift(a)}var cb=0,db=null,eb=null;e.preloadedImages={};e.preloadedAudios={}; -function F(a){if(e.onAbort)e.onAbort(a);H(a);Pa=!0;throw new WebAssembly.RuntimeError("abort("+a+"). Build with -s ASSERTIONS=1 for more info.");}function fb(){return P.startsWith("data:application/octet-stream;base64,")}var P;P="sql-wasm.wasm";if(!fb()){var gb=P;P=e.locateFile?e.locateFile(gb,E):E+gb}function hb(){var a=P;try{if(a==P&&Ka)return new Uint8Array(Ka);if(Ea)return Ea(a);throw"both async and sync fetching of the wasm failed";}catch(b){F(b)}} -function ib(){if(!Ka&&(ya||za)){if("function"===typeof fetch&&!P.startsWith("file://"))return fetch(P,{credentials:"same-origin"}).then(function(a){if(!a.ok)throw"failed to load wasm binary file at '"+P+"'";return a.arrayBuffer()}).catch(function(){return hb()});if(Da)return new Promise(function(a,b){Da(P,function(c){a(new Uint8Array(c))},b)})}return Promise.resolve().then(function(){return hb()})}var M,L; -function jb(a){for(;0>2]=60*g;K[nb()>>2]=Number(b!=f);c=a(c);d=a(d);c=Wa(c);d=Wa(d);f>2]=c,K[ob()+4>>2]=d):(K[ob()>>2]=d,K[ob()+4>>2]=c)}var pb; -function ub(a,b){for(var c=0,d=a.length-1;0<=d;d--){var f=a[d];"."===f?a.splice(d,1):".."===f?(a.splice(d,1),c++):c&&(a.splice(d,1),c--)}if(b)for(;c;c--)a.unshift("..");return a}function r(a){var b="/"===a.charAt(0),c="/"===a.substr(-1);(a=ub(a.split("/").filter(function(d){return!!d}),!b).join("/"))||b||(a=".");a&&c&&(a+="/");return(b?"/":"")+a} -function vb(a){var b=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.exec(a).slice(1);a=b[0];b=b[1];if(!a&&!b)return".";b&&(b=b.substr(0,b.length-1));return a+b}function xb(a){if("/"===a)return"/";a=r(a);a=a.replace(/\/$/,"");var b=a.lastIndexOf("/");return-1===b?a:a.substr(b+1)} -function yb(){if("object"===typeof crypto&&"function"===typeof crypto.getRandomValues){var a=new Uint8Array(1);return function(){crypto.getRandomValues(a);return a[0]}}if(Ba)try{var b=require("crypto");return function(){return b.randomBytes(1)[0]}}catch(c){}return function(){F("randomDevice")}} -function zb(){for(var a="",b=!1,c=arguments.length-1;-1<=c&&!b;c--){b=0<=c?arguments[c]:"/";if("string"!==typeof b)throw new TypeError("Arguments to path.resolve must be strings");if(!b)return"";a=b+"/"+a;b="/"===b.charAt(0)}a=ub(a.split("/").filter(function(d){return!!d}),!b).join("/");return(b?"/":"")+a||"."}var Ab=[];function Bb(a,b){Ab[a]={input:[],output:[],bb:b};Cb(a,Db)} -var Db={open:function(a){var b=Ab[a.node.rdev];if(!b)throw new Q(43);a.tty=b;a.seekable=!1},close:function(a){a.tty.bb.flush(a.tty)},flush:function(a){a.tty.bb.flush(a.tty)},read:function(a,b,c,d){if(!a.tty||!a.tty.bb.zb)throw new Q(60);for(var f=0,g=0;g=b||(b=Math.max(b,c*(1048576>c?2:1.125)>>>0),0!=c&&(b=Math.max(b,256)),c=a.Na,a.Na=new Uint8Array(b),0=a.node.Ra)return 0;a=Math.min(a.node.Ra-f,d);if(8b)throw new Q(28);return b},rb:function(a,b,c){R.wb(a.node,b+c);a.node.Ra=Math.max(a.node.Ra,b+c)},gb:function(a,b,c,d,f,g){if(0!==b)throw new Q(28);if(32768!==(a.node.mode&61440))throw new Q(43);a=a.node.Na;if(g& -2||a.buffer!==Xa){if(0>>0)%U.length}function Tb(a){var b=Sb(a.parent.id,a.name);if(U[b]===a)U[b]=a.ab;else for(b=U[b];b;){if(b.ab===a){b.ab=a.ab;break}b=b.ab}} -function Lb(a,b){var c;if(c=(c=Ub(a,"x"))?c:a.La.lookup?0:2)throw new Q(c,a);for(c=U[Sb(a.id,b)];c;c=c.ab){var d=c.name;if(c.parent.id===a.id&&d===b)return c}return a.La.lookup(a,b)}function Jb(a,b,c,d){a=new Vb(a,b,c,d);b=Sb(a.parent.id,a.name);a.ab=U[b];return U[b]=a}function S(a){return 16384===(a&61440)}var Wb={r:0,"r+":2,w:577,"w+":578,a:1089,"a+":1090};function Xb(a){var b=["r","w","rw"][a&3];a&512&&(b+="w");return b} -function Ub(a,b){if(Pb)return 0;if(!b.includes("r")||a.mode&292){if(b.includes("w")&&!(a.mode&146)||b.includes("x")&&!(a.mode&73))return 2}else return 2;return 0}function Yb(a,b){try{return Lb(a,b),20}catch(c){}return Ub(a,"wx")}function Zb(a,b,c){try{var d=Lb(a,b)}catch(f){return f.Pa}if(a=Ub(a,"wx"))return a;if(c){if(!S(d.mode))return 54;if(d===d.parent||"/"===Rb(d))return 10}else if(S(d.mode))return 31;return 0}function $b(a){var b=4096;for(a=a||0;a<=b;a++)if(!T[a])return a;throw new Q(33);} -function ac(a,b){bc||(bc=function(){},bc.prototype={});var c=new bc,d;for(d in a)c[d]=a[d];a=c;b=$b(b);a.fd=b;return T[b]=a}var Ib={open:function(a){a.Ma=Nb[a.node.rdev].Ma;a.Ma.open&&a.Ma.open(a)},Ya:function(){throw new Q(70);}};function Cb(a,b){Nb[a]={Ma:b}} -function cc(a,b){var c="/"===b,d=!b;if(c&&Mb)throw new Q(10);if(!c&&!d){var f=W(b,{xb:!1});b=f.path;f=f.node;if(f.$a)throw new Q(10);if(!S(f.mode))throw new Q(54);}b={type:a,Sb:{},Ab:b,Kb:[]};a=a.Va(b);a.Va=b;b.root=a;c?Mb=a:f&&(f.$a=b,f.Va&&f.Va.Kb.push(b))}function fa(a,b,c){var d=W(a,{parent:!0}).node;a=xb(a);if(!a||"."===a||".."===a)throw new Q(28);var f=Yb(d,a);if(f)throw new Q(f);if(!d.La.fb)throw new Q(63);return d.La.fb(d,a,b,c)} -function X(a,b){return fa(a,(void 0!==b?b:511)&1023|16384,0)}function dc(a,b,c){"undefined"===typeof c&&(c=b,b=438);fa(a,b|8192,c)}function ec(a,b){if(!zb(a))throw new Q(44);var c=W(b,{parent:!0}).node;if(!c)throw new Q(44);b=xb(b);var d=Yb(c,b);if(d)throw new Q(d);if(!c.La.symlink)throw new Q(63);c.La.symlink(c,b,a)} -function ua(a){var b=W(a,{parent:!0}).node,c=xb(a),d=Lb(b,c),f=Zb(b,c,!1);if(f)throw new Q(f);if(!b.La.unlink)throw new Q(63);if(d.$a)throw new Q(10);try{V.willDeletePath&&V.willDeletePath(a)}catch(g){H("FS.trackingDelegate['willDeletePath']('"+a+"') threw an exception: "+g.message)}b.La.unlink(b,c);Tb(d);try{if(V.onDeletePath)V.onDeletePath(a)}catch(g){H("FS.trackingDelegate['onDeletePath']('"+a+"') threw an exception: "+g.message)}} -function Qb(a){a=W(a).node;if(!a)throw new Q(44);if(!a.La.readlink)throw new Q(28);return zb(Rb(a.parent),a.La.readlink(a))}function fc(a,b){a=W(a,{Xa:!b}).node;if(!a)throw new Q(44);if(!a.La.Ta)throw new Q(63);return a.La.Ta(a)}function gc(a){return fc(a,!0)}function ha(a,b){a="string"===typeof a?W(a,{Xa:!0}).node:a;if(!a.La.Sa)throw new Q(63);a.La.Sa(a,{mode:b&4095|a.mode&-4096,timestamp:Date.now()})} -function Ic(a){a="string"===typeof a?W(a,{Xa:!0}).node:a;if(!a.La.Sa)throw new Q(63);a.La.Sa(a,{timestamp:Date.now()})}function Jc(a,b){if(0>b)throw new Q(28);a="string"===typeof a?W(a,{Xa:!0}).node:a;if(!a.La.Sa)throw new Q(63);if(S(a.mode))throw new Q(31);if(32768!==(a.mode&61440))throw new Q(28);var c=Ub(a,"w");if(c)throw new Q(c);a.La.Sa(a,{size:b,timestamp:Date.now()})} -function ia(a,b,c,d){if(""===a)throw new Q(44);if("string"===typeof b){var f=Wb[b];if("undefined"===typeof f)throw Error("Unknown file open mode: "+b);b=f}c=b&64?("undefined"===typeof c?438:c)&4095|32768:0;if("object"===typeof a)var g=a;else{a=r(a);try{g=W(a,{Xa:!(b&131072)}).node}catch(m){}}f=!1;if(b&64)if(g){if(b&128)throw new Q(20);}else g=fa(a,c,0),f=!0;if(!g)throw new Q(44);8192===(g.mode&61440)&&(b&=-513);if(b&65536&&!S(g.mode))throw new Q(54);if(!f&&(c=g?40960===(g.mode&61440)?32:S(g.mode)&& -("r"!==Xb(b)||b&512)?31:Ub(g,Xb(b)):44))throw new Q(c);b&512&&Jc(g,0);b&=-131713;d=ac({node:g,path:Rb(g),flags:b,seekable:!0,position:0,Ma:g.Ma,Pb:[],error:!1},d);d.Ma.open&&d.Ma.open(d);!e.logReadFiles||b&1||(Lc||(Lc={}),a in Lc||(Lc[a]=1,H("FS.trackingDelegate error on read file: "+a)));try{V.onOpenFile&&(g=0,1!==(b&2097155)&&(g|=1),0!==(b&2097155)&&(g|=2),V.onOpenFile(a,g))}catch(m){H("FS.trackingDelegate['onOpenFile']('"+a+"', flags) threw an exception: "+m.message)}return d} -function la(a){if(null===a.fd)throw new Q(8);a.nb&&(a.nb=null);try{a.Ma.close&&a.Ma.close(a)}catch(b){throw b;}finally{T[a.fd]=null}a.fd=null}function Mc(a,b,c){if(null===a.fd)throw new Q(8);if(!a.seekable||!a.Ma.Ya)throw new Q(70);if(0!=c&&1!=c&&2!=c)throw new Q(28);a.position=a.Ma.Ya(a,b,c);a.Pb=[]} -function Nc(a,b,c,d,f){if(0>d||0>f)throw new Q(28);if(null===a.fd)throw new Q(8);if(1===(a.flags&2097155))throw new Q(8);if(S(a.node.mode))throw new Q(31);if(!a.Ma.read)throw new Q(28);var g="undefined"!==typeof f;if(!g)f=a.position;else if(!a.seekable)throw new Q(70);b=a.Ma.read(a,b,c,d,f);g||(a.position+=b);return b} -function ka(a,b,c,d,f,g){if(0>d||0>f)throw new Q(28);if(null===a.fd)throw new Q(8);if(0===(a.flags&2097155))throw new Q(8);if(S(a.node.mode))throw new Q(31);if(!a.Ma.write)throw new Q(28);a.seekable&&a.flags&1024&&Mc(a,0,2);var m="undefined"!==typeof f;if(!m)f=a.position;else if(!a.seekable)throw new Q(70);b=a.Ma.write(a,b,c,d,f,g);m||(a.position+=b);try{if(a.path&&V.onWriteToFile)V.onWriteToFile(a.path)}catch(t){H("FS.trackingDelegate['onWriteToFile']('"+a.path+"') threw an exception: "+t.message)}return b} -function ta(a){var b={encoding:"binary"};b=b||{};b.flags=b.flags||0;b.encoding=b.encoding||"binary";if("utf8"!==b.encoding&&"binary"!==b.encoding)throw Error('Invalid encoding type "'+b.encoding+'"');var c,d=ia(a,b.flags);a=fc(a).size;var f=new Uint8Array(a);Nc(d,f,0,a,0);"utf8"===b.encoding?c=Va(f,0):"binary"===b.encoding&&(c=f);la(d);return c} -function Oc(){Q||(Q=function(a,b){this.node=b;this.Ob=function(c){this.Pa=c};this.Ob(a);this.message="FS error"},Q.prototype=Error(),Q.prototype.constructor=Q,[44].forEach(function(a){Kb[a]=new Q(a);Kb[a].stack=""}))}var Pc;function ea(a,b){var c=0;a&&(c|=365);b&&(c|=146);return c} -function Qc(a,b,c){a=r("/dev/"+a);var d=ea(!!b,!!c);Rc||(Rc=64);var f=Rc++<<8|0;Cb(f,{open:function(g){g.seekable=!1},close:function(){c&&c.buffer&&c.buffer.length&&c(10)},read:function(g,m,t,w){for(var u=0,C=0;C>2]=d.dev;K[c+4>>2]=0;K[c+8>>2]=d.ino;K[c+12>>2]=d.mode;K[c+16>>2]=d.nlink;K[c+20>>2]=d.uid;K[c+24>>2]=d.gid;K[c+28>>2]=d.rdev;K[c+32>>2]=0;L=[d.size>>>0,(M=d.size,1<=+Math.abs(M)?0>>0:~~+Math.ceil((M-+(~~M>>>0))/4294967296)>>>0:0)];K[c+40>>2]=L[0];K[c+44>>2]=L[1];K[c+48>>2]=4096;K[c+52>>2]=d.blocks;K[c+56>>2]=d.atime.getTime()/1E3|0;K[c+60>>2]= -0;K[c+64>>2]=d.mtime.getTime()/1E3|0;K[c+68>>2]=0;K[c+72>>2]=d.ctime.getTime()/1E3|0;K[c+76>>2]=0;L=[d.ino>>>0,(M=d.ino,1<=+Math.abs(M)?0>>0:~~+Math.ceil((M-+(~~M>>>0))/4294967296)>>>0:0)];K[c+80>>2]=L[0];K[c+84>>2]=L[1];return 0}var Uc=void 0;function Vc(){Uc+=4;return K[Uc-4>>2]}function Z(a){a=T[a];if(!a)throw new Q(8);return a}var Wc;Wc=Ba?function(){var a=process.hrtime();return 1E3*a[0]+a[1]/1E6}:function(){return performance.now()}; -var Xc={};function Yc(){if(!Zc){var a={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"===typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:xa||"./this.program"},b;for(b in Xc)void 0===Xc[b]?delete a[b]:a[b]=Xc[b];var c=[];for(b in a)c.push(b+"="+a[b]);Zc=c}return Zc}var Zc; -function Vb(a,b,c,d){a||(a=this);this.parent=a;this.Va=a.Va;this.$a=null;this.id=Ob++;this.name=b;this.mode=c;this.La={};this.Ma={};this.rdev=d}Object.defineProperties(Vb.prototype,{read:{get:function(){return 365===(this.mode&365)},set:function(a){a?this.mode|=365:this.mode&=-366}},write:{get:function(){return 146===(this.mode&146)},set:function(a){a?this.mode|=146:this.mode&=-147}}});Oc();U=Array(4096);cc(R,"/");X("/tmp");X("/home");X("/home/web_user"); -(function(){X("/dev");Cb(259,{read:function(){return 0},write:function(b,c,d,f){return f}});dc("/dev/null",259);Bb(1280,Eb);Bb(1536,Fb);dc("/dev/tty",1280);dc("/dev/tty1",1536);var a=yb();Qc("random",a);Qc("urandom",a);X("/dev/shm");X("/dev/shm/tmp")})(); -(function(){X("/proc");var a=X("/proc/self");X("/proc/self/fd");cc({Va:function(){var b=Jb(a,"fd",16895,73);b.La={lookup:function(c,d){var f=T[+d];if(!f)throw new Q(8);c={parent:null,Va:{Ab:"fake"},La:{readlink:function(){return f.path}}};return c.parent=c}};return b}},"/proc/self/fd")})();function ma(a,b){var c=Array(aa(a)+1);a=k(a,c,0,c.length);b&&(c.length=a);return c} -var ad={a:function(a,b,c,d){F("Assertion failed: "+A(a)+", at: "+[b?A(b):"unknown filename",c,d?A(d):"unknown function"])},s:function(a,b){pb||(pb=!0,lb());a=new Date(1E3*K[a>>2]);K[b>>2]=a.getSeconds();K[b+4>>2]=a.getMinutes();K[b+8>>2]=a.getHours();K[b+12>>2]=a.getDate();K[b+16>>2]=a.getMonth();K[b+20>>2]=a.getFullYear()-1900;K[b+24>>2]=a.getDay();var c=new Date(a.getFullYear(),0,1);K[b+28>>2]=(a.getTime()-c.getTime())/864E5|0;K[b+36>>2]=-(60*a.getTimezoneOffset());var d=(new Date(a.getFullYear(), -6,1)).getTimezoneOffset();c=c.getTimezoneOffset();a=(d!=c&&a.getTimezoneOffset()==Math.min(c,d))|0;K[b+32>>2]=a;a=K[ob()+(a?4:0)>>2];K[b+40>>2]=a;return b},y:function(a,b){try{a=A(a);if(b&-8)var c=-28;else{var d;(d=W(a,{Xa:!0}).node)?(a="",b&4&&(a+="r"),b&2&&(a+="w"),b&1&&(a+="x"),c=a&&Ub(d,a)?-2:0):c=-44}return c}catch(f){return"undefined"!==typeof Y&&f instanceof Q||F(f),-f.Pa}},i:function(a,b){try{return a=A(a),ha(a,b),0}catch(c){return"undefined"!==typeof Y&&c instanceof Q||F(c),-c.Pa}},z:function(a){try{return a= -A(a),Ic(a),0}catch(b){return"undefined"!==typeof Y&&b instanceof Q||F(b),-b.Pa}},j:function(a,b){try{var c=T[a];if(!c)throw new Q(8);ha(c.node,b);return 0}catch(d){return"undefined"!==typeof Y&&d instanceof Q||F(d),-d.Pa}},A:function(a){try{var b=T[a];if(!b)throw new Q(8);Ic(b.node);return 0}catch(c){return"undefined"!==typeof Y&&c instanceof Q||F(c),-c.Pa}},b:function(a,b,c){Uc=c;try{var d=Z(a);switch(b){case 0:var f=Vc();return 0>f?-28:ia(d.path,d.flags,0,f).fd;case 1:case 2:return 0;case 3:return d.flags; -case 4:return f=Vc(),d.flags|=f,0;case 12:return f=Vc(),La[f+0>>1]=2,0;case 13:case 14:return 0;case 16:case 8:return-28;case 9:return K[$c()>>2]=28,-1;default:return-28}}catch(g){return"undefined"!==typeof Y&&g instanceof Q||F(g),-g.Pa}},k:function(a,b){try{var c=Z(a);return Tc(fc,c.path,b)}catch(d){return"undefined"!==typeof Y&&d instanceof Q||F(d),-d.Pa}},E:function(a,b,c){try{var d=T[a];if(!d)throw new Q(8);if(0===(d.flags&2097155))throw new Q(28);Jc(d.node,c);return 0}catch(f){return"undefined"!== -typeof Y&&f instanceof Q||F(f),-f.Pa}},D:function(a,b){try{if(0===b)return-28;if(b=c)var d=-28;else{var f=Qb(a),g=Math.min(c,aa(f)), -m=y[b+g];k(f,n,b,c+1);y[b+g]=m;d=g}return d}catch(t){return"undefined"!==typeof Y&&t instanceof Q||F(t),-t.Pa}},H:function(a){try{a=A(a);var b=W(a,{parent:!0}).node,c=xb(a),d=Lb(b,c),f=Zb(b,c,!0);if(f)throw new Q(f);if(!b.La.rmdir)throw new Q(63);if(d.$a)throw new Q(10);try{V.willDeletePath&&V.willDeletePath(a)}catch(g){H("FS.trackingDelegate['willDeletePath']('"+a+"') threw an exception: "+g.message)}b.La.rmdir(b,c);Tb(d);try{if(V.onDeletePath)V.onDeletePath(a)}catch(g){H("FS.trackingDelegate['onDeletePath']('"+ -a+"') threw an exception: "+g.message)}return 0}catch(g){return"undefined"!==typeof Y&&g instanceof Q||F(g),-g.Pa}},e:function(a,b){try{return a=A(a),Tc(fc,a,b)}catch(c){return"undefined"!==typeof Y&&c instanceof Q||F(c),-c.Pa}},x:function(a){try{return a=A(a),ua(a),0}catch(b){return"undefined"!==typeof Y&&b instanceof Q||F(b),-b.Pa}},J:function(){return 2147483648},n:function(a,b,c){n.copyWithin(a,b,b+c)},c:function(a){var b=n.length;a>>>=0;if(2147483648=c;c*=2){var d=b* -(1+.2/c);d=Math.min(d,a+100663296);d=Math.max(a,d);0>>16);Ya();var f=1;break a}catch(g){}f=void 0}if(f)return!0}return!1},r:function(a){for(var b=Wc();Wc()-b>2]=g;for(g=0;g>0]=d.charCodeAt(g);y[f>>0]=0;c+=d.length+1});return 0},q:function(a,b){var c=Yc();K[a>>2]=c.length;var d=0;c.forEach(function(f){d+=f.length+1}); -K[b>>2]=d;return 0},f:function(a){try{var b=Z(a);la(b);return 0}catch(c){return"undefined"!==typeof Y&&c instanceof Q||F(c),c.Pa}},o:function(a,b){try{var c=Z(a);y[b>>0]=c.tty?2:S(c.mode)?3:40960===(c.mode&61440)?7:4;return 0}catch(d){return"undefined"!==typeof Y&&d instanceof Q||F(d),d.Pa}},w:function(a,b,c,d){try{a:{for(var f=Z(a),g=a=0;g>2],t=Nc(f,y,K[b+8*g>>2],m,void 0);if(0>t){var w=-1;break a}a+=t;if(t>2]=w;return 0}catch(u){return"undefined"!==typeof Y&& -u instanceof Q||F(u),u.Pa}},m:function(a,b,c,d,f){try{var g=Z(a);a=4294967296*c+(b>>>0);if(-9007199254740992>=a||9007199254740992<=a)return-61;Mc(g,a,d);L=[g.position>>>0,(M=g.position,1<=+Math.abs(M)?0>>0:~~+Math.ceil((M-+(~~M>>>0))/4294967296)>>>0:0)];K[f>>2]=L[0];K[f+4>>2]=L[1];g.nb&&0===a&&0===d&&(g.nb=null);return 0}catch(m){return"undefined"!==typeof Y&&m instanceof Q||F(m),m.Pa}},G:function(a){try{var b=Z(a);return b.Ma&&b.Ma.fsync?-b.Ma.fsync(b): -0}catch(c){return"undefined"!==typeof Y&&c instanceof Q||F(c),c.Pa}},B:function(a,b,c,d){try{a:{for(var f=Z(a),g=a=0;g>2],K[b+(8*g+4)>>2],void 0);if(0>m){var t=-1;break a}a+=m}t=a}K[d>>2]=t;return 0}catch(w){return"undefined"!==typeof Y&&w instanceof Q||F(w),w.Pa}},g:function(a){var b=Date.now();K[a>>2]=b/1E3|0;K[a+4>>2]=b%1E3*1E3|0;return 0},K:function(a){var b=Date.now()/1E3|0;a&&(K[a>>2]=b);return b},C:function(a,b){if(b){var c=b+8;b=1E3*K[c>>2];b+=K[c+4>>2]/1E3}else b= -Date.now();a=A(a);try{var d=W(a,{Xa:!0}).node;d.La.Sa(d,{timestamp:Math.max(b,b)});var f=0}catch(g){if(!(g instanceof Q)){b:{f=Error();if(!f.stack){try{throw Error();}catch(m){f=m}if(!f.stack){f="(no stack trace available)";break b}}f=f.stack.toString()}e.extraStackTrace&&(f+="\n"+e.extraStackTrace());f=kb(f);throw g+" : "+f;}f=g.Pa;K[$c()>>2]=f;f=-1}return f}}; -(function(){function a(f){e.asm=f.exports;Oa=e.asm.L;Ya();J=e.asm.Ca;$a.unshift(e.asm.M);cb--;e.monitorRunDependencies&&e.monitorRunDependencies(cb);0==cb&&(null!==db&&(clearInterval(db),db=null),eb&&(f=eb,eb=null,f()))}function b(f){a(f.instance)}function c(f){return ib().then(function(g){return WebAssembly.instantiate(g,d)}).then(function(g){return g}).then(f,function(g){H("failed to asynchronously prepare wasm: "+g);F(g)})}var d={a:ad};cb++;e.monitorRunDependencies&&e.monitorRunDependencies(cb); -if(e.instantiateWasm)try{return e.instantiateWasm(d,a)}catch(f){return H("Module.instantiateWasm callback failed with error: "+f),!1}(function(){return Ka||"function"!==typeof WebAssembly.instantiateStreaming||fb()||P.startsWith("file://")||"function"!==typeof fetch?c(b):fetch(P,{credentials:"same-origin"}).then(function(f){return WebAssembly.instantiateStreaming(f,d).then(b,function(g){H("wasm streaming compile failed: "+g);H("falling back to ArrayBuffer instantiation");return c(b)})})})();return{}})(); -e.___wasm_call_ctors=function(){return(e.___wasm_call_ctors=e.asm.M).apply(null,arguments)};e._sqlite3_free=function(){return(e._sqlite3_free=e.asm.N).apply(null,arguments)};var $c=e.___errno_location=function(){return($c=e.___errno_location=e.asm.O).apply(null,arguments)};e._sqlite3_step=function(){return(e._sqlite3_step=e.asm.P).apply(null,arguments)};e._sqlite3_finalize=function(){return(e._sqlite3_finalize=e.asm.Q).apply(null,arguments)}; -e._sqlite3_prepare_v2=function(){return(e._sqlite3_prepare_v2=e.asm.R).apply(null,arguments)};e._sqlite3_reset=function(){return(e._sqlite3_reset=e.asm.S).apply(null,arguments)};e._sqlite3_clear_bindings=function(){return(e._sqlite3_clear_bindings=e.asm.T).apply(null,arguments)};e._sqlite3_value_blob=function(){return(e._sqlite3_value_blob=e.asm.U).apply(null,arguments)};e._sqlite3_value_text=function(){return(e._sqlite3_value_text=e.asm.V).apply(null,arguments)}; -e._sqlite3_value_bytes=function(){return(e._sqlite3_value_bytes=e.asm.W).apply(null,arguments)};e._sqlite3_value_double=function(){return(e._sqlite3_value_double=e.asm.X).apply(null,arguments)};e._sqlite3_value_int=function(){return(e._sqlite3_value_int=e.asm.Y).apply(null,arguments)};e._sqlite3_value_type=function(){return(e._sqlite3_value_type=e.asm.Z).apply(null,arguments)};e._sqlite3_result_blob=function(){return(e._sqlite3_result_blob=e.asm._).apply(null,arguments)}; -e._sqlite3_result_double=function(){return(e._sqlite3_result_double=e.asm.$).apply(null,arguments)};e._sqlite3_result_error=function(){return(e._sqlite3_result_error=e.asm.aa).apply(null,arguments)};e._sqlite3_result_int=function(){return(e._sqlite3_result_int=e.asm.ba).apply(null,arguments)};e._sqlite3_result_int64=function(){return(e._sqlite3_result_int64=e.asm.ca).apply(null,arguments)};e._sqlite3_result_null=function(){return(e._sqlite3_result_null=e.asm.da).apply(null,arguments)}; -e._sqlite3_result_text=function(){return(e._sqlite3_result_text=e.asm.ea).apply(null,arguments)};e._sqlite3_column_count=function(){return(e._sqlite3_column_count=e.asm.fa).apply(null,arguments)};e._sqlite3_data_count=function(){return(e._sqlite3_data_count=e.asm.ga).apply(null,arguments)};e._sqlite3_column_blob=function(){return(e._sqlite3_column_blob=e.asm.ha).apply(null,arguments)};e._sqlite3_column_bytes=function(){return(e._sqlite3_column_bytes=e.asm.ia).apply(null,arguments)}; -e._sqlite3_column_double=function(){return(e._sqlite3_column_double=e.asm.ja).apply(null,arguments)};e._sqlite3_column_text=function(){return(e._sqlite3_column_text=e.asm.ka).apply(null,arguments)};e._sqlite3_column_type=function(){return(e._sqlite3_column_type=e.asm.la).apply(null,arguments)};e._sqlite3_column_name=function(){return(e._sqlite3_column_name=e.asm.ma).apply(null,arguments)};e._sqlite3_bind_blob=function(){return(e._sqlite3_bind_blob=e.asm.na).apply(null,arguments)}; -e._sqlite3_bind_double=function(){return(e._sqlite3_bind_double=e.asm.oa).apply(null,arguments)};e._sqlite3_bind_int=function(){return(e._sqlite3_bind_int=e.asm.pa).apply(null,arguments)};e._sqlite3_bind_text=function(){return(e._sqlite3_bind_text=e.asm.qa).apply(null,arguments)};e._sqlite3_bind_parameter_index=function(){return(e._sqlite3_bind_parameter_index=e.asm.ra).apply(null,arguments)};e._sqlite3_sql=function(){return(e._sqlite3_sql=e.asm.sa).apply(null,arguments)}; -e._sqlite3_normalized_sql=function(){return(e._sqlite3_normalized_sql=e.asm.ta).apply(null,arguments)};e._sqlite3_errmsg=function(){return(e._sqlite3_errmsg=e.asm.ua).apply(null,arguments)};e._sqlite3_exec=function(){return(e._sqlite3_exec=e.asm.va).apply(null,arguments)};e._sqlite3_changes=function(){return(e._sqlite3_changes=e.asm.wa).apply(null,arguments)};e._sqlite3_close_v2=function(){return(e._sqlite3_close_v2=e.asm.xa).apply(null,arguments)}; -e._sqlite3_create_function_v2=function(){return(e._sqlite3_create_function_v2=e.asm.ya).apply(null,arguments)};e._sqlite3_open=function(){return(e._sqlite3_open=e.asm.za).apply(null,arguments)};var da=e._malloc=function(){return(da=e._malloc=e.asm.Aa).apply(null,arguments)},oa=e._free=function(){return(oa=e._free=e.asm.Ba).apply(null,arguments)};e._RegisterExtensionFunctions=function(){return(e._RegisterExtensionFunctions=e.asm.Da).apply(null,arguments)}; -var ob=e.__get_tzname=function(){return(ob=e.__get_tzname=e.asm.Ea).apply(null,arguments)},nb=e.__get_daylight=function(){return(nb=e.__get_daylight=e.asm.Fa).apply(null,arguments)},mb=e.__get_timezone=function(){return(mb=e.__get_timezone=e.asm.Ga).apply(null,arguments)},pa=e.stackSave=function(){return(pa=e.stackSave=e.asm.Ha).apply(null,arguments)},ra=e.stackRestore=function(){return(ra=e.stackRestore=e.asm.Ia).apply(null,arguments)},x=e.stackAlloc=function(){return(x=e.stackAlloc=e.asm.Ja).apply(null, -arguments)},Hb=e._memalign=function(){return(Hb=e._memalign=e.asm.Ka).apply(null,arguments)};e.cwrap=function(a,b,c,d){c=c||[];var f=c.every(function(g){return"number"===g});return"string"!==b&&f&&!d?Qa(a):function(){return Ra(a,b,c,arguments)}};e.UTF8ToString=A;e.stackSave=pa;e.stackRestore=ra;e.stackAlloc=x;var bd;eb=function cd(){bd||dd();bd||(eb=cd)}; -function dd(){function a(){if(!bd&&(bd=!0,e.calledRun=!0,!Pa)){e.noFSInit||Pc||(Pc=!0,Oc(),e.stdin=e.stdin,e.stdout=e.stdout,e.stderr=e.stderr,e.stdin?Qc("stdin",e.stdin):ec("/dev/tty","/dev/stdin"),e.stdout?Qc("stdout",null,e.stdout):ec("/dev/tty","/dev/stdout"),e.stderr?Qc("stderr",null,e.stderr):ec("/dev/tty1","/dev/stderr"),ia("/dev/stdin",0),ia("/dev/stdout",1),ia("/dev/stderr",1));Pb=!1;jb($a);if(e.onRuntimeInitialized)e.onRuntimeInitialized();if(e.postRun)for("function"==typeof e.postRun&& -(e.postRun=[e.postRun]);e.postRun.length;){var b=e.postRun.shift();ab.unshift(b)}jb(ab)}}if(!(0:_DEBUG>") -endfunction() - -set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") - -# Flutter library and tool build rules. -add_subdirectory(${FLUTTER_MANAGED_DIR}) - -# Application build -add_subdirectory("runner") - -# Generated plugin build rules, which manage building the plugins and adding -# them to the application. -include(flutter/generated_plugins.cmake) - - -# === Installation === -# Support files are copied into place next to the executable, so that it can -# run in place. This is done instead of making a separate bundle (as on Linux) -# so that building and running from within Visual Studio will work. -set(BUILD_BUNDLE_DIR "$") -# Make the "install" step default, as it's required to run. -set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) -endif() - -set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") -set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") - -install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) - -if(PLUGIN_BUNDLED_LIBRARIES) - install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" - DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endif() - -# Fully re-copy the assets directory on each build to avoid having stale files -# from a previous install. -set(FLUTTER_ASSET_DIR_NAME "flutter_assets") -install(CODE " - file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") - " COMPONENT Runtime) -install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" - DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) - -# Install the AOT library on non-Debug builds only. -install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - CONFIGURATIONS Profile;Release - COMPONENT Runtime) diff --git a/packages/stream_chat_persistence/example/windows/flutter/CMakeLists.txt b/packages/stream_chat_persistence/example/windows/flutter/CMakeLists.txt deleted file mode 100644 index b2e4bd8d65..0000000000 --- a/packages/stream_chat_persistence/example/windows/flutter/CMakeLists.txt +++ /dev/null @@ -1,103 +0,0 @@ -cmake_minimum_required(VERSION 3.14) - -set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") - -# Configuration provided via flutter tool. -include(${EPHEMERAL_DIR}/generated_config.cmake) - -# TODO: Move the rest of this into files in ephemeral. See -# https://github.com/flutter/flutter/issues/57146. -set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") - -# === Flutter Library === -set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") - -# Published to parent scope for install step. -set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) -set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) -set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) -set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) - -list(APPEND FLUTTER_LIBRARY_HEADERS - "flutter_export.h" - "flutter_windows.h" - "flutter_messenger.h" - "flutter_plugin_registrar.h" - "flutter_texture_registrar.h" -) -list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") -add_library(flutter INTERFACE) -target_include_directories(flutter INTERFACE - "${EPHEMERAL_DIR}" -) -target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") -add_dependencies(flutter flutter_assemble) - -# === Wrapper === -list(APPEND CPP_WRAPPER_SOURCES_CORE - "core_implementations.cc" - "standard_codec.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_PLUGIN - "plugin_registrar.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_APP - "flutter_engine.cc" - "flutter_view_controller.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") - -# Wrapper sources needed for a plugin. -add_library(flutter_wrapper_plugin STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} -) -apply_standard_settings(flutter_wrapper_plugin) -set_target_properties(flutter_wrapper_plugin PROPERTIES - POSITION_INDEPENDENT_CODE ON) -set_target_properties(flutter_wrapper_plugin PROPERTIES - CXX_VISIBILITY_PRESET hidden) -target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) -target_include_directories(flutter_wrapper_plugin PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_plugin flutter_assemble) - -# Wrapper sources needed for the runner. -add_library(flutter_wrapper_app STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_APP} -) -apply_standard_settings(flutter_wrapper_app) -target_link_libraries(flutter_wrapper_app PUBLIC flutter) -target_include_directories(flutter_wrapper_app PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_app flutter_assemble) - -# === Flutter tool backend === -# _phony_ is a non-existent file to force this command to run every time, -# since currently there's no way to get a full input/output list from the -# flutter tool. -set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") -set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) -add_custom_command( - OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} - ${PHONY_OUTPUT} - COMMAND ${CMAKE_COMMAND} -E env - ${FLUTTER_TOOL_ENVIRONMENT} - "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ - VERBATIM -) -add_custom_target(flutter_assemble DEPENDS - "${FLUTTER_LIBRARY}" - ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} -) diff --git a/packages/stream_chat_persistence/example/windows/flutter/generated_plugins.cmake b/packages/stream_chat_persistence/example/windows/flutter/generated_plugins.cmake deleted file mode 100644 index 8abff9572e..0000000000 --- a/packages/stream_chat_persistence/example/windows/flutter/generated_plugins.cmake +++ /dev/null @@ -1,24 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST - sqlite3_flutter_libs -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/packages/stream_chat_persistence/example/windows/runner/CMakeLists.txt b/packages/stream_chat_persistence/example/windows/runner/CMakeLists.txt deleted file mode 100644 index de2d8916b7..0000000000 --- a/packages/stream_chat_persistence/example/windows/runner/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -cmake_minimum_required(VERSION 3.14) -project(runner LANGUAGES CXX) - -add_executable(${BINARY_NAME} WIN32 - "flutter_window.cpp" - "main.cpp" - "utils.cpp" - "win32_window.cpp" - "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" - "Runner.rc" - "runner.exe.manifest" -) -apply_standard_settings(${BINARY_NAME}) -target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") -target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) -target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") -add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/packages/stream_chat_persistence/example/windows/runner/Runner.rc b/packages/stream_chat_persistence/example/windows/runner/Runner.rc deleted file mode 100644 index 5fdea291cf..0000000000 --- a/packages/stream_chat_persistence/example/windows/runner/Runner.rc +++ /dev/null @@ -1,121 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#pragma code_page(65001) -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "winres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDI_APP_ICON ICON "resources\\app_icon.ico" - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER -#else -#define VERSION_AS_NUMBER 1,0,0 -#endif - -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME -#else -#define VERSION_AS_STRING "1.0.0" -#endif - -VS_VERSION_INFO VERSIONINFO - FILEVERSION VERSION_AS_NUMBER - PRODUCTVERSION VERSION_AS_NUMBER - FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#ifdef _DEBUG - FILEFLAGS VS_FF_DEBUG -#else - FILEFLAGS 0x0L -#endif - FILEOS VOS__WINDOWS32 - FILETYPE VFT_APP - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904e4" - BEGIN - VALUE "CompanyName", "com.example" "\0" - VALUE "FileDescription", "example" "\0" - VALUE "FileVersion", VERSION_AS_STRING "\0" - VALUE "InternalName", "example" "\0" - VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0" - VALUE "OriginalFilename", "example.exe" "\0" - VALUE "ProductName", "example" "\0" - VALUE "ProductVersion", VERSION_AS_STRING "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED diff --git a/packages/stream_chat_persistence/example/windows/runner/flutter_window.cpp b/packages/stream_chat_persistence/example/windows/runner/flutter_window.cpp deleted file mode 100644 index b43b9095ea..0000000000 --- a/packages/stream_chat_persistence/example/windows/runner/flutter_window.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "flutter_window.h" - -#include - -#include "flutter/generated_plugin_registrant.h" - -FlutterWindow::FlutterWindow(const flutter::DartProject& project) - : project_(project) {} - -FlutterWindow::~FlutterWindow() {} - -bool FlutterWindow::OnCreate() { - if (!Win32Window::OnCreate()) { - return false; - } - - RECT frame = GetClientArea(); - - // The size here must match the window dimensions to avoid unnecessary surface - // creation / destruction in the startup path. - flutter_controller_ = std::make_unique( - frame.right - frame.left, frame.bottom - frame.top, project_); - // Ensure that basic setup of the controller was successful. - if (!flutter_controller_->engine() || !flutter_controller_->view()) { - return false; - } - RegisterPlugins(flutter_controller_->engine()); - SetChildContent(flutter_controller_->view()->GetNativeWindow()); - return true; -} - -void FlutterWindow::OnDestroy() { - if (flutter_controller_) { - flutter_controller_ = nullptr; - } - - Win32Window::OnDestroy(); -} - -LRESULT -FlutterWindow::MessageHandler(HWND hwnd, UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - // Give Flutter, including plugins, an opportunity to handle window messages. - if (flutter_controller_) { - std::optional result = - flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, - lparam); - if (result) { - return *result; - } - } - - switch (message) { - case WM_FONTCHANGE: - flutter_controller_->engine()->ReloadSystemFonts(); - break; - } - - return Win32Window::MessageHandler(hwnd, message, wparam, lparam); -} diff --git a/packages/stream_chat_persistence/example/windows/runner/flutter_window.h b/packages/stream_chat_persistence/example/windows/runner/flutter_window.h deleted file mode 100644 index 6da0652f05..0000000000 --- a/packages/stream_chat_persistence/example/windows/runner/flutter_window.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef RUNNER_FLUTTER_WINDOW_H_ -#define RUNNER_FLUTTER_WINDOW_H_ - -#include -#include - -#include - -#include "win32_window.h" - -// A window that does nothing but host a Flutter view. -class FlutterWindow : public Win32Window { - public: - // Creates a new FlutterWindow hosting a Flutter view running |project|. - explicit FlutterWindow(const flutter::DartProject& project); - virtual ~FlutterWindow(); - - protected: - // Win32Window: - bool OnCreate() override; - void OnDestroy() override; - LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, - LPARAM const lparam) noexcept override; - - private: - // The project to run. - flutter::DartProject project_; - - // The Flutter instance hosted by this window. - std::unique_ptr flutter_controller_; -}; - -#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/stream_chat_persistence/example/windows/runner/main.cpp b/packages/stream_chat_persistence/example/windows/runner/main.cpp deleted file mode 100644 index bcb57b0e2a..0000000000 --- a/packages/stream_chat_persistence/example/windows/runner/main.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include -#include - -#include "flutter_window.h" -#include "utils.h" - -int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, - _In_ wchar_t *command_line, _In_ int show_command) { - // Attach to console when present (e.g., 'flutter run') or create a - // new console when running with a debugger. - if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { - CreateAndAttachConsole(); - } - - // Initialize COM, so that it is available for use in the library and/or - // plugins. - ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); - - flutter::DartProject project(L"data"); - - std::vector command_line_arguments = - GetCommandLineArguments(); - - project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); - - FlutterWindow window(project); - Win32Window::Point origin(10, 10); - Win32Window::Size size(1280, 720); - if (!window.CreateAndShow(L"example", origin, size)) { - return EXIT_FAILURE; - } - window.SetQuitOnClose(true); - - ::MSG msg; - while (::GetMessage(&msg, nullptr, 0, 0)) { - ::TranslateMessage(&msg); - ::DispatchMessage(&msg); - } - - ::CoUninitialize(); - return EXIT_SUCCESS; -} diff --git a/packages/stream_chat_persistence/example/windows/runner/resource.h b/packages/stream_chat_persistence/example/windows/runner/resource.h deleted file mode 100644 index 66a65d1e4a..0000000000 --- a/packages/stream_chat_persistence/example/windows/runner/resource.h +++ /dev/null @@ -1,16 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by Runner.rc -// -#define IDI_APP_ICON 101 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/packages/stream_chat_persistence/example/windows/runner/resources/app_icon.ico b/packages/stream_chat_persistence/example/windows/runner/resources/app_icon.ico deleted file mode 100644 index c04e20caf6..0000000000 Binary files a/packages/stream_chat_persistence/example/windows/runner/resources/app_icon.ico and /dev/null differ diff --git a/packages/stream_chat_persistence/example/windows/runner/runner.exe.manifest b/packages/stream_chat_persistence/example/windows/runner/runner.exe.manifest deleted file mode 100644 index c977c4a425..0000000000 --- a/packages/stream_chat_persistence/example/windows/runner/runner.exe.manifest +++ /dev/null @@ -1,20 +0,0 @@ - - - - - PerMonitorV2 - - - - - - - - - - - - - - - diff --git a/packages/stream_chat_persistence/example/windows/runner/utils.cpp b/packages/stream_chat_persistence/example/windows/runner/utils.cpp deleted file mode 100644 index d19bdbbcc3..0000000000 --- a/packages/stream_chat_persistence/example/windows/runner/utils.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "utils.h" - -#include -#include -#include -#include - -#include - -void CreateAndAttachConsole() { - if (::AllocConsole()) { - FILE *unused; - if (freopen_s(&unused, "CONOUT$", "w", stdout)) { - _dup2(_fileno(stdout), 1); - } - if (freopen_s(&unused, "CONOUT$", "w", stderr)) { - _dup2(_fileno(stdout), 2); - } - std::ios::sync_with_stdio(); - FlutterDesktopResyncOutputStreams(); - } -} - -std::vector GetCommandLineArguments() { - // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. - int argc; - wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); - if (argv == nullptr) { - return std::vector(); - } - - std::vector command_line_arguments; - - // Skip the first argument as it's the binary name. - for (int i = 1; i < argc; i++) { - command_line_arguments.push_back(Utf8FromUtf16(argv[i])); - } - - ::LocalFree(argv); - - return command_line_arguments; -} - -std::string Utf8FromUtf16(const wchar_t* utf16_string) { - if (utf16_string == nullptr) { - return std::string(); - } - int target_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, nullptr, 0, nullptr, nullptr); - if (target_length == 0) { - return std::string(); - } - std::string utf8_string; - utf8_string.resize(target_length); - int converted_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, utf8_string.data(), - target_length, nullptr, nullptr); - if (converted_length == 0) { - return std::string(); - } - return utf8_string; -} diff --git a/packages/stream_chat_persistence/example/windows/runner/utils.h b/packages/stream_chat_persistence/example/windows/runner/utils.h deleted file mode 100644 index 3879d54755..0000000000 --- a/packages/stream_chat_persistence/example/windows/runner/utils.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef RUNNER_UTILS_H_ -#define RUNNER_UTILS_H_ - -#include -#include - -// Creates a console for the process, and redirects stdout and stderr to -// it for both the runner and the Flutter library. -void CreateAndAttachConsole(); - -// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string -// encoded in UTF-8. Returns an empty std::string on failure. -std::string Utf8FromUtf16(const wchar_t* utf16_string); - -// Gets the command line arguments passed in as a std::vector, -// encoded in UTF-8. Returns an empty std::vector on failure. -std::vector GetCommandLineArguments(); - -#endif // RUNNER_UTILS_H_ diff --git a/packages/stream_chat_persistence/example/windows/runner/win32_window.cpp b/packages/stream_chat_persistence/example/windows/runner/win32_window.cpp deleted file mode 100644 index c10f08dc7d..0000000000 --- a/packages/stream_chat_persistence/example/windows/runner/win32_window.cpp +++ /dev/null @@ -1,245 +0,0 @@ -#include "win32_window.h" - -#include - -#include "resource.h" - -namespace { - -constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; - -// The number of Win32Window objects that currently exist. -static int g_active_window_count = 0; - -using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); - -// Scale helper to convert logical scaler values to physical using passed in -// scale factor -int Scale(int source, double scale_factor) { - return static_cast(source * scale_factor); -} - -// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. -// This API is only needed for PerMonitor V1 awareness mode. -void EnableFullDpiSupportIfAvailable(HWND hwnd) { - HMODULE user32_module = LoadLibraryA("User32.dll"); - if (!user32_module) { - return; - } - auto enable_non_client_dpi_scaling = - reinterpret_cast( - GetProcAddress(user32_module, "EnableNonClientDpiScaling")); - if (enable_non_client_dpi_scaling != nullptr) { - enable_non_client_dpi_scaling(hwnd); - FreeLibrary(user32_module); - } -} - -} // namespace - -// Manages the Win32Window's window class registration. -class WindowClassRegistrar { - public: - ~WindowClassRegistrar() = default; - - // Returns the singleton registar instance. - static WindowClassRegistrar* GetInstance() { - if (!instance_) { - instance_ = new WindowClassRegistrar(); - } - return instance_; - } - - // Returns the name of the window class, registering the class if it hasn't - // previously been registered. - const wchar_t* GetWindowClass(); - - // Unregisters the window class. Should only be called if there are no - // instances of the window. - void UnregisterWindowClass(); - - private: - WindowClassRegistrar() = default; - - static WindowClassRegistrar* instance_; - - bool class_registered_ = false; -}; - -WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; - -const wchar_t* WindowClassRegistrar::GetWindowClass() { - if (!class_registered_) { - WNDCLASS window_class{}; - window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); - window_class.lpszClassName = kWindowClassName; - window_class.style = CS_HREDRAW | CS_VREDRAW; - window_class.cbClsExtra = 0; - window_class.cbWndExtra = 0; - window_class.hInstance = GetModuleHandle(nullptr); - window_class.hIcon = - LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); - window_class.hbrBackground = 0; - window_class.lpszMenuName = nullptr; - window_class.lpfnWndProc = Win32Window::WndProc; - RegisterClass(&window_class); - class_registered_ = true; - } - return kWindowClassName; -} - -void WindowClassRegistrar::UnregisterWindowClass() { - UnregisterClass(kWindowClassName, nullptr); - class_registered_ = false; -} - -Win32Window::Win32Window() { - ++g_active_window_count; -} - -Win32Window::~Win32Window() { - --g_active_window_count; - Destroy(); -} - -bool Win32Window::CreateAndShow(const std::wstring& title, - const Point& origin, - const Size& size) { - Destroy(); - - const wchar_t* window_class = - WindowClassRegistrar::GetInstance()->GetWindowClass(); - - const POINT target_point = {static_cast(origin.x), - static_cast(origin.y)}; - HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); - UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); - double scale_factor = dpi / 96.0; - - HWND window = CreateWindow( - window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, - Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), - Scale(size.width, scale_factor), Scale(size.height, scale_factor), - nullptr, nullptr, GetModuleHandle(nullptr), this); - - if (!window) { - return false; - } - - return OnCreate(); -} - -// static -LRESULT CALLBACK Win32Window::WndProc(HWND const window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - if (message == WM_NCCREATE) { - auto window_struct = reinterpret_cast(lparam); - SetWindowLongPtr(window, GWLP_USERDATA, - reinterpret_cast(window_struct->lpCreateParams)); - - auto that = static_cast(window_struct->lpCreateParams); - EnableFullDpiSupportIfAvailable(window); - that->window_handle_ = window; - } else if (Win32Window* that = GetThisFromHandle(window)) { - return that->MessageHandler(window, message, wparam, lparam); - } - - return DefWindowProc(window, message, wparam, lparam); -} - -LRESULT -Win32Window::MessageHandler(HWND hwnd, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - switch (message) { - case WM_DESTROY: - window_handle_ = nullptr; - Destroy(); - if (quit_on_close_) { - PostQuitMessage(0); - } - return 0; - - case WM_DPICHANGED: { - auto newRectSize = reinterpret_cast(lparam); - LONG newWidth = newRectSize->right - newRectSize->left; - LONG newHeight = newRectSize->bottom - newRectSize->top; - - SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, - newHeight, SWP_NOZORDER | SWP_NOACTIVATE); - - return 0; - } - case WM_SIZE: { - RECT rect = GetClientArea(); - if (child_content_ != nullptr) { - // Size and position the child window. - MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, - rect.bottom - rect.top, TRUE); - } - return 0; - } - - case WM_ACTIVATE: - if (child_content_ != nullptr) { - SetFocus(child_content_); - } - return 0; - } - - return DefWindowProc(window_handle_, message, wparam, lparam); -} - -void Win32Window::Destroy() { - OnDestroy(); - - if (window_handle_) { - DestroyWindow(window_handle_); - window_handle_ = nullptr; - } - if (g_active_window_count == 0) { - WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); - } -} - -Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { - return reinterpret_cast( - GetWindowLongPtr(window, GWLP_USERDATA)); -} - -void Win32Window::SetChildContent(HWND content) { - child_content_ = content; - SetParent(content, window_handle_); - RECT frame = GetClientArea(); - - MoveWindow(content, frame.left, frame.top, frame.right - frame.left, - frame.bottom - frame.top, true); - - SetFocus(child_content_); -} - -RECT Win32Window::GetClientArea() { - RECT frame; - GetClientRect(window_handle_, &frame); - return frame; -} - -HWND Win32Window::GetHandle() { - return window_handle_; -} - -void Win32Window::SetQuitOnClose(bool quit_on_close) { - quit_on_close_ = quit_on_close; -} - -bool Win32Window::OnCreate() { - // No-op; provided for subclasses. - return true; -} - -void Win32Window::OnDestroy() { - // No-op; provided for subclasses. -} diff --git a/packages/stream_chat_persistence/example/windows/runner/win32_window.h b/packages/stream_chat_persistence/example/windows/runner/win32_window.h deleted file mode 100644 index 17ba431125..0000000000 --- a/packages/stream_chat_persistence/example/windows/runner/win32_window.h +++ /dev/null @@ -1,98 +0,0 @@ -#ifndef RUNNER_WIN32_WINDOW_H_ -#define RUNNER_WIN32_WINDOW_H_ - -#include - -#include -#include -#include - -// A class abstraction for a high DPI-aware Win32 Window. Intended to be -// inherited from by classes that wish to specialize with custom -// rendering and input handling -class Win32Window { - public: - struct Point { - unsigned int x; - unsigned int y; - Point(unsigned int x, unsigned int y) : x(x), y(y) {} - }; - - struct Size { - unsigned int width; - unsigned int height; - Size(unsigned int width, unsigned int height) - : width(width), height(height) {} - }; - - Win32Window(); - virtual ~Win32Window(); - - // Creates and shows a win32 window with |title| and position and size using - // |origin| and |size|. New windows are created on the default monitor. Window - // sizes are specified to the OS in physical pixels, hence to ensure a - // consistent size to will treat the width height passed in to this function - // as logical pixels and scale to appropriate for the default monitor. Returns - // true if the window was created successfully. - bool CreateAndShow(const std::wstring& title, - const Point& origin, - const Size& size); - - // Release OS resources associated with window. - void Destroy(); - - // Inserts |content| into the window tree. - void SetChildContent(HWND content); - - // Returns the backing Window handle to enable clients to set icon and other - // window properties. Returns nullptr if the window has been destroyed. - HWND GetHandle(); - - // If true, closing this window will quit the application. - void SetQuitOnClose(bool quit_on_close); - - // Return a RECT representing the bounds of the current client area. - RECT GetClientArea(); - - protected: - // Processes and route salient window messages for mouse handling, - // size change and DPI. Delegates handling of these to member overloads that - // inheriting classes can handle. - virtual LRESULT MessageHandler(HWND window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept; - - // Called when CreateAndShow is called, allowing subclass window-related - // setup. Subclasses should return false if setup fails. - virtual bool OnCreate(); - - // Called when Destroy is called. - virtual void OnDestroy(); - - private: - friend class WindowClassRegistrar; - - // OS callback called by message pump. Handles the WM_NCCREATE message which - // is passed when the non-client area is being created and enables automatic - // non-client DPI scaling so that the non-client area automatically - // responsponds to changes in DPI. All other messages are handled by - // MessageHandler. - static LRESULT CALLBACK WndProc(HWND const window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept; - - // Retrieves a class instance pointer for |window| - static Win32Window* GetThisFromHandle(HWND const window) noexcept; - - bool quit_on_close_ = false; - - // window handle for top level window. - HWND window_handle_ = nullptr; - - // window handle for hosted content. - HWND child_content_ = nullptr; -}; - -#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/stream_chat_persistence/lib/src/converter/converter.dart b/packages/stream_chat_persistence/lib/src/converter/converter.dart deleted file mode 100644 index 8608b30fde..0000000000 --- a/packages/stream_chat_persistence/lib/src/converter/converter.dart +++ /dev/null @@ -1,3 +0,0 @@ -export 'list_converter.dart'; -export 'map_converter.dart'; -export 'voting_visibility_converter.dart'; diff --git a/packages/stream_chat_persistence/lib/src/converter/list_converter.dart b/packages/stream_chat_persistence/lib/src/converter/list_converter.dart deleted file mode 100644 index af5d0c15b6..0000000000 --- a/packages/stream_chat_persistence/lib/src/converter/list_converter.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'dart:convert'; - -import 'package:drift/drift.dart'; - -/// Maps a [List] of type [T] into a [String] understood -/// by the sqlite backend. -class ListConverter extends TypeConverter, String> { - @override - List fromSql(String fromDb) { - return List.from(jsonDecode(fromDb) ?? []); - } - - @override - String toSql(List value) { - return jsonEncode(value); - } -} - -/// Maps a nullable [List] of type [T] into a nullable [String] understood -/// by the sqlite backend. -class NullableListConverter extends TypeConverter?, String?> { - @override - List? fromSql(String? fromDb) { - if (fromDb == null) { - return null; - } - return List.from(jsonDecode(fromDb) ?? []); - } - - @override - String? toSql(List? value) { - if (value == null) { - return null; - } - return jsonEncode(value); - } -} diff --git a/packages/stream_chat_persistence/lib/src/converter/map_converter.dart b/packages/stream_chat_persistence/lib/src/converter/map_converter.dart deleted file mode 100644 index cff5965b50..0000000000 --- a/packages/stream_chat_persistence/lib/src/converter/map_converter.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'dart:convert'; - -import 'package:drift/drift.dart'; - -/// Maps a [Map] of type [String], [T] into a [String] understood -/// by the sqlite backend. -class MapConverter extends TypeConverter, String> { - @override - Map fromSql(String fromDb) { - return Map.from(jsonDecode(fromDb) ?? {}); - } - - @override - String toSql(Map value) { - return jsonEncode(value); - } -} - -/// Maps a nullable [Map] of type [String], [T] into a nullable [String] -/// understood by the sqlite backend. -class NullableMapConverter extends TypeConverter?, String?> { - @override - Map? fromSql(String? fromDb) { - if (fromDb == null) { - return null; - } - return Map.from(jsonDecode(fromDb) ?? {}); - } - - @override - String? toSql(Map? value) { - if (value == null) { - return null; - } - return jsonEncode(value); - } -} diff --git a/packages/stream_chat_persistence/lib/src/converter/voting_visibility_converter.dart b/packages/stream_chat_persistence/lib/src/converter/voting_visibility_converter.dart deleted file mode 100644 index 2d5786d558..0000000000 --- a/packages/stream_chat_persistence/lib/src/converter/voting_visibility_converter.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:stream_chat/stream_chat.dart'; - -/// A [TypeConverter] that serializes [VotingVisibility] to a [String] column. -class VotingVisibilityConverter - extends TypeConverter { - /// Constant default constructor. - const VotingVisibilityConverter(); - - @override - VotingVisibility fromSql(String fromDb) { - for (final entry in _votingVisibilityEnumMap.entries) { - if (entry.value == fromDb) { - return entry.key; - } - } - - throw ArgumentError( - '`$fromDb` is not one of the supported values: ' - '${_votingVisibilityEnumMap.values.join(', ')}', - ); - } - - @override - String toSql(VotingVisibility value) { - return _votingVisibilityEnumMap[value]!; - } -} - -const _votingVisibilityEnumMap = { - VotingVisibility.anonymous: 'anonymous', - VotingVisibility.public: 'public', -}; diff --git a/packages/stream_chat_persistence/lib/src/dao/channel_dao.dart b/packages/stream_chat_persistence/lib/src/dao/channel_dao.dart deleted file mode 100644 index f3a3de415a..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/channel_dao.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/entity/channels.dart'; -import 'package:stream_chat_persistence/src/entity/users.dart'; -import 'package:stream_chat_persistence/src/mapper/mapper.dart'; - -part 'channel_dao.g.dart'; - -/// The Data Access Object for operations in [Channels] table. -@DriftAccessor(tables: [Channels, Users]) -class ChannelDao extends DatabaseAccessor - with _$ChannelDaoMixin { - /// Creates a new channel dao instance - ChannelDao(super.db); - - /// Get channel by cid - Future getChannelByCid(String cid) async => - (select(channels)..where((c) => c.cid.equals(cid))).join([ - leftOuterJoin(users, channels.createdById.equalsExp(users.id)), - ]).map((rows) { - final channel = rows.readTable(channels); - final createdBy = rows.readTableOrNull(users); - return channel.toChannelModel(createdBy: createdBy?.toUser()); - }).getSingleOrNull(); - - /// Delete all channels by matching cid in [cids] - /// - /// This will automatically delete the following linked records - /// 1. Channel Reads - /// 2. Channel Members - /// 3. Channel Messages -> Messages Reactions - Future deleteChannelByCids(List cids) async => - (delete(channels)..where((tbl) => tbl.cid.isIn(cids))).go(); - - /// Get the channel cids saved in the storage - Future> get cids => (select(channels) - ..orderBy([(c) => OrderingTerm.desc(c.lastMessageAt)]) - ..limit(250)) - .map((c) => c.cid) - .get(); - - /// Updates all the channels using the new [channelList] data - Future updateChannels(List channelList) => batch( - (it) => it.insertAllOnConflictUpdate( - channels, - channelList.map((c) => c.toEntity()).toList(), - ), - ); -} diff --git a/packages/stream_chat_persistence/lib/src/dao/channel_dao.g.dart b/packages/stream_chat_persistence/lib/src/dao/channel_dao.g.dart deleted file mode 100644 index d908367517..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/channel_dao.g.dart +++ /dev/null @@ -1,9 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'channel_dao.dart'; - -// ignore_for_file: type=lint -mixin _$ChannelDaoMixin on DatabaseAccessor { - $ChannelsTable get channels => attachedDatabase.channels; - $UsersTable get users => attachedDatabase.users; -} diff --git a/packages/stream_chat_persistence/lib/src/dao/channel_query_dao.dart b/packages/stream_chat_persistence/lib/src/dao/channel_query_dao.dart deleted file mode 100644 index e7a74f5845..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/channel_query_dao.dart +++ /dev/null @@ -1,126 +0,0 @@ -import 'dart:convert'; - -import 'package:drift/drift.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/entity/channel_queries.dart'; -import 'package:stream_chat_persistence/src/entity/channels.dart'; -import 'package:stream_chat_persistence/src/entity/users.dart'; -import 'package:stream_chat_persistence/src/mapper/mapper.dart'; - -part 'channel_query_dao.g.dart'; - -/// The Data Access Object for operations in [ChannelQueries] table. -@DriftAccessor(tables: [ChannelQueries, Channels, Users]) -class ChannelQueryDao extends DatabaseAccessor - with _$ChannelQueryDaoMixin { - /// Creates a new channel query dao instance - ChannelQueryDao(super.db); - - String _computeHash(Filter? filter) { - if (filter == null) { - return 'allchannels'; - } - final hash = base64Encode(utf8.encode('filter: ${jsonEncode(filter)}')); - return hash; - } - - /// Update list of channel queries - /// If [clearQueryCache] is true before the insert - /// the list of matching rows will be deleted - Future updateChannelQueries( - Filter? filter, - List cids, { - bool clearQueryCache = false, - }) async => - transaction(() async { - final hash = _computeHash(filter); - if (clearQueryCache) { - await batch((it) { - it.deleteWhere( - channelQueries, - (c) => c.queryHash.equals(hash), - ); - }); - } - - await batch((it) { - it.insertAllOnConflictUpdate( - channelQueries, - cids - .map((cid) => - ChannelQueryEntity(queryHash: hash, channelCid: cid)) - .toList(), - ); - }); - }); - - /// - Future> getCachedChannelCids(Filter? filter) { - final hash = _computeHash(filter); - return (select(channelQueries)..where((c) => c.queryHash.equals(hash))) - .map((c) => c.channelCid) - .get(); - } - - /// Get list of channels by filter, sort and paginationParams - Future> getChannels({ - Filter? filter, - List>? sort, - PaginationParams? paginationParams, - }) async { - assert(() { - if (sort != null && sort.any((it) => it.comparator == null)) { - throw ArgumentError( - 'SortOption requires a comparator in order to sort', - ); - } - return true; - }(), ''); - - final cachedChannelCids = await getCachedChannelCids(filter); - final query = select(channels)..where((c) => c.cid.isIn(cachedChannelCids)); - - final cachedChannels = await query.join([ - leftOuterJoin(users, channels.createdById.equalsExp(users.id)), - ]).map((row) { - final createdByEntity = row.readTableOrNull(users); - final channelEntity = row.readTable(channels); - return channelEntity.toChannelModel(createdBy: createdByEntity?.toUser()); - }).get(); - - var chainedComparator = (ChannelModel a, ChannelModel b) { - final dateA = a.lastMessageAt ?? a.createdAt; - final dateB = b.lastMessageAt ?? b.createdAt; - return dateB.compareTo(dateA); - }; - - if (sort != null && sort.isNotEmpty) { - chainedComparator = (a, b) { - int result; - for (final comparator in sort.map((it) => it.comparator)) { - try { - result = comparator!(a, b); - } catch (e) { - result = 0; - } - if (result != 0) return result; - } - return 0; - }; - } - - cachedChannels.sort(chainedComparator); - - final offset = paginationParams?.offset; - if (offset != null && offset > 0 && cachedChannels.isNotEmpty) { - cachedChannels.removeRange(0, offset); - } - - if (paginationParams?.limit != null) { - return cachedChannels.take(paginationParams!.limit).toList(); - } - - return cachedChannels; - } -} diff --git a/packages/stream_chat_persistence/lib/src/dao/channel_query_dao.g.dart b/packages/stream_chat_persistence/lib/src/dao/channel_query_dao.g.dart deleted file mode 100644 index 07a9dabbb6..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/channel_query_dao.g.dart +++ /dev/null @@ -1,10 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'channel_query_dao.dart'; - -// ignore_for_file: type=lint -mixin _$ChannelQueryDaoMixin on DatabaseAccessor { - $ChannelQueriesTable get channelQueries => attachedDatabase.channelQueries; - $ChannelsTable get channels => attachedDatabase.channels; - $UsersTable get users => attachedDatabase.users; -} diff --git a/packages/stream_chat_persistence/lib/src/dao/connection_event_dao.dart b/packages/stream_chat_persistence/lib/src/dao/connection_event_dao.dart deleted file mode 100644 index fa7ff9829d..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/connection_event_dao.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/entity/connection_events.dart'; - -import 'package:stream_chat_persistence/src/mapper/mapper.dart'; - -part 'connection_event_dao.g.dart'; - -/// The Data Access Object for operations in [ConnectionEvents] table. -@DriftAccessor(tables: [ConnectionEvents]) -class ConnectionEventDao extends DatabaseAccessor - with _$ConnectionEventDaoMixin { - /// Creates a new connection event dao instance - ConnectionEventDao(super.db); - - /// Get the latest stored connection event - Future get connectionEvent => select(connectionEvents) - .map((eventEntity) => eventEntity.toEvent()) - .getSingleOrNull(); - - /// Get the latest stored lastSyncAt - Future get lastSyncAt => - select(connectionEvents).getSingleOrNull().then((r) => r?.lastSyncAt); - - /// Update stored connection event with latest data - Future updateConnectionEvent(Event event) => transaction(() async { - final connectionInfo = await select(connectionEvents).getSingleOrNull(); - return into(connectionEvents).insertOnConflictUpdate( - ConnectionEventEntity( - id: 1, - type: event.type, - lastSyncAt: connectionInfo?.lastSyncAt, - lastEventAt: event.createdAt, - totalUnreadCount: - event.totalUnreadCount ?? connectionInfo?.totalUnreadCount, - ownUser: event.me?.toJson() ?? connectionInfo?.ownUser, - unreadChannels: - event.unreadChannels ?? connectionInfo?.unreadChannels, - ), - ); - }); - - /// Update stored lastSyncAt with latest data - Future updateLastSyncAt(DateTime lastSyncAt) async => - (update(connectionEvents)..where((tbl) => tbl.id.equals(1))).write( - ConnectionEventsCompanion( - lastSyncAt: Value(lastSyncAt), - ), - ); -} diff --git a/packages/stream_chat_persistence/lib/src/dao/connection_event_dao.g.dart b/packages/stream_chat_persistence/lib/src/dao/connection_event_dao.g.dart deleted file mode 100644 index 32541de5d5..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/connection_event_dao.g.dart +++ /dev/null @@ -1,9 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'connection_event_dao.dart'; - -// ignore_for_file: type=lint -mixin _$ConnectionEventDaoMixin on DatabaseAccessor { - $ConnectionEventsTable get connectionEvents => - attachedDatabase.connectionEvents; -} diff --git a/packages/stream_chat_persistence/lib/src/dao/dao.dart b/packages/stream_chat_persistence/lib/src/dao/dao.dart deleted file mode 100644 index ea76909b1f..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/dao.dart +++ /dev/null @@ -1,12 +0,0 @@ -export 'channel_dao.dart'; -export 'channel_query_dao.dart'; -export 'connection_event_dao.dart'; -export 'member_dao.dart'; -export 'message_dao.dart'; -export 'pinned_message_dao.dart'; -export 'pinned_message_reaction_dao.dart'; -export 'poll_dao.dart'; -export 'poll_vote_dao.dart'; -export 'reaction_dao.dart'; -export 'read_dao.dart'; -export 'user_dao.dart'; diff --git a/packages/stream_chat_persistence/lib/src/dao/member_dao.dart b/packages/stream_chat_persistence/lib/src/dao/member_dao.dart deleted file mode 100644 index fb345d3bff..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/member_dao.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/entity/members.dart'; -import 'package:stream_chat_persistence/src/entity/users.dart'; -import 'package:stream_chat_persistence/src/mapper/mapper.dart'; - -part 'member_dao.g.dart'; - -/// The Data Access Object for operations in [Members] table. -@DriftAccessor(tables: [Members, Users]) -class MemberDao extends DatabaseAccessor - with _$MemberDaoMixin { - /// Creates a new member dao instance - MemberDao(super.db); - - /// Get all members where [Members.channelCid] matches [cid] - Future> getMembersByCid(String cid) async => - (select(members).join([ - leftOuterJoin(users, members.userId.equalsExp(users.id)), - ]) - ..where(members.channelCid.equals(cid)) - ..orderBy([OrderingTerm.asc(members.createdAt)])) - .map((row) { - final userEntity = row.readTable(users); - final memberEntity = row.readTable(members); - return memberEntity.toMember(user: userEntity.toUser()); - }).get(); - - /// Updates all the members using the new [memberList] data - Future updateMembers(String cid, List memberList) => - bulkUpdateMembers({cid: memberList}); - - /// Bulk updates the members data of multiple channels - Future bulkUpdateMembers( - Map?> channelWithMembers, - ) { - final entities = channelWithMembers.entries - .map((entry) => - entry.value?.map( - (member) => member.toEntity(cid: entry.key), - ) ?? - []) - .expand((it) => it) - .toList(growable: false); - return batch( - (batch) => batch.insertAllOnConflictUpdate(members, entities), - ); - } - - /// Deletes all the members whose [Members.channelCid] is present in [cids] - Future deleteMemberByCids(List cids) async => batch((it) { - it.deleteWhere( - members, - (m) => m.channelCid.isIn(cids), - ); - }); -} diff --git a/packages/stream_chat_persistence/lib/src/dao/member_dao.g.dart b/packages/stream_chat_persistence/lib/src/dao/member_dao.g.dart deleted file mode 100644 index bc95ddafdc..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/member_dao.g.dart +++ /dev/null @@ -1,10 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'member_dao.dart'; - -// ignore_for_file: type=lint -mixin _$MemberDaoMixin on DatabaseAccessor { - $ChannelsTable get channels => attachedDatabase.channels; - $MembersTable get members => attachedDatabase.members; - $UsersTable get users => attachedDatabase.users; -} diff --git a/packages/stream_chat_persistence/lib/src/dao/message_dao.dart b/packages/stream_chat_persistence/lib/src/dao/message_dao.dart deleted file mode 100644 index 762da39ae6..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/message_dao.dart +++ /dev/null @@ -1,209 +0,0 @@ -import 'dart:math'; - -import 'package:drift/drift.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/entity/messages.dart'; -import 'package:stream_chat_persistence/src/entity/users.dart'; -import 'package:stream_chat_persistence/src/mapper/mapper.dart'; - -part 'message_dao.g.dart'; - -/// The Data Access Object for operations in [Messages] table. -@DriftAccessor(tables: [Messages, Users]) -class MessageDao extends DatabaseAccessor - with _$MessageDaoMixin { - /// Creates a new message dao instance - MessageDao(this._db) : super(_db); - - final DriftChatDatabase _db; - - $UsersTable get _users => alias(users, 'users'); - - $UsersTable get _pinnedByUsers => alias(users, 'pinnedByUsers'); - - /// Removes all the messages by matching [Messages.id] in [messageIds] - /// - /// This will automatically delete the following linked records - /// 1. Message Reactions - Future deleteMessageByIds(List messageIds) => - (delete(messages)..where((tbl) => tbl.id.isIn(messageIds))).go(); - - /// Removes all the messages by matching [Messages.channelCid] in [cids] - /// - /// This will automatically delete the following linked records - /// 1. Message Reactions - Future deleteMessageByCids(List cids) async => - (delete(messages)..where((tbl) => tbl.channelCid.isIn(cids))).go(); - - Future _messageFromJoinRow(TypedResult rows) async { - final userEntity = rows.readTableOrNull(_users); - final pinnedByEntity = rows.readTableOrNull(_pinnedByUsers); - final msgEntity = rows.readTable(messages); - final latestReactions = await _db.reactionDao.getReactions(msgEntity.id); - final ownReactions = await _db.reactionDao.getReactionsByUserId( - msgEntity.id, - _db.userId, - ); - Message? quotedMessage; - final quotedMessageId = msgEntity.quotedMessageId; - if (quotedMessageId != null) { - quotedMessage = await getMessageById(quotedMessageId); - } - Poll? poll; - final pollId = msgEntity.pollId; - if (pollId != null) { - poll = await _db.pollDao.getPollById(pollId); - } - return msgEntity.toMessage( - user: userEntity?.toUser(), - pinnedBy: pinnedByEntity?.toUser(), - latestReactions: latestReactions, - ownReactions: ownReactions, - quotedMessage: quotedMessage, - poll: poll, - ); - } - - /// Returns a single message by matching the [Messages.id] with [id] - Future getMessageById(String id) async => - await (select(messages).join([ - leftOuterJoin(_users, messages.userId.equalsExp(_users.id)), - leftOuterJoin( - _pinnedByUsers, - messages.pinnedByUserId.equalsExp(_pinnedByUsers.id), - ), - ]) - ..where(messages.id.equals(id))) - .map(_messageFromJoinRow) - .getSingleOrNull(); - - /// Returns all the messages of a particular thread by matching - /// [Messages.channelCid] with [cid] - Future> getThreadMessages(String cid) async => - Future.wait(await (select(messages).join([ - leftOuterJoin(_users, messages.userId.equalsExp(_users.id)), - leftOuterJoin( - _pinnedByUsers, - messages.pinnedByUserId.equalsExp(_pinnedByUsers.id), - ), - ]) - ..where(messages.channelCid.equals(cid)) - ..where(messages.parentId.isNotNull()) - ..orderBy([OrderingTerm.asc(messages.createdAt)])) - .map(_messageFromJoinRow) - .get()); - - /// Returns all the messages of a particular thread by matching - /// [Messages.parentId] with [parentId] - Future> getThreadMessagesByParentId( - String parentId, { - PaginationParams? options, - }) async { - final msgList = await Future.wait(await (select(messages).join([ - leftOuterJoin(_users, messages.userId.equalsExp(_users.id)), - leftOuterJoin( - _pinnedByUsers, - messages.pinnedByUserId.equalsExp(_pinnedByUsers.id), - ), - ]) - ..where(messages.parentId.isNotNull()) - ..where(messages.parentId.equals(parentId)) - ..orderBy([OrderingTerm.asc(messages.createdAt)])) - .map(_messageFromJoinRow) - .get()); - - if (msgList.isNotEmpty) { - if (options?.lessThan != null) { - final lessThanIndex = msgList.indexWhere( - (m) => m.id == options!.lessThan, - ); - if (lessThanIndex != -1) { - msgList.removeRange(lessThanIndex, msgList.length); - } - } - if (options?.greaterThanOrEqual != null) { - final greaterThanIndex = msgList.indexWhere( - (m) => m.id == options!.greaterThanOrEqual, - ); - if (greaterThanIndex != -1) { - msgList.removeRange(0, greaterThanIndex); - } - } - final limit = options?.limit; - if (limit != null && limit > 0) { - return msgList.take(limit).toList(); - } - } - return msgList; - } - - /// Returns all the messages of a channel by matching - /// [Messages.channelCid] with [parentId] - Future> getMessagesByCid( - String cid, { - PaginationParams? messagePagination, - }) async { - final msgList = await Future.wait(await (select(messages).join([ - leftOuterJoin(_users, messages.userId.equalsExp(_users.id)), - leftOuterJoin( - _pinnedByUsers, - messages.pinnedByUserId.equalsExp(_pinnedByUsers.id), - ), - ]) - ..where(messages.channelCid.equals(cid)) - ..where( - messages.parentId.isNull() | messages.showInChannel.equals(true), - ) - ..orderBy([OrderingTerm.asc(messages.createdAt)])) - .map(_messageFromJoinRow) - .get()); - - if (msgList.isNotEmpty) { - if (messagePagination?.lessThan != null) { - final lessThanIndex = msgList.indexWhere( - (m) => m.id == messagePagination!.lessThan, - ); - if (lessThanIndex != -1) { - msgList.removeRange(lessThanIndex, msgList.length); - } - } - if (messagePagination?.greaterThanOrEqual != null) { - final greaterThanIndex = msgList.indexWhere( - (m) => m.id == messagePagination!.greaterThanOrEqual, - ); - if (greaterThanIndex != -1) { - msgList.removeRange(0, greaterThanIndex); - } - } - if (messagePagination?.limit != null) { - return msgList - .skip(max(0, msgList.length - messagePagination!.limit)) - .toList(); - } - } - return msgList; - } - - /// Updates the message data of a particular channel with - /// the new [messageList] data - Future updateMessages(String cid, List messageList) => - bulkUpdateMessages({cid: messageList}); - - /// Bulk updates the message data of multiple channels - Future bulkUpdateMessages( - Map?> channelWithMessages, - ) { - final entities = channelWithMessages.entries - .map((entry) => - entry.value?.map( - (message) => message.toEntity(cid: entry.key), - ) ?? - []) - .expand((it) => it) - .toList(growable: false); - return batch( - (batch) => batch.insertAllOnConflictUpdate(messages, entities), - ); - } -} diff --git a/packages/stream_chat_persistence/lib/src/dao/message_dao.g.dart b/packages/stream_chat_persistence/lib/src/dao/message_dao.g.dart deleted file mode 100644 index 15520a3f87..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/message_dao.g.dart +++ /dev/null @@ -1,10 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'message_dao.dart'; - -// ignore_for_file: type=lint -mixin _$MessageDaoMixin on DatabaseAccessor { - $ChannelsTable get channels => attachedDatabase.channels; - $MessagesTable get messages => attachedDatabase.messages; - $UsersTable get users => attachedDatabase.users; -} diff --git a/packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.dart b/packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.dart deleted file mode 100644 index bf7b45b12b..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.dart +++ /dev/null @@ -1,206 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/entity/pinned_messages.dart'; -import 'package:stream_chat_persistence/src/entity/users.dart'; - -import 'package:stream_chat_persistence/src/mapper/mapper.dart'; - -part 'pinned_message_dao.g.dart'; - -/// The Data Access Object for operations in [Messages] table. -@DriftAccessor(tables: [PinnedMessages, Users]) -class PinnedMessageDao extends DatabaseAccessor - with _$PinnedMessageDaoMixin { - /// Creates a new message dao instance - PinnedMessageDao(this._db) : super(_db); - - final DriftChatDatabase _db; - - $UsersTable get _users => alias(users, 'users'); - - $UsersTable get _pinnedByUsers => alias(users, 'pinnedByUsers'); - - /// Removes all the messages by matching [PinnedMessages.id] in [messageIds] - /// - /// This will automatically delete the following linked records - /// 1. Message Reactions - Future deleteMessageByIds(List messageIds) => - (delete(pinnedMessages)..where((tbl) => tbl.id.isIn(messageIds))).go(); - - /// Removes all the messages by matching [PinnedMessages.channelCid] in [cids] - /// - /// This will automatically delete the following linked records - /// 1. Message Reactions - Future deleteMessageByCids(List cids) async => - (delete(pinnedMessages)..where((tbl) => tbl.channelCid.isIn(cids))).go(); - - Future _messageFromJoinRow(TypedResult rows) async { - final userEntity = rows.readTableOrNull(users); - final pinnedByEntity = rows.readTableOrNull(_pinnedByUsers); - final msgEntity = rows.readTable(pinnedMessages); - final latestReactions = - await _db.pinnedMessageReactionDao.getReactions(msgEntity.id); - final ownReactions = - await _db.pinnedMessageReactionDao.getReactionsByUserId( - msgEntity.id, - _db.userId, - ); - Message? quotedMessage; - final quotedMessageId = msgEntity.quotedMessageId; - if (quotedMessageId != null) { - quotedMessage = await getMessageById(quotedMessageId); - } - Poll? poll; - final pollId = msgEntity.pollId; - if (pollId != null) { - poll = await _db.pollDao.getPollById(pollId); - } - return msgEntity.toMessage( - user: userEntity?.toUser(), - pinnedBy: pinnedByEntity?.toUser(), - latestReactions: latestReactions, - ownReactions: ownReactions, - quotedMessage: quotedMessage, - poll: poll, - ); - } - - /// Returns a single message by matching the [PinnedMessages.id] with [id] - Future getMessageById(String id) async => - await (select(pinnedMessages).join([ - leftOuterJoin(_users, pinnedMessages.userId.equalsExp(_users.id)), - leftOuterJoin( - _pinnedByUsers, - pinnedMessages.pinnedByUserId.equalsExp(_pinnedByUsers.id), - ), - ]) - ..where(pinnedMessages.id.equals(id))) - .map(_messageFromJoinRow) - .getSingleOrNull(); - - /// Returns all the messages of a particular thread by matching - /// [PinnedMessages.channelCid] with [cid] - Future> getThreadMessages(String cid) async => - Future.wait(await (select(pinnedMessages).join([ - leftOuterJoin(_users, pinnedMessages.userId.equalsExp(_users.id)), - leftOuterJoin( - _pinnedByUsers, - pinnedMessages.pinnedByUserId.equalsExp(_pinnedByUsers.id), - ), - ]) - ..where(pinnedMessages.channelCid.equals(cid)) - ..where(pinnedMessages.parentId.isNotNull()) - ..orderBy([OrderingTerm.asc(pinnedMessages.createdAt)])) - .map(_messageFromJoinRow) - .get()); - - /// Returns all the messages of a particular thread by matching - /// [PinnedMessages.parentId] with [parentId] - Future> getThreadMessagesByParentId( - String parentId, { - PaginationParams? options, - }) async { - final msgList = await Future.wait(await (select(pinnedMessages).join([ - leftOuterJoin(_users, pinnedMessages.userId.equalsExp(_users.id)), - leftOuterJoin( - _pinnedByUsers, - pinnedMessages.pinnedByUserId.equalsExp(_pinnedByUsers.id), - ), - ]) - ..where(pinnedMessages.parentId.isNotNull()) - ..where(pinnedMessages.parentId.equals(parentId)) - ..orderBy([OrderingTerm.asc(pinnedMessages.createdAt)])) - .map(_messageFromJoinRow) - .get()); - - if (msgList.isNotEmpty) { - if (options?.lessThan != null) { - final lessThanIndex = msgList.indexWhere( - (m) => m.id == options!.lessThan, - ); - if (lessThanIndex != -1) { - msgList.removeRange(lessThanIndex, msgList.length); - } - } - if (options?.greaterThanOrEqual != null) { - final greaterThanIndex = msgList.indexWhere( - (m) => m.id == options!.greaterThanOrEqual, - ); - if (greaterThanIndex != -1) { - msgList.removeRange(0, greaterThanIndex); - } - } - if (options?.limit != null) { - return msgList.take(options!.limit).toList(); - } - } - return msgList; - } - - /// Returns all the messages of a channel by matching - /// [PinnedMessages.channelCid] with [parentId] - Future> getMessagesByCid( - String cid, { - PaginationParams? messagePagination, - }) async { - final msgList = await Future.wait(await (select(pinnedMessages).join([ - leftOuterJoin(_users, pinnedMessages.userId.equalsExp(_users.id)), - leftOuterJoin( - _pinnedByUsers, - pinnedMessages.pinnedByUserId.equalsExp(_pinnedByUsers.id), - ), - ]) - ..where(pinnedMessages.channelCid.equals(cid)) - ..where(pinnedMessages.parentId.isNull() | - pinnedMessages.showInChannel.equals(true)) - ..orderBy([OrderingTerm.asc(pinnedMessages.createdAt)])) - .map(_messageFromJoinRow) - .get()); - - if (msgList.isNotEmpty) { - if (messagePagination?.lessThan != null) { - final lessThanIndex = msgList.indexWhere( - (m) => m.id == messagePagination!.lessThan, - ); - if (lessThanIndex != -1) { - msgList.removeRange(lessThanIndex, msgList.length); - } - } - if (messagePagination?.greaterThanOrEqual != null) { - final greaterThanIndex = msgList.indexWhere( - (m) => m.id == messagePagination!.greaterThanOrEqual, - ); - if (greaterThanIndex != -1) { - msgList.removeRange(0, greaterThanIndex); - } - } - if (messagePagination?.limit != null) { - return msgList.take(messagePagination!.limit).toList(); - } - } - return msgList; - } - - /// Updates the message data of a particular channel with - /// the new [messageList] data - Future updateMessages(String cid, List messageList) => - bulkUpdateMessages({cid: messageList}); - - /// Bulk updates the message data of multiple channels - Future bulkUpdateMessages( - Map?> channelWithMessages, - ) { - final entities = channelWithMessages.entries - .map((entry) => - entry.value?.map( - (message) => message.toPinnedEntity(cid: entry.key), - ) ?? - []) - .expand((it) => it) - .toList(growable: false); - return batch( - (batch) => batch.insertAllOnConflictUpdate(pinnedMessages, entities), - ); - } -} diff --git a/packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.g.dart b/packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.g.dart deleted file mode 100644 index c07e998f67..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.g.dart +++ /dev/null @@ -1,9 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'pinned_message_dao.dart'; - -// ignore_for_file: type=lint -mixin _$PinnedMessageDaoMixin on DatabaseAccessor { - $PinnedMessagesTable get pinnedMessages => attachedDatabase.pinnedMessages; - $UsersTable get users => attachedDatabase.users; -} diff --git a/packages/stream_chat_persistence/lib/src/dao/pinned_message_reaction_dao.dart b/packages/stream_chat_persistence/lib/src/dao/pinned_message_reaction_dao.dart deleted file mode 100644 index c5c5a6d456..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/pinned_message_reaction_dao.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/entity/pinned_message_reactions.dart'; -import 'package:stream_chat_persistence/src/entity/users.dart'; -import 'package:stream_chat_persistence/src/mapper/mapper.dart'; - -part 'pinned_message_reaction_dao.g.dart'; - -/// The Data Access Object for operations in [PinnedMessageReactions] table. -@DriftAccessor(tables: [PinnedMessageReactions, Users]) -class PinnedMessageReactionDao extends DatabaseAccessor - with _$PinnedMessageReactionDaoMixin { - /// Creates a new reaction dao instance - PinnedMessageReactionDao(super.db); - - /// Returns all the reactions of a particular message by matching - /// [Reactions.messageId] with [messageId] - Future> getReactions(String messageId) => - (select(pinnedMessageReactions).join([ - leftOuterJoin(users, pinnedMessageReactions.userId.equalsExp(users.id)), - ]) - ..where(pinnedMessageReactions.messageId.equals(messageId)) - ..orderBy([OrderingTerm.asc(pinnedMessageReactions.createdAt)])) - .map((rows) { - final userEntity = rows.readTableOrNull(users); - final reactionEntity = rows.readTable(pinnedMessageReactions); - return reactionEntity.toReaction(user: userEntity?.toUser()); - }).get(); - - /// Returns all the reactions of a particular message - /// added by a particular user by matching - /// [Reactions.messageId] with [messageId] and - /// [Reactions.userId] with [userId] - Future> getReactionsByUserId( - String messageId, - String userId, - ) async { - final reactions = await getReactions(messageId); - return reactions.where((it) => it.userId == userId).toList(); - } - - /// Updates the reactions data with the new [reactionList] data - Future updateReactions(List reactionList) => batch((it) { - it.insertAllOnConflictUpdate( - pinnedMessageReactions, - reactionList.map((r) => r.toPinnedEntity()).toList(), - ); - }); - - /// Deletes all the reactions whose [Reactions.messageId] is - /// present in [messageIds] - Future deleteReactionsByMessageIds(List messageIds) => - batch((it) { - it.deleteWhere( - pinnedMessageReactions, - (r) => r.messageId.isIn(messageIds), - ); - }); -} diff --git a/packages/stream_chat_persistence/lib/src/dao/pinned_message_reaction_dao.g.dart b/packages/stream_chat_persistence/lib/src/dao/pinned_message_reaction_dao.g.dart deleted file mode 100644 index d25c2f4512..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/pinned_message_reaction_dao.g.dart +++ /dev/null @@ -1,11 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'pinned_message_reaction_dao.dart'; - -// ignore_for_file: type=lint -mixin _$PinnedMessageReactionDaoMixin on DatabaseAccessor { - $PinnedMessagesTable get pinnedMessages => attachedDatabase.pinnedMessages; - $PinnedMessageReactionsTable get pinnedMessageReactions => - attachedDatabase.pinnedMessageReactions; - $UsersTable get users => attachedDatabase.users; -} diff --git a/packages/stream_chat_persistence/lib/src/dao/poll_dao.dart b/packages/stream_chat_persistence/lib/src/dao/poll_dao.dart deleted file mode 100644 index 10a10a024d..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/poll_dao.dart +++ /dev/null @@ -1,72 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/entity/poll_votes.dart'; -import 'package:stream_chat_persistence/src/entity/polls.dart'; -import 'package:stream_chat_persistence/src/entity/users.dart'; -import 'package:stream_chat_persistence/src/mapper/poll_mapper.dart'; -import 'package:stream_chat_persistence/src/mapper/user_mapper.dart'; - -part 'poll_dao.g.dart'; - -/// The Data Access Object for operations in [Polls] table. -@DriftAccessor(tables: [Polls, PollVotes, Users]) -class PollDao extends DatabaseAccessor with _$PollDaoMixin { - /// Creates a new poll dao instance - PollDao(this._db) : super(_db); - - final DriftChatDatabase _db; - - Future _pollFromJoinRow(TypedResult row) async { - final pollEntity = row.readTable(polls); - final userEntity = row.readTable(users); - final allVotes = await _db.pollVoteDao.getPollVotes(pollEntity.id); - final latestAnswers = allVotes.where((it) => it.isAnswer); - final ownVotesAndAnswers = allVotes.where((it) => it.userId == _db.userId); - - final latestVotesByOption = >{}; - for (final vote in allVotes) { - if (vote.isAnswer) continue; - if (vote.optionId case final optionId?) { - latestVotesByOption.update( - optionId, - (value) => [...value, vote], - ifAbsent: () => [vote], - ); - } - } - - return pollEntity.toPoll( - createdBy: userEntity.toUser(), - latestAnswers: latestAnswers.toList(), - ownVotesAndAnswers: ownVotesAndAnswers.toList(), - latestVotesByOption: latestVotesByOption, - ); - } - - /// Returns the poll by matching [Polls.id] with [pollId] - Future getPollById(String pollId) async => - await (select(polls)..where((it) => it.id.equals(pollId))) - .join([leftOuterJoin(users, polls.createdById.equalsExp(users.id))]) - .map(_pollFromJoinRow) - .getSingleOrNull(); - - /// Updates all the polls using the new [pollList] data - Future updatePolls(List pollList) => batch( - (it) => it.insertAllOnConflictUpdate( - polls, - pollList.map((it) => it.toEntity()), - ), - ); - - /// Returns the list of all the polls stored in db - Future> getPolls() async => Future.wait(await (select(polls) - ..orderBy([(it) => OrderingTerm.desc(it.createdAt)])) - .join([leftOuterJoin(users, polls.createdById.equalsExp(users.id))]) - .map(_pollFromJoinRow) - .get()); - - /// Deletes all the polls whose [Polls.id] is present in [pollIds] - Future deletePollsByIds(List pollIds) => - (delete(polls)..where((tbl) => tbl.id.isIn(pollIds))).go(); -} diff --git a/packages/stream_chat_persistence/lib/src/dao/poll_dao.g.dart b/packages/stream_chat_persistence/lib/src/dao/poll_dao.g.dart deleted file mode 100644 index 7b3c32a045..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/poll_dao.g.dart +++ /dev/null @@ -1,10 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'poll_dao.dart'; - -// ignore_for_file: type=lint -mixin _$PollDaoMixin on DatabaseAccessor { - $PollsTable get polls => attachedDatabase.polls; - $PollVotesTable get pollVotes => attachedDatabase.pollVotes; - $UsersTable get users => attachedDatabase.users; -} diff --git a/packages/stream_chat_persistence/lib/src/dao/poll_vote_dao.dart b/packages/stream_chat_persistence/lib/src/dao/poll_vote_dao.dart deleted file mode 100644 index 55884f4a70..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/poll_vote_dao.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/entity/poll_votes.dart'; -import 'package:stream_chat_persistence/src/entity/polls.dart'; -import 'package:stream_chat_persistence/src/entity/users.dart'; -import 'package:stream_chat_persistence/src/mapper/poll_vote_mapper.dart'; -import 'package:stream_chat_persistence/src/mapper/user_mapper.dart'; - -part 'poll_vote_dao.g.dart'; - -/// The Data Access Object for operations in [Polls] table. -@DriftAccessor(tables: [PollVotes, Users]) -class PollVoteDao extends DatabaseAccessor - with _$PollVoteDaoMixin { - /// Creates a new poll vote dao instance - PollVoteDao(super.db); - - /// Returns all the reactions of a particular message by matching - /// [Reactions.messageId] with [messageId] - Future> getPollVotes(String pollId) => - (select(pollVotes).join([ - leftOuterJoin(users, pollVotes.userId.equalsExp(users.id)), - ]) - ..where(pollVotes.pollId.equals(pollId)) - ..orderBy([OrderingTerm.asc(pollVotes.createdAt)])) - .map((rows) { - final userEntity = rows.readTableOrNull(users); - final pollVoteEntity = rows.readTable(pollVotes); - return pollVoteEntity.toPollVote(user: userEntity?.toUser()); - }).get(); - - /// Updates the poll votes data with the new [pollVoteList] data - Future updatePollVotes(List pollVoteList) => batch( - (it) => it.insertAllOnConflictUpdate( - pollVotes, - pollVoteList.map((it) => it.toEntity()), - ), - ); - - /// Deletes all the poll votes whose [PollVote.pollId] is - /// present in [pollIds] - Future deletePollVotesByPollIds(List pollIds) => - (delete(pollVotes)..where((tbl) => tbl.pollId.isIn(pollIds))).go(); -} diff --git a/packages/stream_chat_persistence/lib/src/dao/poll_vote_dao.g.dart b/packages/stream_chat_persistence/lib/src/dao/poll_vote_dao.g.dart deleted file mode 100644 index 9731591ebc..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/poll_vote_dao.g.dart +++ /dev/null @@ -1,10 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'poll_vote_dao.dart'; - -// ignore_for_file: type=lint -mixin _$PollVoteDaoMixin on DatabaseAccessor { - $PollsTable get polls => attachedDatabase.polls; - $PollVotesTable get pollVotes => attachedDatabase.pollVotes; - $UsersTable get users => attachedDatabase.users; -} diff --git a/packages/stream_chat_persistence/lib/src/dao/reaction_dao.dart b/packages/stream_chat_persistence/lib/src/dao/reaction_dao.dart deleted file mode 100644 index d6dae9bd99..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/reaction_dao.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/entity/reactions.dart'; -import 'package:stream_chat_persistence/src/entity/users.dart'; -import 'package:stream_chat_persistence/src/mapper/mapper.dart'; - -part 'reaction_dao.g.dart'; - -/// The Data Access Object for operations in [Reactions] table. -@DriftAccessor(tables: [Reactions, Users]) -class ReactionDao extends DatabaseAccessor - with _$ReactionDaoMixin { - /// Creates a new reaction dao instance - ReactionDao(super.db); - - /// Returns all the reactions of a particular message by matching - /// [Reactions.messageId] with [messageId] - Future> getReactions(String messageId) => - (select(reactions).join([ - leftOuterJoin(users, reactions.userId.equalsExp(users.id)), - ]) - ..where(reactions.messageId.equals(messageId)) - ..orderBy([OrderingTerm.asc(reactions.createdAt)])) - .map((rows) { - final userEntity = rows.readTableOrNull(users); - final reactionEntity = rows.readTable(reactions); - return reactionEntity.toReaction(user: userEntity?.toUser()); - }).get(); - - /// Returns all the reactions of a particular message - /// added by a particular user by matching - /// [Reactions.messageId] with [messageId] and - /// [Reactions.userId] with [userId] - Future> getReactionsByUserId( - String messageId, - String userId, - ) async { - final reactions = await getReactions(messageId); - return reactions.where((it) => it.userId == userId).toList(); - } - - /// Updates the reactions data with the new [reactionList] data - Future updateReactions(List reactionList) => batch((it) { - it.insertAllOnConflictUpdate( - reactions, - reactionList.map((r) => r.toEntity()).toList(), - ); - }); - - /// Deletes all the reactions whose [Reactions.messageId] is - /// present in [messageIds] - Future deleteReactionsByMessageIds(List messageIds) => - batch((it) { - it.deleteWhere( - reactions, - (r) => r.messageId.isIn(messageIds), - ); - }); -} diff --git a/packages/stream_chat_persistence/lib/src/dao/reaction_dao.g.dart b/packages/stream_chat_persistence/lib/src/dao/reaction_dao.g.dart deleted file mode 100644 index cdaddc17a5..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/reaction_dao.g.dart +++ /dev/null @@ -1,11 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'reaction_dao.dart'; - -// ignore_for_file: type=lint -mixin _$ReactionDaoMixin on DatabaseAccessor { - $ChannelsTable get channels => attachedDatabase.channels; - $MessagesTable get messages => attachedDatabase.messages; - $ReactionsTable get reactions => attachedDatabase.reactions; - $UsersTable get users => attachedDatabase.users; -} diff --git a/packages/stream_chat_persistence/lib/src/dao/read_dao.dart b/packages/stream_chat_persistence/lib/src/dao/read_dao.dart deleted file mode 100644 index 4e2024f1ff..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/read_dao.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/entity/reads.dart'; -import 'package:stream_chat_persistence/src/entity/users.dart'; -import 'package:stream_chat_persistence/src/mapper/mapper.dart'; - -part 'read_dao.g.dart'; - -/// The Data Access Object for operations in [Reads] table. -@DriftAccessor(tables: [Reads, Users]) -class ReadDao extends DatabaseAccessor with _$ReadDaoMixin { - /// Creates a new read dao instance - ReadDao(super.db); - - /// Get all reads where [Reads.channelCid] matches [cid] - Future> getReadsByCid(String cid) async => (select(reads).join([ - leftOuterJoin(users, reads.userId.equalsExp(users.id)), - ]) - ..where(reads.channelCid.equals(cid)) - ..orderBy([ - OrderingTerm.asc(reads.lastRead), - ])) - .map((row) { - final userEntity = row.readTable(users); - final readEntity = row.readTable(reads); - return readEntity.toRead(user: userEntity.toUser()); - }).get(); - - /// Updates the read data of a particular channel with - /// the new [readList] data - Future updateReads(String cid, List readList) => - bulkUpdateReads({cid: readList}); - - /// Bulk updates the reads data of multiple channels - Future bulkUpdateReads(Map?> channelWithReads) { - final entities = channelWithReads.entries - .map((entry) => - entry.value?.map( - (read) => read.toEntity(cid: entry.key), - ) ?? - []) - .expand((it) => it) - .toList(growable: false); - return batch((batch) => batch.insertAllOnConflictUpdate(reads, entities)); - } -} diff --git a/packages/stream_chat_persistence/lib/src/dao/read_dao.g.dart b/packages/stream_chat_persistence/lib/src/dao/read_dao.g.dart deleted file mode 100644 index 80b1c1cff1..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/read_dao.g.dart +++ /dev/null @@ -1,10 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'read_dao.dart'; - -// ignore_for_file: type=lint -mixin _$ReadDaoMixin on DatabaseAccessor { - $ChannelsTable get channels => attachedDatabase.channels; - $ReadsTable get reads => attachedDatabase.reads; - $UsersTable get users => attachedDatabase.users; -} diff --git a/packages/stream_chat_persistence/lib/src/dao/user_dao.dart b/packages/stream_chat_persistence/lib/src/dao/user_dao.dart deleted file mode 100644 index 1366dd2541..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/user_dao.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/entity/users.dart'; -import 'package:stream_chat_persistence/src/mapper/user_mapper.dart'; - -part 'user_dao.g.dart'; - -/// The Data Access Object for operations in [Users] table. -@DriftAccessor(tables: [Users]) -class UserDao extends DatabaseAccessor with _$UserDaoMixin { - /// Creates a new user dao instance - UserDao(super.db); - - /// Updates the users data with the new [userList] data - Future updateUsers(List userList) => batch( - (it) => it.insertAllOnConflictUpdate( - users, - userList.map((u) => u.toEntity()).toList(), - ), - ); - - /// Returns the list of all the users stored in db - Future> getUsers() => - (select(users)..orderBy([(u) => OrderingTerm.desc(u.createdAt)])) - .map((it) => it.toUser()) - .get(); -} diff --git a/packages/stream_chat_persistence/lib/src/dao/user_dao.g.dart b/packages/stream_chat_persistence/lib/src/dao/user_dao.g.dart deleted file mode 100644 index 06186d0f41..0000000000 --- a/packages/stream_chat_persistence/lib/src/dao/user_dao.g.dart +++ /dev/null @@ -1,8 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'user_dao.dart'; - -// ignore_for_file: type=lint -mixin _$UserDaoMixin on DatabaseAccessor { - $UsersTable get users => attachedDatabase.users; -} diff --git a/packages/stream_chat_persistence/lib/src/db/drift_chat_database.dart b/packages/stream_chat_persistence/lib/src/db/drift_chat_database.dart deleted file mode 100644 index 5acd58f154..0000000000 --- a/packages/stream_chat_persistence/lib/src/db/drift_chat_database.dart +++ /dev/null @@ -1,83 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:stream_chat/stream_chat.dart' show VotingVisibility; -import 'package:stream_chat_persistence/src/converter/converter.dart'; -import 'package:stream_chat_persistence/src/dao/dao.dart'; -import 'package:stream_chat_persistence/src/entity/entity.dart'; - -export 'shared/shared_db.dart'; - -part 'drift_chat_database.g.dart'; - -/// A chat database implemented using moor -@DriftDatabase( - tables: [ - Channels, - Messages, - PinnedMessages, - Polls, - PollVotes, - PinnedMessageReactions, - Reactions, - Users, - Members, - Reads, - ChannelQueries, - ConnectionEvents, - ], - daos: [ - UserDao, - ChannelDao, - MessageDao, - PinnedMessageDao, - PinnedMessageReactionDao, - MemberDao, - PollDao, - PollVoteDao, - ReactionDao, - ReadDao, - ChannelQueryDao, - ConnectionEventDao, - ], -) -class DriftChatDatabase extends _$DriftChatDatabase { - /// Creates a new drift chat database instance - DriftChatDatabase( - this._userId, - QueryExecutor executor, - ) : super(executor); - - final String _userId; - - /// User id to which the database is connected - String get userId => _userId; - - // you should bump this number whenever you change or add a table definition. - @override - int get schemaVersion => 16; - - @override - MigrationStrategy get migration => MigrationStrategy( - beforeOpen: (details) async { - await customStatement('PRAGMA foreign_keys = ON'); - }, - onUpgrade: (openingDetails, before, after) async { - if (before != after) { - final m = createMigrator(); - for (final table in allTables) { - await m.deleteTable(table.actualTableName); - await m.createTable(table); - } - } - }, - ); - - /// Deletes all the tables - Future flush() => batch((batch) { - allTables.forEach((table) { - delete(table).go(); - }); - }); - - /// Closes the database instance - Future disconnect() => close(); -} diff --git a/packages/stream_chat_persistence/lib/src/db/drift_chat_database.g.dart b/packages/stream_chat_persistence/lib/src/db/drift_chat_database.g.dart deleted file mode 100644 index 92a8e9f592..0000000000 --- a/packages/stream_chat_persistence/lib/src/db/drift_chat_database.g.dart +++ /dev/null @@ -1,12076 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'drift_chat_database.dart'; - -// ignore_for_file: type=lint -class $ChannelsTable extends Channels - with TableInfo<$ChannelsTable, ChannelEntity> { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - $ChannelsTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _idMeta = const VerificationMeta('id'); - @override - late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - static const VerificationMeta _typeMeta = const VerificationMeta('type'); - @override - late final GeneratedColumn type = GeneratedColumn( - 'type', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - static const VerificationMeta _cidMeta = const VerificationMeta('cid'); - @override - late final GeneratedColumn cid = GeneratedColumn( - 'cid', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - static const VerificationMeta _ownCapabilitiesMeta = - const VerificationMeta('ownCapabilities'); - @override - late final GeneratedColumnWithTypeConverter?, String> - ownCapabilities = GeneratedColumn( - 'own_capabilities', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false) - .withConverter?>( - $ChannelsTable.$converterownCapabilitiesn); - static const VerificationMeta _configMeta = const VerificationMeta('config'); - @override - late final GeneratedColumnWithTypeConverter, String> - config = GeneratedColumn('config', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true) - .withConverter>($ChannelsTable.$converterconfig); - static const VerificationMeta _frozenMeta = const VerificationMeta('frozen'); - @override - late final GeneratedColumn frozen = GeneratedColumn( - 'frozen', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("frozen" IN (0, 1))'), - defaultValue: const Constant(false)); - static const VerificationMeta _lastMessageAtMeta = - const VerificationMeta('lastMessageAt'); - @override - late final GeneratedColumn lastMessageAt = - GeneratedColumn('last_message_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _createdAtMeta = - const VerificationMeta('createdAt'); - @override - late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: currentDateAndTime); - static const VerificationMeta _updatedAtMeta = - const VerificationMeta('updatedAt'); - @override - late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: currentDateAndTime); - static const VerificationMeta _deletedAtMeta = - const VerificationMeta('deletedAt'); - @override - late final GeneratedColumn deletedAt = GeneratedColumn( - 'deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _memberCountMeta = - const VerificationMeta('memberCount'); - @override - late final GeneratedColumn memberCount = GeneratedColumn( - 'member_count', aliasedName, false, - type: DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const Constant(0)); - static const VerificationMeta _createdByIdMeta = - const VerificationMeta('createdById'); - @override - late final GeneratedColumn createdById = GeneratedColumn( - 'created_by_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _extraDataMeta = - const VerificationMeta('extraData'); - @override - late final GeneratedColumnWithTypeConverter?, String> - extraData = GeneratedColumn('extra_data', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false) - .withConverter?>( - $ChannelsTable.$converterextraDatan); - @override - List get $columns => [ - id, - type, - cid, - ownCapabilities, - config, - frozen, - lastMessageAt, - createdAt, - updatedAt, - deletedAt, - memberCount, - createdById, - extraData - ]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'channels'; - @override - VerificationContext validateIntegrity(Insertable instance, - {bool isInserting = false}) { - final context = VerificationContext(); - final data = instance.toColumns(true); - if (data.containsKey('id')) { - context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); - } else if (isInserting) { - context.missing(_idMeta); - } - if (data.containsKey('type')) { - context.handle( - _typeMeta, type.isAcceptableOrUnknown(data['type']!, _typeMeta)); - } else if (isInserting) { - context.missing(_typeMeta); - } - if (data.containsKey('cid')) { - context.handle( - _cidMeta, cid.isAcceptableOrUnknown(data['cid']!, _cidMeta)); - } else if (isInserting) { - context.missing(_cidMeta); - } - context.handle(_ownCapabilitiesMeta, const VerificationResult.success()); - context.handle(_configMeta, const VerificationResult.success()); - if (data.containsKey('frozen')) { - context.handle(_frozenMeta, - frozen.isAcceptableOrUnknown(data['frozen']!, _frozenMeta)); - } - if (data.containsKey('last_message_at')) { - context.handle( - _lastMessageAtMeta, - lastMessageAt.isAcceptableOrUnknown( - data['last_message_at']!, _lastMessageAtMeta)); - } - if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); - } - if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); - } - if (data.containsKey('deleted_at')) { - context.handle(_deletedAtMeta, - deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta)); - } - if (data.containsKey('member_count')) { - context.handle( - _memberCountMeta, - memberCount.isAcceptableOrUnknown( - data['member_count']!, _memberCountMeta)); - } - if (data.containsKey('created_by_id')) { - context.handle( - _createdByIdMeta, - createdById.isAcceptableOrUnknown( - data['created_by_id']!, _createdByIdMeta)); - } - context.handle(_extraDataMeta, const VerificationResult.success()); - return context; - } - - @override - Set get $primaryKey => {cid}; - @override - ChannelEntity map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return ChannelEntity( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - type: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}type'])!, - cid: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}cid'])!, - ownCapabilities: $ChannelsTable.$converterownCapabilitiesn.fromSql( - attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}own_capabilities'])), - config: $ChannelsTable.$converterconfig.fromSql(attachedDatabase - .typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}config'])!), - frozen: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}frozen'])!, - lastMessageAt: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}last_message_at']), - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - deletedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - memberCount: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}member_count'])!, - createdById: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}created_by_id']), - extraData: $ChannelsTable.$converterextraDatan.fromSql(attachedDatabase - .typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}extra_data'])), - ); - } - - @override - $ChannelsTable createAlias(String alias) { - return $ChannelsTable(attachedDatabase, alias); - } - - static TypeConverter, String> $converterownCapabilities = - ListConverter(); - static TypeConverter?, String?> $converterownCapabilitiesn = - NullAwareTypeConverter.wrap($converterownCapabilities); - static TypeConverter, String> $converterconfig = - MapConverter(); - static TypeConverter, String> $converterextraData = - MapConverter(); - static TypeConverter?, String?> $converterextraDatan = - NullAwareTypeConverter.wrap($converterextraData); -} - -class ChannelEntity extends DataClass implements Insertable { - /// The id of this channel - final String id; - - /// The type of this channel - final String type; - - /// The cid of this channel - final String cid; - - /// List of user permissions on this channel - final List? ownCapabilities; - - /// The channel configuration data - final Map config; - - /// True if this channel entity is frozen - final bool frozen; - - /// The date of the last message - final DateTime? lastMessageAt; - - /// The date of channel creation - final DateTime createdAt; - - /// The date of the last channel update - final DateTime updatedAt; - - /// The date of channel deletion - final DateTime? deletedAt; - - /// The count of this channel members - final int memberCount; - - /// The id of the user that created this channel - final String? createdById; - - /// Map of custom channel extraData - final Map? extraData; - const ChannelEntity( - {required this.id, - required this.type, - required this.cid, - this.ownCapabilities, - required this.config, - required this.frozen, - this.lastMessageAt, - required this.createdAt, - required this.updatedAt, - this.deletedAt, - required this.memberCount, - this.createdById, - this.extraData}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['id'] = Variable(id); - map['type'] = Variable(type); - map['cid'] = Variable(cid); - if (!nullToAbsent || ownCapabilities != null) { - map['own_capabilities'] = Variable( - $ChannelsTable.$converterownCapabilitiesn.toSql(ownCapabilities)); - } - { - map['config'] = - Variable($ChannelsTable.$converterconfig.toSql(config)); - } - map['frozen'] = Variable(frozen); - if (!nullToAbsent || lastMessageAt != null) { - map['last_message_at'] = Variable(lastMessageAt); - } - map['created_at'] = Variable(createdAt); - map['updated_at'] = Variable(updatedAt); - if (!nullToAbsent || deletedAt != null) { - map['deleted_at'] = Variable(deletedAt); - } - map['member_count'] = Variable(memberCount); - if (!nullToAbsent || createdById != null) { - map['created_by_id'] = Variable(createdById); - } - if (!nullToAbsent || extraData != null) { - map['extra_data'] = Variable( - $ChannelsTable.$converterextraDatan.toSql(extraData)); - } - return map; - } - - factory ChannelEntity.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return ChannelEntity( - id: serializer.fromJson(json['id']), - type: serializer.fromJson(json['type']), - cid: serializer.fromJson(json['cid']), - ownCapabilities: - serializer.fromJson?>(json['ownCapabilities']), - config: serializer.fromJson>(json['config']), - frozen: serializer.fromJson(json['frozen']), - lastMessageAt: serializer.fromJson(json['lastMessageAt']), - createdAt: serializer.fromJson(json['createdAt']), - updatedAt: serializer.fromJson(json['updatedAt']), - deletedAt: serializer.fromJson(json['deletedAt']), - memberCount: serializer.fromJson(json['memberCount']), - createdById: serializer.fromJson(json['createdById']), - extraData: serializer.fromJson?>(json['extraData']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'id': serializer.toJson(id), - 'type': serializer.toJson(type), - 'cid': serializer.toJson(cid), - 'ownCapabilities': serializer.toJson?>(ownCapabilities), - 'config': serializer.toJson>(config), - 'frozen': serializer.toJson(frozen), - 'lastMessageAt': serializer.toJson(lastMessageAt), - 'createdAt': serializer.toJson(createdAt), - 'updatedAt': serializer.toJson(updatedAt), - 'deletedAt': serializer.toJson(deletedAt), - 'memberCount': serializer.toJson(memberCount), - 'createdById': serializer.toJson(createdById), - 'extraData': serializer.toJson?>(extraData), - }; - } - - ChannelEntity copyWith( - {String? id, - String? type, - String? cid, - Value?> ownCapabilities = const Value.absent(), - Map? config, - bool? frozen, - Value lastMessageAt = const Value.absent(), - DateTime? createdAt, - DateTime? updatedAt, - Value deletedAt = const Value.absent(), - int? memberCount, - Value createdById = const Value.absent(), - Value?> extraData = const Value.absent()}) => - ChannelEntity( - id: id ?? this.id, - type: type ?? this.type, - cid: cid ?? this.cid, - ownCapabilities: ownCapabilities.present - ? ownCapabilities.value - : this.ownCapabilities, - config: config ?? this.config, - frozen: frozen ?? this.frozen, - lastMessageAt: - lastMessageAt.present ? lastMessageAt.value : this.lastMessageAt, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - memberCount: memberCount ?? this.memberCount, - createdById: createdById.present ? createdById.value : this.createdById, - extraData: extraData.present ? extraData.value : this.extraData, - ); - ChannelEntity copyWithCompanion(ChannelsCompanion data) { - return ChannelEntity( - id: data.id.present ? data.id.value : this.id, - type: data.type.present ? data.type.value : this.type, - cid: data.cid.present ? data.cid.value : this.cid, - ownCapabilities: data.ownCapabilities.present - ? data.ownCapabilities.value - : this.ownCapabilities, - config: data.config.present ? data.config.value : this.config, - frozen: data.frozen.present ? data.frozen.value : this.frozen, - lastMessageAt: data.lastMessageAt.present - ? data.lastMessageAt.value - : this.lastMessageAt, - createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, - updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, - memberCount: - data.memberCount.present ? data.memberCount.value : this.memberCount, - createdById: - data.createdById.present ? data.createdById.value : this.createdById, - extraData: data.extraData.present ? data.extraData.value : this.extraData, - ); - } - - @override - String toString() { - return (StringBuffer('ChannelEntity(') - ..write('id: $id, ') - ..write('type: $type, ') - ..write('cid: $cid, ') - ..write('ownCapabilities: $ownCapabilities, ') - ..write('config: $config, ') - ..write('frozen: $frozen, ') - ..write('lastMessageAt: $lastMessageAt, ') - ..write('createdAt: $createdAt, ') - ..write('updatedAt: $updatedAt, ') - ..write('deletedAt: $deletedAt, ') - ..write('memberCount: $memberCount, ') - ..write('createdById: $createdById, ') - ..write('extraData: $extraData') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash( - id, - type, - cid, - ownCapabilities, - config, - frozen, - lastMessageAt, - createdAt, - updatedAt, - deletedAt, - memberCount, - createdById, - extraData); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is ChannelEntity && - other.id == this.id && - other.type == this.type && - other.cid == this.cid && - other.ownCapabilities == this.ownCapabilities && - other.config == this.config && - other.frozen == this.frozen && - other.lastMessageAt == this.lastMessageAt && - other.createdAt == this.createdAt && - other.updatedAt == this.updatedAt && - other.deletedAt == this.deletedAt && - other.memberCount == this.memberCount && - other.createdById == this.createdById && - other.extraData == this.extraData); -} - -class ChannelsCompanion extends UpdateCompanion { - final Value id; - final Value type; - final Value cid; - final Value?> ownCapabilities; - final Value> config; - final Value frozen; - final Value lastMessageAt; - final Value createdAt; - final Value updatedAt; - final Value deletedAt; - final Value memberCount; - final Value createdById; - final Value?> extraData; - final Value rowid; - const ChannelsCompanion({ - this.id = const Value.absent(), - this.type = const Value.absent(), - this.cid = const Value.absent(), - this.ownCapabilities = const Value.absent(), - this.config = const Value.absent(), - this.frozen = const Value.absent(), - this.lastMessageAt = const Value.absent(), - this.createdAt = const Value.absent(), - this.updatedAt = const Value.absent(), - this.deletedAt = const Value.absent(), - this.memberCount = const Value.absent(), - this.createdById = const Value.absent(), - this.extraData = const Value.absent(), - this.rowid = const Value.absent(), - }); - ChannelsCompanion.insert({ - required String id, - required String type, - required String cid, - this.ownCapabilities = const Value.absent(), - required Map config, - this.frozen = const Value.absent(), - this.lastMessageAt = const Value.absent(), - this.createdAt = const Value.absent(), - this.updatedAt = const Value.absent(), - this.deletedAt = const Value.absent(), - this.memberCount = const Value.absent(), - this.createdById = const Value.absent(), - this.extraData = const Value.absent(), - this.rowid = const Value.absent(), - }) : id = Value(id), - type = Value(type), - cid = Value(cid), - config = Value(config); - static Insertable custom({ - Expression? id, - Expression? type, - Expression? cid, - Expression? ownCapabilities, - Expression? config, - Expression? frozen, - Expression? lastMessageAt, - Expression? createdAt, - Expression? updatedAt, - Expression? deletedAt, - Expression? memberCount, - Expression? createdById, - Expression? extraData, - Expression? rowid, - }) { - return RawValuesInsertable({ - if (id != null) 'id': id, - if (type != null) 'type': type, - if (cid != null) 'cid': cid, - if (ownCapabilities != null) 'own_capabilities': ownCapabilities, - if (config != null) 'config': config, - if (frozen != null) 'frozen': frozen, - if (lastMessageAt != null) 'last_message_at': lastMessageAt, - if (createdAt != null) 'created_at': createdAt, - if (updatedAt != null) 'updated_at': updatedAt, - if (deletedAt != null) 'deleted_at': deletedAt, - if (memberCount != null) 'member_count': memberCount, - if (createdById != null) 'created_by_id': createdById, - if (extraData != null) 'extra_data': extraData, - if (rowid != null) 'rowid': rowid, - }); - } - - ChannelsCompanion copyWith( - {Value? id, - Value? type, - Value? cid, - Value?>? ownCapabilities, - Value>? config, - Value? frozen, - Value? lastMessageAt, - Value? createdAt, - Value? updatedAt, - Value? deletedAt, - Value? memberCount, - Value? createdById, - Value?>? extraData, - Value? rowid}) { - return ChannelsCompanion( - id: id ?? this.id, - type: type ?? this.type, - cid: cid ?? this.cid, - ownCapabilities: ownCapabilities ?? this.ownCapabilities, - config: config ?? this.config, - frozen: frozen ?? this.frozen, - lastMessageAt: lastMessageAt ?? this.lastMessageAt, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - deletedAt: deletedAt ?? this.deletedAt, - memberCount: memberCount ?? this.memberCount, - createdById: createdById ?? this.createdById, - extraData: extraData ?? this.extraData, - rowid: rowid ?? this.rowid, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (id.present) { - map['id'] = Variable(id.value); - } - if (type.present) { - map['type'] = Variable(type.value); - } - if (cid.present) { - map['cid'] = Variable(cid.value); - } - if (ownCapabilities.present) { - map['own_capabilities'] = Variable($ChannelsTable - .$converterownCapabilitiesn - .toSql(ownCapabilities.value)); - } - if (config.present) { - map['config'] = - Variable($ChannelsTable.$converterconfig.toSql(config.value)); - } - if (frozen.present) { - map['frozen'] = Variable(frozen.value); - } - if (lastMessageAt.present) { - map['last_message_at'] = Variable(lastMessageAt.value); - } - if (createdAt.present) { - map['created_at'] = Variable(createdAt.value); - } - if (updatedAt.present) { - map['updated_at'] = Variable(updatedAt.value); - } - if (deletedAt.present) { - map['deleted_at'] = Variable(deletedAt.value); - } - if (memberCount.present) { - map['member_count'] = Variable(memberCount.value); - } - if (createdById.present) { - map['created_by_id'] = Variable(createdById.value); - } - if (extraData.present) { - map['extra_data'] = Variable( - $ChannelsTable.$converterextraDatan.toSql(extraData.value)); - } - if (rowid.present) { - map['rowid'] = Variable(rowid.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('ChannelsCompanion(') - ..write('id: $id, ') - ..write('type: $type, ') - ..write('cid: $cid, ') - ..write('ownCapabilities: $ownCapabilities, ') - ..write('config: $config, ') - ..write('frozen: $frozen, ') - ..write('lastMessageAt: $lastMessageAt, ') - ..write('createdAt: $createdAt, ') - ..write('updatedAt: $updatedAt, ') - ..write('deletedAt: $deletedAt, ') - ..write('memberCount: $memberCount, ') - ..write('createdById: $createdById, ') - ..write('extraData: $extraData, ') - ..write('rowid: $rowid') - ..write(')')) - .toString(); - } -} - -class $MessagesTable extends Messages - with TableInfo<$MessagesTable, MessageEntity> { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - $MessagesTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _idMeta = const VerificationMeta('id'); - @override - late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - static const VerificationMeta _messageTextMeta = - const VerificationMeta('messageText'); - @override - late final GeneratedColumn messageText = GeneratedColumn( - 'message_text', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _attachmentsMeta = - const VerificationMeta('attachments'); - @override - late final GeneratedColumnWithTypeConverter, String> - attachments = GeneratedColumn('attachments', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true) - .withConverter>($MessagesTable.$converterattachments); - static const VerificationMeta _stateMeta = const VerificationMeta('state'); - @override - late final GeneratedColumn state = GeneratedColumn( - 'state', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - static const VerificationMeta _typeMeta = const VerificationMeta('type'); - @override - late final GeneratedColumn type = GeneratedColumn( - 'type', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultValue: const Constant('regular')); - static const VerificationMeta _mentionedUsersMeta = - const VerificationMeta('mentionedUsers'); - @override - late final GeneratedColumnWithTypeConverter, String> - mentionedUsers = GeneratedColumn( - 'mentioned_users', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true) - .withConverter>($MessagesTable.$convertermentionedUsers); - static const VerificationMeta _reactionCountsMeta = - const VerificationMeta('reactionCounts'); - @override - late final GeneratedColumnWithTypeConverter?, String> - reactionCounts = GeneratedColumn( - 'reaction_counts', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false) - .withConverter?>( - $MessagesTable.$converterreactionCountsn); - static const VerificationMeta _reactionScoresMeta = - const VerificationMeta('reactionScores'); - @override - late final GeneratedColumnWithTypeConverter?, String> - reactionScores = GeneratedColumn( - 'reaction_scores', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false) - .withConverter?>( - $MessagesTable.$converterreactionScoresn); - static const VerificationMeta _parentIdMeta = - const VerificationMeta('parentId'); - @override - late final GeneratedColumn parentId = GeneratedColumn( - 'parent_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _quotedMessageIdMeta = - const VerificationMeta('quotedMessageId'); - @override - late final GeneratedColumn quotedMessageId = GeneratedColumn( - 'quoted_message_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _pollIdMeta = const VerificationMeta('pollId'); - @override - late final GeneratedColumn pollId = GeneratedColumn( - 'poll_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _replyCountMeta = - const VerificationMeta('replyCount'); - @override - late final GeneratedColumn replyCount = GeneratedColumn( - 'reply_count', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - static const VerificationMeta _showInChannelMeta = - const VerificationMeta('showInChannel'); - @override - late final GeneratedColumn showInChannel = GeneratedColumn( - 'show_in_channel', aliasedName, true, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("show_in_channel" IN (0, 1))')); - static const VerificationMeta _shadowedMeta = - const VerificationMeta('shadowed'); - @override - late final GeneratedColumn shadowed = GeneratedColumn( - 'shadowed', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("shadowed" IN (0, 1))'), - defaultValue: const Constant(false)); - static const VerificationMeta _commandMeta = - const VerificationMeta('command'); - @override - late final GeneratedColumn command = GeneratedColumn( - 'command', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _localCreatedAtMeta = - const VerificationMeta('localCreatedAt'); - @override - late final GeneratedColumn localCreatedAt = - GeneratedColumn('local_created_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _remoteCreatedAtMeta = - const VerificationMeta('remoteCreatedAt'); - @override - late final GeneratedColumn remoteCreatedAt = - GeneratedColumn('remote_created_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _localUpdatedAtMeta = - const VerificationMeta('localUpdatedAt'); - @override - late final GeneratedColumn localUpdatedAt = - GeneratedColumn('local_updated_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _remoteUpdatedAtMeta = - const VerificationMeta('remoteUpdatedAt'); - @override - late final GeneratedColumn remoteUpdatedAt = - GeneratedColumn('remote_updated_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _localDeletedAtMeta = - const VerificationMeta('localDeletedAt'); - @override - late final GeneratedColumn localDeletedAt = - GeneratedColumn('local_deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _remoteDeletedAtMeta = - const VerificationMeta('remoteDeletedAt'); - @override - late final GeneratedColumn remoteDeletedAt = - GeneratedColumn('remote_deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _messageTextUpdatedAtMeta = - const VerificationMeta('messageTextUpdatedAt'); - @override - late final GeneratedColumn messageTextUpdatedAt = - GeneratedColumn('message_text_updated_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _userIdMeta = const VerificationMeta('userId'); - @override - late final GeneratedColumn userId = GeneratedColumn( - 'user_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _pinnedMeta = const VerificationMeta('pinned'); - @override - late final GeneratedColumn pinned = GeneratedColumn( - 'pinned', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("pinned" IN (0, 1))'), - defaultValue: const Constant(false)); - static const VerificationMeta _pinnedAtMeta = - const VerificationMeta('pinnedAt'); - @override - late final GeneratedColumn pinnedAt = GeneratedColumn( - 'pinned_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _pinExpiresMeta = - const VerificationMeta('pinExpires'); - @override - late final GeneratedColumn pinExpires = GeneratedColumn( - 'pin_expires', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _pinnedByUserIdMeta = - const VerificationMeta('pinnedByUserId'); - @override - late final GeneratedColumn pinnedByUserId = GeneratedColumn( - 'pinned_by_user_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _channelCidMeta = - const VerificationMeta('channelCid'); - @override - late final GeneratedColumn channelCid = GeneratedColumn( - 'channel_cid', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES channels (cid) ON DELETE CASCADE')); - static const VerificationMeta _i18nMeta = const VerificationMeta('i18n'); - @override - late final GeneratedColumnWithTypeConverter?, String> - i18n = GeneratedColumn('i18n', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false) - .withConverter?>($MessagesTable.$converteri18n); - static const VerificationMeta _extraDataMeta = - const VerificationMeta('extraData'); - @override - late final GeneratedColumnWithTypeConverter?, String> - extraData = GeneratedColumn('extra_data', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false) - .withConverter?>( - $MessagesTable.$converterextraDatan); - @override - List get $columns => [ - id, - messageText, - attachments, - state, - type, - mentionedUsers, - reactionCounts, - reactionScores, - parentId, - quotedMessageId, - pollId, - replyCount, - showInChannel, - shadowed, - command, - localCreatedAt, - remoteCreatedAt, - localUpdatedAt, - remoteUpdatedAt, - localDeletedAt, - remoteDeletedAt, - messageTextUpdatedAt, - userId, - pinned, - pinnedAt, - pinExpires, - pinnedByUserId, - channelCid, - i18n, - extraData - ]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'messages'; - @override - VerificationContext validateIntegrity(Insertable instance, - {bool isInserting = false}) { - final context = VerificationContext(); - final data = instance.toColumns(true); - if (data.containsKey('id')) { - context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); - } else if (isInserting) { - context.missing(_idMeta); - } - if (data.containsKey('message_text')) { - context.handle( - _messageTextMeta, - messageText.isAcceptableOrUnknown( - data['message_text']!, _messageTextMeta)); - } - context.handle(_attachmentsMeta, const VerificationResult.success()); - if (data.containsKey('state')) { - context.handle( - _stateMeta, state.isAcceptableOrUnknown(data['state']!, _stateMeta)); - } else if (isInserting) { - context.missing(_stateMeta); - } - if (data.containsKey('type')) { - context.handle( - _typeMeta, type.isAcceptableOrUnknown(data['type']!, _typeMeta)); - } - context.handle(_mentionedUsersMeta, const VerificationResult.success()); - context.handle(_reactionCountsMeta, const VerificationResult.success()); - context.handle(_reactionScoresMeta, const VerificationResult.success()); - if (data.containsKey('parent_id')) { - context.handle(_parentIdMeta, - parentId.isAcceptableOrUnknown(data['parent_id']!, _parentIdMeta)); - } - if (data.containsKey('quoted_message_id')) { - context.handle( - _quotedMessageIdMeta, - quotedMessageId.isAcceptableOrUnknown( - data['quoted_message_id']!, _quotedMessageIdMeta)); - } - if (data.containsKey('poll_id')) { - context.handle(_pollIdMeta, - pollId.isAcceptableOrUnknown(data['poll_id']!, _pollIdMeta)); - } - if (data.containsKey('reply_count')) { - context.handle( - _replyCountMeta, - replyCount.isAcceptableOrUnknown( - data['reply_count']!, _replyCountMeta)); - } - if (data.containsKey('show_in_channel')) { - context.handle( - _showInChannelMeta, - showInChannel.isAcceptableOrUnknown( - data['show_in_channel']!, _showInChannelMeta)); - } - if (data.containsKey('shadowed')) { - context.handle(_shadowedMeta, - shadowed.isAcceptableOrUnknown(data['shadowed']!, _shadowedMeta)); - } - if (data.containsKey('command')) { - context.handle(_commandMeta, - command.isAcceptableOrUnknown(data['command']!, _commandMeta)); - } - if (data.containsKey('local_created_at')) { - context.handle( - _localCreatedAtMeta, - localCreatedAt.isAcceptableOrUnknown( - data['local_created_at']!, _localCreatedAtMeta)); - } - if (data.containsKey('remote_created_at')) { - context.handle( - _remoteCreatedAtMeta, - remoteCreatedAt.isAcceptableOrUnknown( - data['remote_created_at']!, _remoteCreatedAtMeta)); - } - if (data.containsKey('local_updated_at')) { - context.handle( - _localUpdatedAtMeta, - localUpdatedAt.isAcceptableOrUnknown( - data['local_updated_at']!, _localUpdatedAtMeta)); - } - if (data.containsKey('remote_updated_at')) { - context.handle( - _remoteUpdatedAtMeta, - remoteUpdatedAt.isAcceptableOrUnknown( - data['remote_updated_at']!, _remoteUpdatedAtMeta)); - } - if (data.containsKey('local_deleted_at')) { - context.handle( - _localDeletedAtMeta, - localDeletedAt.isAcceptableOrUnknown( - data['local_deleted_at']!, _localDeletedAtMeta)); - } - if (data.containsKey('remote_deleted_at')) { - context.handle( - _remoteDeletedAtMeta, - remoteDeletedAt.isAcceptableOrUnknown( - data['remote_deleted_at']!, _remoteDeletedAtMeta)); - } - if (data.containsKey('message_text_updated_at')) { - context.handle( - _messageTextUpdatedAtMeta, - messageTextUpdatedAt.isAcceptableOrUnknown( - data['message_text_updated_at']!, _messageTextUpdatedAtMeta)); - } - if (data.containsKey('user_id')) { - context.handle(_userIdMeta, - userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); - } - if (data.containsKey('pinned')) { - context.handle(_pinnedMeta, - pinned.isAcceptableOrUnknown(data['pinned']!, _pinnedMeta)); - } - if (data.containsKey('pinned_at')) { - context.handle(_pinnedAtMeta, - pinnedAt.isAcceptableOrUnknown(data['pinned_at']!, _pinnedAtMeta)); - } - if (data.containsKey('pin_expires')) { - context.handle( - _pinExpiresMeta, - pinExpires.isAcceptableOrUnknown( - data['pin_expires']!, _pinExpiresMeta)); - } - if (data.containsKey('pinned_by_user_id')) { - context.handle( - _pinnedByUserIdMeta, - pinnedByUserId.isAcceptableOrUnknown( - data['pinned_by_user_id']!, _pinnedByUserIdMeta)); - } - if (data.containsKey('channel_cid')) { - context.handle( - _channelCidMeta, - channelCid.isAcceptableOrUnknown( - data['channel_cid']!, _channelCidMeta)); - } else if (isInserting) { - context.missing(_channelCidMeta); - } - context.handle(_i18nMeta, const VerificationResult.success()); - context.handle(_extraDataMeta, const VerificationResult.success()); - return context; - } - - @override - Set get $primaryKey => {id}; - @override - MessageEntity map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return MessageEntity( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - messageText: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}message_text']), - attachments: $MessagesTable.$converterattachments.fromSql(attachedDatabase - .typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}attachments'])!), - state: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}state'])!, - type: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}type'])!, - mentionedUsers: $MessagesTable.$convertermentionedUsers.fromSql( - attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}mentioned_users'])!), - reactionCounts: $MessagesTable.$converterreactionCountsn.fromSql( - attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}reaction_counts'])), - reactionScores: $MessagesTable.$converterreactionScoresn.fromSql( - attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}reaction_scores'])), - parentId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}parent_id']), - quotedMessageId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}quoted_message_id']), - pollId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}poll_id']), - replyCount: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}reply_count']), - showInChannel: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}show_in_channel']), - shadowed: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}shadowed'])!, - command: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}command']), - localCreatedAt: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}local_created_at']), - remoteCreatedAt: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}remote_created_at']), - localUpdatedAt: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}local_updated_at']), - remoteUpdatedAt: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}remote_updated_at']), - localDeletedAt: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}local_deleted_at']), - remoteDeletedAt: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}remote_deleted_at']), - messageTextUpdatedAt: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, - data['${effectivePrefix}message_text_updated_at']), - userId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}user_id']), - pinned: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}pinned'])!, - pinnedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}pinned_at']), - pinExpires: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}pin_expires']), - pinnedByUserId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}pinned_by_user_id']), - channelCid: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}channel_cid'])!, - i18n: $MessagesTable.$converteri18n.fromSql(attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}i18n'])), - extraData: $MessagesTable.$converterextraDatan.fromSql(attachedDatabase - .typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}extra_data'])), - ); - } - - @override - $MessagesTable createAlias(String alias) { - return $MessagesTable(attachedDatabase, alias); - } - - static TypeConverter, String> $converterattachments = - ListConverter(); - static TypeConverter, String> $convertermentionedUsers = - ListConverter(); - static TypeConverter, String> $converterreactionCounts = - MapConverter(); - static TypeConverter?, String?> $converterreactionCountsn = - NullAwareTypeConverter.wrap($converterreactionCounts); - static TypeConverter, String> $converterreactionScores = - MapConverter(); - static TypeConverter?, String?> $converterreactionScoresn = - NullAwareTypeConverter.wrap($converterreactionScores); - static TypeConverter?, String?> $converteri18n = - NullableMapConverter(); - static TypeConverter, String> $converterextraData = - MapConverter(); - static TypeConverter?, String?> $converterextraDatan = - NullAwareTypeConverter.wrap($converterextraData); -} - -class MessageEntity extends DataClass implements Insertable { - /// The message id - final String id; - - /// The text of this message - final String? messageText; - - /// The list of attachments, either provided by the user - /// or generated from a command or as a result of URL scraping. - final List attachments; - - /// The current state of the message. - final String state; - - /// The message type - final String type; - - /// The list of user mentioned in the message - final List mentionedUsers; - - /// A map describing the count of number of every reaction - final Map? reactionCounts; - - /// A map describing the count of score of every reaction - final Map? reactionScores; - - /// The ID of the parent message, if the message is a thread reply. - final String? parentId; - - /// The ID of the quoted message, if the message is a quoted reply. - final String? quotedMessageId; - - /// The ID of the poll, if the message is a poll. - final String? pollId; - - /// Number of replies for this message. - final int? replyCount; - - /// Check if this message needs to show in the channel. - final bool? showInChannel; - - /// If true the message is shadowed - final bool shadowed; - - /// A used command name. - final String? command; - - /// The DateTime on which the message was created on the client. - final DateTime? localCreatedAt; - - /// The DateTime on which the message was created on the server. - final DateTime? remoteCreatedAt; - - /// The DateTime on which the message was updated on the client. - final DateTime? localUpdatedAt; - - /// The DateTime on which the message was updated on the server. - final DateTime? remoteUpdatedAt; - - /// The DateTime on which the message was deleted on the client. - final DateTime? localDeletedAt; - - /// The DateTime on which the message was deleted on the server. - final DateTime? remoteDeletedAt; - - /// The DateTime at which the message text was edited - final DateTime? messageTextUpdatedAt; - - /// Id of the User who sent the message - final String? userId; - - /// Whether the message is pinned or not - final bool pinned; - - /// The DateTime at which the message was pinned - final DateTime? pinnedAt; - - /// The DateTime on which the message pin expires - final DateTime? pinExpires; - - /// Id of the User who pinned the message - final String? pinnedByUserId; - - /// The channel cid of which this message is part of - final String channelCid; - - /// A Map of [messageText] translations. - final Map? i18n; - - /// Message custom extraData - final Map? extraData; - const MessageEntity( - {required this.id, - this.messageText, - required this.attachments, - required this.state, - required this.type, - required this.mentionedUsers, - this.reactionCounts, - this.reactionScores, - this.parentId, - this.quotedMessageId, - this.pollId, - this.replyCount, - this.showInChannel, - required this.shadowed, - this.command, - this.localCreatedAt, - this.remoteCreatedAt, - this.localUpdatedAt, - this.remoteUpdatedAt, - this.localDeletedAt, - this.remoteDeletedAt, - this.messageTextUpdatedAt, - this.userId, - required this.pinned, - this.pinnedAt, - this.pinExpires, - this.pinnedByUserId, - required this.channelCid, - this.i18n, - this.extraData}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['id'] = Variable(id); - if (!nullToAbsent || messageText != null) { - map['message_text'] = Variable(messageText); - } - { - map['attachments'] = Variable( - $MessagesTable.$converterattachments.toSql(attachments)); - } - map['state'] = Variable(state); - map['type'] = Variable(type); - { - map['mentioned_users'] = Variable( - $MessagesTable.$convertermentionedUsers.toSql(mentionedUsers)); - } - if (!nullToAbsent || reactionCounts != null) { - map['reaction_counts'] = Variable( - $MessagesTable.$converterreactionCountsn.toSql(reactionCounts)); - } - if (!nullToAbsent || reactionScores != null) { - map['reaction_scores'] = Variable( - $MessagesTable.$converterreactionScoresn.toSql(reactionScores)); - } - if (!nullToAbsent || parentId != null) { - map['parent_id'] = Variable(parentId); - } - if (!nullToAbsent || quotedMessageId != null) { - map['quoted_message_id'] = Variable(quotedMessageId); - } - if (!nullToAbsent || pollId != null) { - map['poll_id'] = Variable(pollId); - } - if (!nullToAbsent || replyCount != null) { - map['reply_count'] = Variable(replyCount); - } - if (!nullToAbsent || showInChannel != null) { - map['show_in_channel'] = Variable(showInChannel); - } - map['shadowed'] = Variable(shadowed); - if (!nullToAbsent || command != null) { - map['command'] = Variable(command); - } - if (!nullToAbsent || localCreatedAt != null) { - map['local_created_at'] = Variable(localCreatedAt); - } - if (!nullToAbsent || remoteCreatedAt != null) { - map['remote_created_at'] = Variable(remoteCreatedAt); - } - if (!nullToAbsent || localUpdatedAt != null) { - map['local_updated_at'] = Variable(localUpdatedAt); - } - if (!nullToAbsent || remoteUpdatedAt != null) { - map['remote_updated_at'] = Variable(remoteUpdatedAt); - } - if (!nullToAbsent || localDeletedAt != null) { - map['local_deleted_at'] = Variable(localDeletedAt); - } - if (!nullToAbsent || remoteDeletedAt != null) { - map['remote_deleted_at'] = Variable(remoteDeletedAt); - } - if (!nullToAbsent || messageTextUpdatedAt != null) { - map['message_text_updated_at'] = Variable(messageTextUpdatedAt); - } - if (!nullToAbsent || userId != null) { - map['user_id'] = Variable(userId); - } - map['pinned'] = Variable(pinned); - if (!nullToAbsent || pinnedAt != null) { - map['pinned_at'] = Variable(pinnedAt); - } - if (!nullToAbsent || pinExpires != null) { - map['pin_expires'] = Variable(pinExpires); - } - if (!nullToAbsent || pinnedByUserId != null) { - map['pinned_by_user_id'] = Variable(pinnedByUserId); - } - map['channel_cid'] = Variable(channelCid); - if (!nullToAbsent || i18n != null) { - map['i18n'] = Variable($MessagesTable.$converteri18n.toSql(i18n)); - } - if (!nullToAbsent || extraData != null) { - map['extra_data'] = Variable( - $MessagesTable.$converterextraDatan.toSql(extraData)); - } - return map; - } - - factory MessageEntity.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return MessageEntity( - id: serializer.fromJson(json['id']), - messageText: serializer.fromJson(json['messageText']), - attachments: serializer.fromJson>(json['attachments']), - state: serializer.fromJson(json['state']), - type: serializer.fromJson(json['type']), - mentionedUsers: serializer.fromJson>(json['mentionedUsers']), - reactionCounts: - serializer.fromJson?>(json['reactionCounts']), - reactionScores: - serializer.fromJson?>(json['reactionScores']), - parentId: serializer.fromJson(json['parentId']), - quotedMessageId: serializer.fromJson(json['quotedMessageId']), - pollId: serializer.fromJson(json['pollId']), - replyCount: serializer.fromJson(json['replyCount']), - showInChannel: serializer.fromJson(json['showInChannel']), - shadowed: serializer.fromJson(json['shadowed']), - command: serializer.fromJson(json['command']), - localCreatedAt: serializer.fromJson(json['localCreatedAt']), - remoteCreatedAt: serializer.fromJson(json['remoteCreatedAt']), - localUpdatedAt: serializer.fromJson(json['localUpdatedAt']), - remoteUpdatedAt: serializer.fromJson(json['remoteUpdatedAt']), - localDeletedAt: serializer.fromJson(json['localDeletedAt']), - remoteDeletedAt: serializer.fromJson(json['remoteDeletedAt']), - messageTextUpdatedAt: - serializer.fromJson(json['messageTextUpdatedAt']), - userId: serializer.fromJson(json['userId']), - pinned: serializer.fromJson(json['pinned']), - pinnedAt: serializer.fromJson(json['pinnedAt']), - pinExpires: serializer.fromJson(json['pinExpires']), - pinnedByUserId: serializer.fromJson(json['pinnedByUserId']), - channelCid: serializer.fromJson(json['channelCid']), - i18n: serializer.fromJson?>(json['i18n']), - extraData: serializer.fromJson?>(json['extraData']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'id': serializer.toJson(id), - 'messageText': serializer.toJson(messageText), - 'attachments': serializer.toJson>(attachments), - 'state': serializer.toJson(state), - 'type': serializer.toJson(type), - 'mentionedUsers': serializer.toJson>(mentionedUsers), - 'reactionCounts': serializer.toJson?>(reactionCounts), - 'reactionScores': serializer.toJson?>(reactionScores), - 'parentId': serializer.toJson(parentId), - 'quotedMessageId': serializer.toJson(quotedMessageId), - 'pollId': serializer.toJson(pollId), - 'replyCount': serializer.toJson(replyCount), - 'showInChannel': serializer.toJson(showInChannel), - 'shadowed': serializer.toJson(shadowed), - 'command': serializer.toJson(command), - 'localCreatedAt': serializer.toJson(localCreatedAt), - 'remoteCreatedAt': serializer.toJson(remoteCreatedAt), - 'localUpdatedAt': serializer.toJson(localUpdatedAt), - 'remoteUpdatedAt': serializer.toJson(remoteUpdatedAt), - 'localDeletedAt': serializer.toJson(localDeletedAt), - 'remoteDeletedAt': serializer.toJson(remoteDeletedAt), - 'messageTextUpdatedAt': - serializer.toJson(messageTextUpdatedAt), - 'userId': serializer.toJson(userId), - 'pinned': serializer.toJson(pinned), - 'pinnedAt': serializer.toJson(pinnedAt), - 'pinExpires': serializer.toJson(pinExpires), - 'pinnedByUserId': serializer.toJson(pinnedByUserId), - 'channelCid': serializer.toJson(channelCid), - 'i18n': serializer.toJson?>(i18n), - 'extraData': serializer.toJson?>(extraData), - }; - } - - MessageEntity copyWith( - {String? id, - Value messageText = const Value.absent(), - List? attachments, - String? state, - String? type, - List? mentionedUsers, - Value?> reactionCounts = const Value.absent(), - Value?> reactionScores = const Value.absent(), - Value parentId = const Value.absent(), - Value quotedMessageId = const Value.absent(), - Value pollId = const Value.absent(), - Value replyCount = const Value.absent(), - Value showInChannel = const Value.absent(), - bool? shadowed, - Value command = const Value.absent(), - Value localCreatedAt = const Value.absent(), - Value remoteCreatedAt = const Value.absent(), - Value localUpdatedAt = const Value.absent(), - Value remoteUpdatedAt = const Value.absent(), - Value localDeletedAt = const Value.absent(), - Value remoteDeletedAt = const Value.absent(), - Value messageTextUpdatedAt = const Value.absent(), - Value userId = const Value.absent(), - bool? pinned, - Value pinnedAt = const Value.absent(), - Value pinExpires = const Value.absent(), - Value pinnedByUserId = const Value.absent(), - String? channelCid, - Value?> i18n = const Value.absent(), - Value?> extraData = const Value.absent()}) => - MessageEntity( - id: id ?? this.id, - messageText: messageText.present ? messageText.value : this.messageText, - attachments: attachments ?? this.attachments, - state: state ?? this.state, - type: type ?? this.type, - mentionedUsers: mentionedUsers ?? this.mentionedUsers, - reactionCounts: - reactionCounts.present ? reactionCounts.value : this.reactionCounts, - reactionScores: - reactionScores.present ? reactionScores.value : this.reactionScores, - parentId: parentId.present ? parentId.value : this.parentId, - quotedMessageId: quotedMessageId.present - ? quotedMessageId.value - : this.quotedMessageId, - pollId: pollId.present ? pollId.value : this.pollId, - replyCount: replyCount.present ? replyCount.value : this.replyCount, - showInChannel: - showInChannel.present ? showInChannel.value : this.showInChannel, - shadowed: shadowed ?? this.shadowed, - command: command.present ? command.value : this.command, - localCreatedAt: - localCreatedAt.present ? localCreatedAt.value : this.localCreatedAt, - remoteCreatedAt: remoteCreatedAt.present - ? remoteCreatedAt.value - : this.remoteCreatedAt, - localUpdatedAt: - localUpdatedAt.present ? localUpdatedAt.value : this.localUpdatedAt, - remoteUpdatedAt: remoteUpdatedAt.present - ? remoteUpdatedAt.value - : this.remoteUpdatedAt, - localDeletedAt: - localDeletedAt.present ? localDeletedAt.value : this.localDeletedAt, - remoteDeletedAt: remoteDeletedAt.present - ? remoteDeletedAt.value - : this.remoteDeletedAt, - messageTextUpdatedAt: messageTextUpdatedAt.present - ? messageTextUpdatedAt.value - : this.messageTextUpdatedAt, - userId: userId.present ? userId.value : this.userId, - pinned: pinned ?? this.pinned, - pinnedAt: pinnedAt.present ? pinnedAt.value : this.pinnedAt, - pinExpires: pinExpires.present ? pinExpires.value : this.pinExpires, - pinnedByUserId: - pinnedByUserId.present ? pinnedByUserId.value : this.pinnedByUserId, - channelCid: channelCid ?? this.channelCid, - i18n: i18n.present ? i18n.value : this.i18n, - extraData: extraData.present ? extraData.value : this.extraData, - ); - MessageEntity copyWithCompanion(MessagesCompanion data) { - return MessageEntity( - id: data.id.present ? data.id.value : this.id, - messageText: - data.messageText.present ? data.messageText.value : this.messageText, - attachments: - data.attachments.present ? data.attachments.value : this.attachments, - state: data.state.present ? data.state.value : this.state, - type: data.type.present ? data.type.value : this.type, - mentionedUsers: data.mentionedUsers.present - ? data.mentionedUsers.value - : this.mentionedUsers, - reactionCounts: data.reactionCounts.present - ? data.reactionCounts.value - : this.reactionCounts, - reactionScores: data.reactionScores.present - ? data.reactionScores.value - : this.reactionScores, - parentId: data.parentId.present ? data.parentId.value : this.parentId, - quotedMessageId: data.quotedMessageId.present - ? data.quotedMessageId.value - : this.quotedMessageId, - pollId: data.pollId.present ? data.pollId.value : this.pollId, - replyCount: - data.replyCount.present ? data.replyCount.value : this.replyCount, - showInChannel: data.showInChannel.present - ? data.showInChannel.value - : this.showInChannel, - shadowed: data.shadowed.present ? data.shadowed.value : this.shadowed, - command: data.command.present ? data.command.value : this.command, - localCreatedAt: data.localCreatedAt.present - ? data.localCreatedAt.value - : this.localCreatedAt, - remoteCreatedAt: data.remoteCreatedAt.present - ? data.remoteCreatedAt.value - : this.remoteCreatedAt, - localUpdatedAt: data.localUpdatedAt.present - ? data.localUpdatedAt.value - : this.localUpdatedAt, - remoteUpdatedAt: data.remoteUpdatedAt.present - ? data.remoteUpdatedAt.value - : this.remoteUpdatedAt, - localDeletedAt: data.localDeletedAt.present - ? data.localDeletedAt.value - : this.localDeletedAt, - remoteDeletedAt: data.remoteDeletedAt.present - ? data.remoteDeletedAt.value - : this.remoteDeletedAt, - messageTextUpdatedAt: data.messageTextUpdatedAt.present - ? data.messageTextUpdatedAt.value - : this.messageTextUpdatedAt, - userId: data.userId.present ? data.userId.value : this.userId, - pinned: data.pinned.present ? data.pinned.value : this.pinned, - pinnedAt: data.pinnedAt.present ? data.pinnedAt.value : this.pinnedAt, - pinExpires: - data.pinExpires.present ? data.pinExpires.value : this.pinExpires, - pinnedByUserId: data.pinnedByUserId.present - ? data.pinnedByUserId.value - : this.pinnedByUserId, - channelCid: - data.channelCid.present ? data.channelCid.value : this.channelCid, - i18n: data.i18n.present ? data.i18n.value : this.i18n, - extraData: data.extraData.present ? data.extraData.value : this.extraData, - ); - } - - @override - String toString() { - return (StringBuffer('MessageEntity(') - ..write('id: $id, ') - ..write('messageText: $messageText, ') - ..write('attachments: $attachments, ') - ..write('state: $state, ') - ..write('type: $type, ') - ..write('mentionedUsers: $mentionedUsers, ') - ..write('reactionCounts: $reactionCounts, ') - ..write('reactionScores: $reactionScores, ') - ..write('parentId: $parentId, ') - ..write('quotedMessageId: $quotedMessageId, ') - ..write('pollId: $pollId, ') - ..write('replyCount: $replyCount, ') - ..write('showInChannel: $showInChannel, ') - ..write('shadowed: $shadowed, ') - ..write('command: $command, ') - ..write('localCreatedAt: $localCreatedAt, ') - ..write('remoteCreatedAt: $remoteCreatedAt, ') - ..write('localUpdatedAt: $localUpdatedAt, ') - ..write('remoteUpdatedAt: $remoteUpdatedAt, ') - ..write('localDeletedAt: $localDeletedAt, ') - ..write('remoteDeletedAt: $remoteDeletedAt, ') - ..write('messageTextUpdatedAt: $messageTextUpdatedAt, ') - ..write('userId: $userId, ') - ..write('pinned: $pinned, ') - ..write('pinnedAt: $pinnedAt, ') - ..write('pinExpires: $pinExpires, ') - ..write('pinnedByUserId: $pinnedByUserId, ') - ..write('channelCid: $channelCid, ') - ..write('i18n: $i18n, ') - ..write('extraData: $extraData') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hashAll([ - id, - messageText, - attachments, - state, - type, - mentionedUsers, - reactionCounts, - reactionScores, - parentId, - quotedMessageId, - pollId, - replyCount, - showInChannel, - shadowed, - command, - localCreatedAt, - remoteCreatedAt, - localUpdatedAt, - remoteUpdatedAt, - localDeletedAt, - remoteDeletedAt, - messageTextUpdatedAt, - userId, - pinned, - pinnedAt, - pinExpires, - pinnedByUserId, - channelCid, - i18n, - extraData - ]); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is MessageEntity && - other.id == this.id && - other.messageText == this.messageText && - other.attachments == this.attachments && - other.state == this.state && - other.type == this.type && - other.mentionedUsers == this.mentionedUsers && - other.reactionCounts == this.reactionCounts && - other.reactionScores == this.reactionScores && - other.parentId == this.parentId && - other.quotedMessageId == this.quotedMessageId && - other.pollId == this.pollId && - other.replyCount == this.replyCount && - other.showInChannel == this.showInChannel && - other.shadowed == this.shadowed && - other.command == this.command && - other.localCreatedAt == this.localCreatedAt && - other.remoteCreatedAt == this.remoteCreatedAt && - other.localUpdatedAt == this.localUpdatedAt && - other.remoteUpdatedAt == this.remoteUpdatedAt && - other.localDeletedAt == this.localDeletedAt && - other.remoteDeletedAt == this.remoteDeletedAt && - other.messageTextUpdatedAt == this.messageTextUpdatedAt && - other.userId == this.userId && - other.pinned == this.pinned && - other.pinnedAt == this.pinnedAt && - other.pinExpires == this.pinExpires && - other.pinnedByUserId == this.pinnedByUserId && - other.channelCid == this.channelCid && - other.i18n == this.i18n && - other.extraData == this.extraData); -} - -class MessagesCompanion extends UpdateCompanion { - final Value id; - final Value messageText; - final Value> attachments; - final Value state; - final Value type; - final Value> mentionedUsers; - final Value?> reactionCounts; - final Value?> reactionScores; - final Value parentId; - final Value quotedMessageId; - final Value pollId; - final Value replyCount; - final Value showInChannel; - final Value shadowed; - final Value command; - final Value localCreatedAt; - final Value remoteCreatedAt; - final Value localUpdatedAt; - final Value remoteUpdatedAt; - final Value localDeletedAt; - final Value remoteDeletedAt; - final Value messageTextUpdatedAt; - final Value userId; - final Value pinned; - final Value pinnedAt; - final Value pinExpires; - final Value pinnedByUserId; - final Value channelCid; - final Value?> i18n; - final Value?> extraData; - final Value rowid; - const MessagesCompanion({ - this.id = const Value.absent(), - this.messageText = const Value.absent(), - this.attachments = const Value.absent(), - this.state = const Value.absent(), - this.type = const Value.absent(), - this.mentionedUsers = const Value.absent(), - this.reactionCounts = const Value.absent(), - this.reactionScores = const Value.absent(), - this.parentId = const Value.absent(), - this.quotedMessageId = const Value.absent(), - this.pollId = const Value.absent(), - this.replyCount = const Value.absent(), - this.showInChannel = const Value.absent(), - this.shadowed = const Value.absent(), - this.command = const Value.absent(), - this.localCreatedAt = const Value.absent(), - this.remoteCreatedAt = const Value.absent(), - this.localUpdatedAt = const Value.absent(), - this.remoteUpdatedAt = const Value.absent(), - this.localDeletedAt = const Value.absent(), - this.remoteDeletedAt = const Value.absent(), - this.messageTextUpdatedAt = const Value.absent(), - this.userId = const Value.absent(), - this.pinned = const Value.absent(), - this.pinnedAt = const Value.absent(), - this.pinExpires = const Value.absent(), - this.pinnedByUserId = const Value.absent(), - this.channelCid = const Value.absent(), - this.i18n = const Value.absent(), - this.extraData = const Value.absent(), - this.rowid = const Value.absent(), - }); - MessagesCompanion.insert({ - required String id, - this.messageText = const Value.absent(), - required List attachments, - required String state, - this.type = const Value.absent(), - required List mentionedUsers, - this.reactionCounts = const Value.absent(), - this.reactionScores = const Value.absent(), - this.parentId = const Value.absent(), - this.quotedMessageId = const Value.absent(), - this.pollId = const Value.absent(), - this.replyCount = const Value.absent(), - this.showInChannel = const Value.absent(), - this.shadowed = const Value.absent(), - this.command = const Value.absent(), - this.localCreatedAt = const Value.absent(), - this.remoteCreatedAt = const Value.absent(), - this.localUpdatedAt = const Value.absent(), - this.remoteUpdatedAt = const Value.absent(), - this.localDeletedAt = const Value.absent(), - this.remoteDeletedAt = const Value.absent(), - this.messageTextUpdatedAt = const Value.absent(), - this.userId = const Value.absent(), - this.pinned = const Value.absent(), - this.pinnedAt = const Value.absent(), - this.pinExpires = const Value.absent(), - this.pinnedByUserId = const Value.absent(), - required String channelCid, - this.i18n = const Value.absent(), - this.extraData = const Value.absent(), - this.rowid = const Value.absent(), - }) : id = Value(id), - attachments = Value(attachments), - state = Value(state), - mentionedUsers = Value(mentionedUsers), - channelCid = Value(channelCid); - static Insertable custom({ - Expression? id, - Expression? messageText, - Expression? attachments, - Expression? state, - Expression? type, - Expression? mentionedUsers, - Expression? reactionCounts, - Expression? reactionScores, - Expression? parentId, - Expression? quotedMessageId, - Expression? pollId, - Expression? replyCount, - Expression? showInChannel, - Expression? shadowed, - Expression? command, - Expression? localCreatedAt, - Expression? remoteCreatedAt, - Expression? localUpdatedAt, - Expression? remoteUpdatedAt, - Expression? localDeletedAt, - Expression? remoteDeletedAt, - Expression? messageTextUpdatedAt, - Expression? userId, - Expression? pinned, - Expression? pinnedAt, - Expression? pinExpires, - Expression? pinnedByUserId, - Expression? channelCid, - Expression? i18n, - Expression? extraData, - Expression? rowid, - }) { - return RawValuesInsertable({ - if (id != null) 'id': id, - if (messageText != null) 'message_text': messageText, - if (attachments != null) 'attachments': attachments, - if (state != null) 'state': state, - if (type != null) 'type': type, - if (mentionedUsers != null) 'mentioned_users': mentionedUsers, - if (reactionCounts != null) 'reaction_counts': reactionCounts, - if (reactionScores != null) 'reaction_scores': reactionScores, - if (parentId != null) 'parent_id': parentId, - if (quotedMessageId != null) 'quoted_message_id': quotedMessageId, - if (pollId != null) 'poll_id': pollId, - if (replyCount != null) 'reply_count': replyCount, - if (showInChannel != null) 'show_in_channel': showInChannel, - if (shadowed != null) 'shadowed': shadowed, - if (command != null) 'command': command, - if (localCreatedAt != null) 'local_created_at': localCreatedAt, - if (remoteCreatedAt != null) 'remote_created_at': remoteCreatedAt, - if (localUpdatedAt != null) 'local_updated_at': localUpdatedAt, - if (remoteUpdatedAt != null) 'remote_updated_at': remoteUpdatedAt, - if (localDeletedAt != null) 'local_deleted_at': localDeletedAt, - if (remoteDeletedAt != null) 'remote_deleted_at': remoteDeletedAt, - if (messageTextUpdatedAt != null) - 'message_text_updated_at': messageTextUpdatedAt, - if (userId != null) 'user_id': userId, - if (pinned != null) 'pinned': pinned, - if (pinnedAt != null) 'pinned_at': pinnedAt, - if (pinExpires != null) 'pin_expires': pinExpires, - if (pinnedByUserId != null) 'pinned_by_user_id': pinnedByUserId, - if (channelCid != null) 'channel_cid': channelCid, - if (i18n != null) 'i18n': i18n, - if (extraData != null) 'extra_data': extraData, - if (rowid != null) 'rowid': rowid, - }); - } - - MessagesCompanion copyWith( - {Value? id, - Value? messageText, - Value>? attachments, - Value? state, - Value? type, - Value>? mentionedUsers, - Value?>? reactionCounts, - Value?>? reactionScores, - Value? parentId, - Value? quotedMessageId, - Value? pollId, - Value? replyCount, - Value? showInChannel, - Value? shadowed, - Value? command, - Value? localCreatedAt, - Value? remoteCreatedAt, - Value? localUpdatedAt, - Value? remoteUpdatedAt, - Value? localDeletedAt, - Value? remoteDeletedAt, - Value? messageTextUpdatedAt, - Value? userId, - Value? pinned, - Value? pinnedAt, - Value? pinExpires, - Value? pinnedByUserId, - Value? channelCid, - Value?>? i18n, - Value?>? extraData, - Value? rowid}) { - return MessagesCompanion( - id: id ?? this.id, - messageText: messageText ?? this.messageText, - attachments: attachments ?? this.attachments, - state: state ?? this.state, - type: type ?? this.type, - mentionedUsers: mentionedUsers ?? this.mentionedUsers, - reactionCounts: reactionCounts ?? this.reactionCounts, - reactionScores: reactionScores ?? this.reactionScores, - parentId: parentId ?? this.parentId, - quotedMessageId: quotedMessageId ?? this.quotedMessageId, - pollId: pollId ?? this.pollId, - replyCount: replyCount ?? this.replyCount, - showInChannel: showInChannel ?? this.showInChannel, - shadowed: shadowed ?? this.shadowed, - command: command ?? this.command, - localCreatedAt: localCreatedAt ?? this.localCreatedAt, - remoteCreatedAt: remoteCreatedAt ?? this.remoteCreatedAt, - localUpdatedAt: localUpdatedAt ?? this.localUpdatedAt, - remoteUpdatedAt: remoteUpdatedAt ?? this.remoteUpdatedAt, - localDeletedAt: localDeletedAt ?? this.localDeletedAt, - remoteDeletedAt: remoteDeletedAt ?? this.remoteDeletedAt, - messageTextUpdatedAt: messageTextUpdatedAt ?? this.messageTextUpdatedAt, - userId: userId ?? this.userId, - pinned: pinned ?? this.pinned, - pinnedAt: pinnedAt ?? this.pinnedAt, - pinExpires: pinExpires ?? this.pinExpires, - pinnedByUserId: pinnedByUserId ?? this.pinnedByUserId, - channelCid: channelCid ?? this.channelCid, - i18n: i18n ?? this.i18n, - extraData: extraData ?? this.extraData, - rowid: rowid ?? this.rowid, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (id.present) { - map['id'] = Variable(id.value); - } - if (messageText.present) { - map['message_text'] = Variable(messageText.value); - } - if (attachments.present) { - map['attachments'] = Variable( - $MessagesTable.$converterattachments.toSql(attachments.value)); - } - if (state.present) { - map['state'] = Variable(state.value); - } - if (type.present) { - map['type'] = Variable(type.value); - } - if (mentionedUsers.present) { - map['mentioned_users'] = Variable( - $MessagesTable.$convertermentionedUsers.toSql(mentionedUsers.value)); - } - if (reactionCounts.present) { - map['reaction_counts'] = Variable( - $MessagesTable.$converterreactionCountsn.toSql(reactionCounts.value)); - } - if (reactionScores.present) { - map['reaction_scores'] = Variable( - $MessagesTable.$converterreactionScoresn.toSql(reactionScores.value)); - } - if (parentId.present) { - map['parent_id'] = Variable(parentId.value); - } - if (quotedMessageId.present) { - map['quoted_message_id'] = Variable(quotedMessageId.value); - } - if (pollId.present) { - map['poll_id'] = Variable(pollId.value); - } - if (replyCount.present) { - map['reply_count'] = Variable(replyCount.value); - } - if (showInChannel.present) { - map['show_in_channel'] = Variable(showInChannel.value); - } - if (shadowed.present) { - map['shadowed'] = Variable(shadowed.value); - } - if (command.present) { - map['command'] = Variable(command.value); - } - if (localCreatedAt.present) { - map['local_created_at'] = Variable(localCreatedAt.value); - } - if (remoteCreatedAt.present) { - map['remote_created_at'] = Variable(remoteCreatedAt.value); - } - if (localUpdatedAt.present) { - map['local_updated_at'] = Variable(localUpdatedAt.value); - } - if (remoteUpdatedAt.present) { - map['remote_updated_at'] = Variable(remoteUpdatedAt.value); - } - if (localDeletedAt.present) { - map['local_deleted_at'] = Variable(localDeletedAt.value); - } - if (remoteDeletedAt.present) { - map['remote_deleted_at'] = Variable(remoteDeletedAt.value); - } - if (messageTextUpdatedAt.present) { - map['message_text_updated_at'] = - Variable(messageTextUpdatedAt.value); - } - if (userId.present) { - map['user_id'] = Variable(userId.value); - } - if (pinned.present) { - map['pinned'] = Variable(pinned.value); - } - if (pinnedAt.present) { - map['pinned_at'] = Variable(pinnedAt.value); - } - if (pinExpires.present) { - map['pin_expires'] = Variable(pinExpires.value); - } - if (pinnedByUserId.present) { - map['pinned_by_user_id'] = Variable(pinnedByUserId.value); - } - if (channelCid.present) { - map['channel_cid'] = Variable(channelCid.value); - } - if (i18n.present) { - map['i18n'] = - Variable($MessagesTable.$converteri18n.toSql(i18n.value)); - } - if (extraData.present) { - map['extra_data'] = Variable( - $MessagesTable.$converterextraDatan.toSql(extraData.value)); - } - if (rowid.present) { - map['rowid'] = Variable(rowid.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('MessagesCompanion(') - ..write('id: $id, ') - ..write('messageText: $messageText, ') - ..write('attachments: $attachments, ') - ..write('state: $state, ') - ..write('type: $type, ') - ..write('mentionedUsers: $mentionedUsers, ') - ..write('reactionCounts: $reactionCounts, ') - ..write('reactionScores: $reactionScores, ') - ..write('parentId: $parentId, ') - ..write('quotedMessageId: $quotedMessageId, ') - ..write('pollId: $pollId, ') - ..write('replyCount: $replyCount, ') - ..write('showInChannel: $showInChannel, ') - ..write('shadowed: $shadowed, ') - ..write('command: $command, ') - ..write('localCreatedAt: $localCreatedAt, ') - ..write('remoteCreatedAt: $remoteCreatedAt, ') - ..write('localUpdatedAt: $localUpdatedAt, ') - ..write('remoteUpdatedAt: $remoteUpdatedAt, ') - ..write('localDeletedAt: $localDeletedAt, ') - ..write('remoteDeletedAt: $remoteDeletedAt, ') - ..write('messageTextUpdatedAt: $messageTextUpdatedAt, ') - ..write('userId: $userId, ') - ..write('pinned: $pinned, ') - ..write('pinnedAt: $pinnedAt, ') - ..write('pinExpires: $pinExpires, ') - ..write('pinnedByUserId: $pinnedByUserId, ') - ..write('channelCid: $channelCid, ') - ..write('i18n: $i18n, ') - ..write('extraData: $extraData, ') - ..write('rowid: $rowid') - ..write(')')) - .toString(); - } -} - -class $PinnedMessagesTable extends PinnedMessages - with TableInfo<$PinnedMessagesTable, PinnedMessageEntity> { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - $PinnedMessagesTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _idMeta = const VerificationMeta('id'); - @override - late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - static const VerificationMeta _messageTextMeta = - const VerificationMeta('messageText'); - @override - late final GeneratedColumn messageText = GeneratedColumn( - 'message_text', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _attachmentsMeta = - const VerificationMeta('attachments'); - @override - late final GeneratedColumnWithTypeConverter, String> - attachments = GeneratedColumn('attachments', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true) - .withConverter>( - $PinnedMessagesTable.$converterattachments); - static const VerificationMeta _stateMeta = const VerificationMeta('state'); - @override - late final GeneratedColumn state = GeneratedColumn( - 'state', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - static const VerificationMeta _typeMeta = const VerificationMeta('type'); - @override - late final GeneratedColumn type = GeneratedColumn( - 'type', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultValue: const Constant('regular')); - static const VerificationMeta _mentionedUsersMeta = - const VerificationMeta('mentionedUsers'); - @override - late final GeneratedColumnWithTypeConverter, String> - mentionedUsers = GeneratedColumn( - 'mentioned_users', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true) - .withConverter>( - $PinnedMessagesTable.$convertermentionedUsers); - static const VerificationMeta _reactionCountsMeta = - const VerificationMeta('reactionCounts'); - @override - late final GeneratedColumnWithTypeConverter?, String> - reactionCounts = GeneratedColumn( - 'reaction_counts', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false) - .withConverter?>( - $PinnedMessagesTable.$converterreactionCountsn); - static const VerificationMeta _reactionScoresMeta = - const VerificationMeta('reactionScores'); - @override - late final GeneratedColumnWithTypeConverter?, String> - reactionScores = GeneratedColumn( - 'reaction_scores', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false) - .withConverter?>( - $PinnedMessagesTable.$converterreactionScoresn); - static const VerificationMeta _parentIdMeta = - const VerificationMeta('parentId'); - @override - late final GeneratedColumn parentId = GeneratedColumn( - 'parent_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _quotedMessageIdMeta = - const VerificationMeta('quotedMessageId'); - @override - late final GeneratedColumn quotedMessageId = GeneratedColumn( - 'quoted_message_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _pollIdMeta = const VerificationMeta('pollId'); - @override - late final GeneratedColumn pollId = GeneratedColumn( - 'poll_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _replyCountMeta = - const VerificationMeta('replyCount'); - @override - late final GeneratedColumn replyCount = GeneratedColumn( - 'reply_count', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - static const VerificationMeta _showInChannelMeta = - const VerificationMeta('showInChannel'); - @override - late final GeneratedColumn showInChannel = GeneratedColumn( - 'show_in_channel', aliasedName, true, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("show_in_channel" IN (0, 1))')); - static const VerificationMeta _shadowedMeta = - const VerificationMeta('shadowed'); - @override - late final GeneratedColumn shadowed = GeneratedColumn( - 'shadowed', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("shadowed" IN (0, 1))'), - defaultValue: const Constant(false)); - static const VerificationMeta _commandMeta = - const VerificationMeta('command'); - @override - late final GeneratedColumn command = GeneratedColumn( - 'command', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _localCreatedAtMeta = - const VerificationMeta('localCreatedAt'); - @override - late final GeneratedColumn localCreatedAt = - GeneratedColumn('local_created_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _remoteCreatedAtMeta = - const VerificationMeta('remoteCreatedAt'); - @override - late final GeneratedColumn remoteCreatedAt = - GeneratedColumn('remote_created_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _localUpdatedAtMeta = - const VerificationMeta('localUpdatedAt'); - @override - late final GeneratedColumn localUpdatedAt = - GeneratedColumn('local_updated_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _remoteUpdatedAtMeta = - const VerificationMeta('remoteUpdatedAt'); - @override - late final GeneratedColumn remoteUpdatedAt = - GeneratedColumn('remote_updated_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _localDeletedAtMeta = - const VerificationMeta('localDeletedAt'); - @override - late final GeneratedColumn localDeletedAt = - GeneratedColumn('local_deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _remoteDeletedAtMeta = - const VerificationMeta('remoteDeletedAt'); - @override - late final GeneratedColumn remoteDeletedAt = - GeneratedColumn('remote_deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _messageTextUpdatedAtMeta = - const VerificationMeta('messageTextUpdatedAt'); - @override - late final GeneratedColumn messageTextUpdatedAt = - GeneratedColumn('message_text_updated_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _userIdMeta = const VerificationMeta('userId'); - @override - late final GeneratedColumn userId = GeneratedColumn( - 'user_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _pinnedMeta = const VerificationMeta('pinned'); - @override - late final GeneratedColumn pinned = GeneratedColumn( - 'pinned', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("pinned" IN (0, 1))'), - defaultValue: const Constant(false)); - static const VerificationMeta _pinnedAtMeta = - const VerificationMeta('pinnedAt'); - @override - late final GeneratedColumn pinnedAt = GeneratedColumn( - 'pinned_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _pinExpiresMeta = - const VerificationMeta('pinExpires'); - @override - late final GeneratedColumn pinExpires = GeneratedColumn( - 'pin_expires', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _pinnedByUserIdMeta = - const VerificationMeta('pinnedByUserId'); - @override - late final GeneratedColumn pinnedByUserId = GeneratedColumn( - 'pinned_by_user_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _channelCidMeta = - const VerificationMeta('channelCid'); - @override - late final GeneratedColumn channelCid = GeneratedColumn( - 'channel_cid', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - static const VerificationMeta _i18nMeta = const VerificationMeta('i18n'); - @override - late final GeneratedColumnWithTypeConverter?, String> - i18n = GeneratedColumn('i18n', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false) - .withConverter?>( - $PinnedMessagesTable.$converteri18n); - static const VerificationMeta _extraDataMeta = - const VerificationMeta('extraData'); - @override - late final GeneratedColumnWithTypeConverter?, String> - extraData = GeneratedColumn('extra_data', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false) - .withConverter?>( - $PinnedMessagesTable.$converterextraDatan); - @override - List get $columns => [ - id, - messageText, - attachments, - state, - type, - mentionedUsers, - reactionCounts, - reactionScores, - parentId, - quotedMessageId, - pollId, - replyCount, - showInChannel, - shadowed, - command, - localCreatedAt, - remoteCreatedAt, - localUpdatedAt, - remoteUpdatedAt, - localDeletedAt, - remoteDeletedAt, - messageTextUpdatedAt, - userId, - pinned, - pinnedAt, - pinExpires, - pinnedByUserId, - channelCid, - i18n, - extraData - ]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'pinned_messages'; - @override - VerificationContext validateIntegrity( - Insertable instance, - {bool isInserting = false}) { - final context = VerificationContext(); - final data = instance.toColumns(true); - if (data.containsKey('id')) { - context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); - } else if (isInserting) { - context.missing(_idMeta); - } - if (data.containsKey('message_text')) { - context.handle( - _messageTextMeta, - messageText.isAcceptableOrUnknown( - data['message_text']!, _messageTextMeta)); - } - context.handle(_attachmentsMeta, const VerificationResult.success()); - if (data.containsKey('state')) { - context.handle( - _stateMeta, state.isAcceptableOrUnknown(data['state']!, _stateMeta)); - } else if (isInserting) { - context.missing(_stateMeta); - } - if (data.containsKey('type')) { - context.handle( - _typeMeta, type.isAcceptableOrUnknown(data['type']!, _typeMeta)); - } - context.handle(_mentionedUsersMeta, const VerificationResult.success()); - context.handle(_reactionCountsMeta, const VerificationResult.success()); - context.handle(_reactionScoresMeta, const VerificationResult.success()); - if (data.containsKey('parent_id')) { - context.handle(_parentIdMeta, - parentId.isAcceptableOrUnknown(data['parent_id']!, _parentIdMeta)); - } - if (data.containsKey('quoted_message_id')) { - context.handle( - _quotedMessageIdMeta, - quotedMessageId.isAcceptableOrUnknown( - data['quoted_message_id']!, _quotedMessageIdMeta)); - } - if (data.containsKey('poll_id')) { - context.handle(_pollIdMeta, - pollId.isAcceptableOrUnknown(data['poll_id']!, _pollIdMeta)); - } - if (data.containsKey('reply_count')) { - context.handle( - _replyCountMeta, - replyCount.isAcceptableOrUnknown( - data['reply_count']!, _replyCountMeta)); - } - if (data.containsKey('show_in_channel')) { - context.handle( - _showInChannelMeta, - showInChannel.isAcceptableOrUnknown( - data['show_in_channel']!, _showInChannelMeta)); - } - if (data.containsKey('shadowed')) { - context.handle(_shadowedMeta, - shadowed.isAcceptableOrUnknown(data['shadowed']!, _shadowedMeta)); - } - if (data.containsKey('command')) { - context.handle(_commandMeta, - command.isAcceptableOrUnknown(data['command']!, _commandMeta)); - } - if (data.containsKey('local_created_at')) { - context.handle( - _localCreatedAtMeta, - localCreatedAt.isAcceptableOrUnknown( - data['local_created_at']!, _localCreatedAtMeta)); - } - if (data.containsKey('remote_created_at')) { - context.handle( - _remoteCreatedAtMeta, - remoteCreatedAt.isAcceptableOrUnknown( - data['remote_created_at']!, _remoteCreatedAtMeta)); - } - if (data.containsKey('local_updated_at')) { - context.handle( - _localUpdatedAtMeta, - localUpdatedAt.isAcceptableOrUnknown( - data['local_updated_at']!, _localUpdatedAtMeta)); - } - if (data.containsKey('remote_updated_at')) { - context.handle( - _remoteUpdatedAtMeta, - remoteUpdatedAt.isAcceptableOrUnknown( - data['remote_updated_at']!, _remoteUpdatedAtMeta)); - } - if (data.containsKey('local_deleted_at')) { - context.handle( - _localDeletedAtMeta, - localDeletedAt.isAcceptableOrUnknown( - data['local_deleted_at']!, _localDeletedAtMeta)); - } - if (data.containsKey('remote_deleted_at')) { - context.handle( - _remoteDeletedAtMeta, - remoteDeletedAt.isAcceptableOrUnknown( - data['remote_deleted_at']!, _remoteDeletedAtMeta)); - } - if (data.containsKey('message_text_updated_at')) { - context.handle( - _messageTextUpdatedAtMeta, - messageTextUpdatedAt.isAcceptableOrUnknown( - data['message_text_updated_at']!, _messageTextUpdatedAtMeta)); - } - if (data.containsKey('user_id')) { - context.handle(_userIdMeta, - userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); - } - if (data.containsKey('pinned')) { - context.handle(_pinnedMeta, - pinned.isAcceptableOrUnknown(data['pinned']!, _pinnedMeta)); - } - if (data.containsKey('pinned_at')) { - context.handle(_pinnedAtMeta, - pinnedAt.isAcceptableOrUnknown(data['pinned_at']!, _pinnedAtMeta)); - } - if (data.containsKey('pin_expires')) { - context.handle( - _pinExpiresMeta, - pinExpires.isAcceptableOrUnknown( - data['pin_expires']!, _pinExpiresMeta)); - } - if (data.containsKey('pinned_by_user_id')) { - context.handle( - _pinnedByUserIdMeta, - pinnedByUserId.isAcceptableOrUnknown( - data['pinned_by_user_id']!, _pinnedByUserIdMeta)); - } - if (data.containsKey('channel_cid')) { - context.handle( - _channelCidMeta, - channelCid.isAcceptableOrUnknown( - data['channel_cid']!, _channelCidMeta)); - } else if (isInserting) { - context.missing(_channelCidMeta); - } - context.handle(_i18nMeta, const VerificationResult.success()); - context.handle(_extraDataMeta, const VerificationResult.success()); - return context; - } - - @override - Set get $primaryKey => {id}; - @override - PinnedMessageEntity map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return PinnedMessageEntity( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - messageText: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}message_text']), - attachments: $PinnedMessagesTable.$converterattachments.fromSql( - attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}attachments'])!), - state: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}state'])!, - type: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}type'])!, - mentionedUsers: $PinnedMessagesTable.$convertermentionedUsers.fromSql( - attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}mentioned_users'])!), - reactionCounts: $PinnedMessagesTable.$converterreactionCountsn.fromSql( - attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}reaction_counts'])), - reactionScores: $PinnedMessagesTable.$converterreactionScoresn.fromSql( - attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}reaction_scores'])), - parentId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}parent_id']), - quotedMessageId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}quoted_message_id']), - pollId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}poll_id']), - replyCount: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}reply_count']), - showInChannel: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}show_in_channel']), - shadowed: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}shadowed'])!, - command: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}command']), - localCreatedAt: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}local_created_at']), - remoteCreatedAt: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}remote_created_at']), - localUpdatedAt: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}local_updated_at']), - remoteUpdatedAt: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}remote_updated_at']), - localDeletedAt: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}local_deleted_at']), - remoteDeletedAt: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}remote_deleted_at']), - messageTextUpdatedAt: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, - data['${effectivePrefix}message_text_updated_at']), - userId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}user_id']), - pinned: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}pinned'])!, - pinnedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}pinned_at']), - pinExpires: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}pin_expires']), - pinnedByUserId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}pinned_by_user_id']), - channelCid: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}channel_cid'])!, - i18n: $PinnedMessagesTable.$converteri18n.fromSql(attachedDatabase - .typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}i18n'])), - extraData: $PinnedMessagesTable.$converterextraDatan.fromSql( - attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}extra_data'])), - ); - } - - @override - $PinnedMessagesTable createAlias(String alias) { - return $PinnedMessagesTable(attachedDatabase, alias); - } - - static TypeConverter, String> $converterattachments = - ListConverter(); - static TypeConverter, String> $convertermentionedUsers = - ListConverter(); - static TypeConverter, String> $converterreactionCounts = - MapConverter(); - static TypeConverter?, String?> $converterreactionCountsn = - NullAwareTypeConverter.wrap($converterreactionCounts); - static TypeConverter, String> $converterreactionScores = - MapConverter(); - static TypeConverter?, String?> $converterreactionScoresn = - NullAwareTypeConverter.wrap($converterreactionScores); - static TypeConverter?, String?> $converteri18n = - NullableMapConverter(); - static TypeConverter, String> $converterextraData = - MapConverter(); - static TypeConverter?, String?> $converterextraDatan = - NullAwareTypeConverter.wrap($converterextraData); -} - -class PinnedMessageEntity extends DataClass - implements Insertable { - /// The message id - final String id; - - /// The text of this message - final String? messageText; - - /// The list of attachments, either provided by the user - /// or generated from a command or as a result of URL scraping. - final List attachments; - - /// The current state of the message. - final String state; - - /// The message type - final String type; - - /// The list of user mentioned in the message - final List mentionedUsers; - - /// A map describing the count of number of every reaction - final Map? reactionCounts; - - /// A map describing the count of score of every reaction - final Map? reactionScores; - - /// The ID of the parent message, if the message is a thread reply. - final String? parentId; - - /// The ID of the quoted message, if the message is a quoted reply. - final String? quotedMessageId; - - /// The ID of the poll, if the message is a poll. - final String? pollId; - - /// Number of replies for this message. - final int? replyCount; - - /// Check if this message needs to show in the channel. - final bool? showInChannel; - - /// If true the message is shadowed - final bool shadowed; - - /// A used command name. - final String? command; - - /// The DateTime on which the message was created on the client. - final DateTime? localCreatedAt; - - /// The DateTime on which the message was created on the server. - final DateTime? remoteCreatedAt; - - /// The DateTime on which the message was updated on the client. - final DateTime? localUpdatedAt; - - /// The DateTime on which the message was updated on the server. - final DateTime? remoteUpdatedAt; - - /// The DateTime on which the message was deleted on the client. - final DateTime? localDeletedAt; - - /// The DateTime on which the message was deleted on the server. - final DateTime? remoteDeletedAt; - - /// The DateTime at which the message text was edited - final DateTime? messageTextUpdatedAt; - - /// Id of the User who sent the message - final String? userId; - - /// Whether the message is pinned or not - final bool pinned; - - /// The DateTime at which the message was pinned - final DateTime? pinnedAt; - - /// The DateTime on which the message pin expires - final DateTime? pinExpires; - - /// Id of the User who pinned the message - final String? pinnedByUserId; - - /// The channel cid of which this message is part of - final String channelCid; - - /// A Map of [messageText] translations. - final Map? i18n; - - /// Message custom extraData - final Map? extraData; - const PinnedMessageEntity( - {required this.id, - this.messageText, - required this.attachments, - required this.state, - required this.type, - required this.mentionedUsers, - this.reactionCounts, - this.reactionScores, - this.parentId, - this.quotedMessageId, - this.pollId, - this.replyCount, - this.showInChannel, - required this.shadowed, - this.command, - this.localCreatedAt, - this.remoteCreatedAt, - this.localUpdatedAt, - this.remoteUpdatedAt, - this.localDeletedAt, - this.remoteDeletedAt, - this.messageTextUpdatedAt, - this.userId, - required this.pinned, - this.pinnedAt, - this.pinExpires, - this.pinnedByUserId, - required this.channelCid, - this.i18n, - this.extraData}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['id'] = Variable(id); - if (!nullToAbsent || messageText != null) { - map['message_text'] = Variable(messageText); - } - { - map['attachments'] = Variable( - $PinnedMessagesTable.$converterattachments.toSql(attachments)); - } - map['state'] = Variable(state); - map['type'] = Variable(type); - { - map['mentioned_users'] = Variable( - $PinnedMessagesTable.$convertermentionedUsers.toSql(mentionedUsers)); - } - if (!nullToAbsent || reactionCounts != null) { - map['reaction_counts'] = Variable( - $PinnedMessagesTable.$converterreactionCountsn.toSql(reactionCounts)); - } - if (!nullToAbsent || reactionScores != null) { - map['reaction_scores'] = Variable( - $PinnedMessagesTable.$converterreactionScoresn.toSql(reactionScores)); - } - if (!nullToAbsent || parentId != null) { - map['parent_id'] = Variable(parentId); - } - if (!nullToAbsent || quotedMessageId != null) { - map['quoted_message_id'] = Variable(quotedMessageId); - } - if (!nullToAbsent || pollId != null) { - map['poll_id'] = Variable(pollId); - } - if (!nullToAbsent || replyCount != null) { - map['reply_count'] = Variable(replyCount); - } - if (!nullToAbsent || showInChannel != null) { - map['show_in_channel'] = Variable(showInChannel); - } - map['shadowed'] = Variable(shadowed); - if (!nullToAbsent || command != null) { - map['command'] = Variable(command); - } - if (!nullToAbsent || localCreatedAt != null) { - map['local_created_at'] = Variable(localCreatedAt); - } - if (!nullToAbsent || remoteCreatedAt != null) { - map['remote_created_at'] = Variable(remoteCreatedAt); - } - if (!nullToAbsent || localUpdatedAt != null) { - map['local_updated_at'] = Variable(localUpdatedAt); - } - if (!nullToAbsent || remoteUpdatedAt != null) { - map['remote_updated_at'] = Variable(remoteUpdatedAt); - } - if (!nullToAbsent || localDeletedAt != null) { - map['local_deleted_at'] = Variable(localDeletedAt); - } - if (!nullToAbsent || remoteDeletedAt != null) { - map['remote_deleted_at'] = Variable(remoteDeletedAt); - } - if (!nullToAbsent || messageTextUpdatedAt != null) { - map['message_text_updated_at'] = Variable(messageTextUpdatedAt); - } - if (!nullToAbsent || userId != null) { - map['user_id'] = Variable(userId); - } - map['pinned'] = Variable(pinned); - if (!nullToAbsent || pinnedAt != null) { - map['pinned_at'] = Variable(pinnedAt); - } - if (!nullToAbsent || pinExpires != null) { - map['pin_expires'] = Variable(pinExpires); - } - if (!nullToAbsent || pinnedByUserId != null) { - map['pinned_by_user_id'] = Variable(pinnedByUserId); - } - map['channel_cid'] = Variable(channelCid); - if (!nullToAbsent || i18n != null) { - map['i18n'] = - Variable($PinnedMessagesTable.$converteri18n.toSql(i18n)); - } - if (!nullToAbsent || extraData != null) { - map['extra_data'] = Variable( - $PinnedMessagesTable.$converterextraDatan.toSql(extraData)); - } - return map; - } - - factory PinnedMessageEntity.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return PinnedMessageEntity( - id: serializer.fromJson(json['id']), - messageText: serializer.fromJson(json['messageText']), - attachments: serializer.fromJson>(json['attachments']), - state: serializer.fromJson(json['state']), - type: serializer.fromJson(json['type']), - mentionedUsers: serializer.fromJson>(json['mentionedUsers']), - reactionCounts: - serializer.fromJson?>(json['reactionCounts']), - reactionScores: - serializer.fromJson?>(json['reactionScores']), - parentId: serializer.fromJson(json['parentId']), - quotedMessageId: serializer.fromJson(json['quotedMessageId']), - pollId: serializer.fromJson(json['pollId']), - replyCount: serializer.fromJson(json['replyCount']), - showInChannel: serializer.fromJson(json['showInChannel']), - shadowed: serializer.fromJson(json['shadowed']), - command: serializer.fromJson(json['command']), - localCreatedAt: serializer.fromJson(json['localCreatedAt']), - remoteCreatedAt: serializer.fromJson(json['remoteCreatedAt']), - localUpdatedAt: serializer.fromJson(json['localUpdatedAt']), - remoteUpdatedAt: serializer.fromJson(json['remoteUpdatedAt']), - localDeletedAt: serializer.fromJson(json['localDeletedAt']), - remoteDeletedAt: serializer.fromJson(json['remoteDeletedAt']), - messageTextUpdatedAt: - serializer.fromJson(json['messageTextUpdatedAt']), - userId: serializer.fromJson(json['userId']), - pinned: serializer.fromJson(json['pinned']), - pinnedAt: serializer.fromJson(json['pinnedAt']), - pinExpires: serializer.fromJson(json['pinExpires']), - pinnedByUserId: serializer.fromJson(json['pinnedByUserId']), - channelCid: serializer.fromJson(json['channelCid']), - i18n: serializer.fromJson?>(json['i18n']), - extraData: serializer.fromJson?>(json['extraData']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'id': serializer.toJson(id), - 'messageText': serializer.toJson(messageText), - 'attachments': serializer.toJson>(attachments), - 'state': serializer.toJson(state), - 'type': serializer.toJson(type), - 'mentionedUsers': serializer.toJson>(mentionedUsers), - 'reactionCounts': serializer.toJson?>(reactionCounts), - 'reactionScores': serializer.toJson?>(reactionScores), - 'parentId': serializer.toJson(parentId), - 'quotedMessageId': serializer.toJson(quotedMessageId), - 'pollId': serializer.toJson(pollId), - 'replyCount': serializer.toJson(replyCount), - 'showInChannel': serializer.toJson(showInChannel), - 'shadowed': serializer.toJson(shadowed), - 'command': serializer.toJson(command), - 'localCreatedAt': serializer.toJson(localCreatedAt), - 'remoteCreatedAt': serializer.toJson(remoteCreatedAt), - 'localUpdatedAt': serializer.toJson(localUpdatedAt), - 'remoteUpdatedAt': serializer.toJson(remoteUpdatedAt), - 'localDeletedAt': serializer.toJson(localDeletedAt), - 'remoteDeletedAt': serializer.toJson(remoteDeletedAt), - 'messageTextUpdatedAt': - serializer.toJson(messageTextUpdatedAt), - 'userId': serializer.toJson(userId), - 'pinned': serializer.toJson(pinned), - 'pinnedAt': serializer.toJson(pinnedAt), - 'pinExpires': serializer.toJson(pinExpires), - 'pinnedByUserId': serializer.toJson(pinnedByUserId), - 'channelCid': serializer.toJson(channelCid), - 'i18n': serializer.toJson?>(i18n), - 'extraData': serializer.toJson?>(extraData), - }; - } - - PinnedMessageEntity copyWith( - {String? id, - Value messageText = const Value.absent(), - List? attachments, - String? state, - String? type, - List? mentionedUsers, - Value?> reactionCounts = const Value.absent(), - Value?> reactionScores = const Value.absent(), - Value parentId = const Value.absent(), - Value quotedMessageId = const Value.absent(), - Value pollId = const Value.absent(), - Value replyCount = const Value.absent(), - Value showInChannel = const Value.absent(), - bool? shadowed, - Value command = const Value.absent(), - Value localCreatedAt = const Value.absent(), - Value remoteCreatedAt = const Value.absent(), - Value localUpdatedAt = const Value.absent(), - Value remoteUpdatedAt = const Value.absent(), - Value localDeletedAt = const Value.absent(), - Value remoteDeletedAt = const Value.absent(), - Value messageTextUpdatedAt = const Value.absent(), - Value userId = const Value.absent(), - bool? pinned, - Value pinnedAt = const Value.absent(), - Value pinExpires = const Value.absent(), - Value pinnedByUserId = const Value.absent(), - String? channelCid, - Value?> i18n = const Value.absent(), - Value?> extraData = const Value.absent()}) => - PinnedMessageEntity( - id: id ?? this.id, - messageText: messageText.present ? messageText.value : this.messageText, - attachments: attachments ?? this.attachments, - state: state ?? this.state, - type: type ?? this.type, - mentionedUsers: mentionedUsers ?? this.mentionedUsers, - reactionCounts: - reactionCounts.present ? reactionCounts.value : this.reactionCounts, - reactionScores: - reactionScores.present ? reactionScores.value : this.reactionScores, - parentId: parentId.present ? parentId.value : this.parentId, - quotedMessageId: quotedMessageId.present - ? quotedMessageId.value - : this.quotedMessageId, - pollId: pollId.present ? pollId.value : this.pollId, - replyCount: replyCount.present ? replyCount.value : this.replyCount, - showInChannel: - showInChannel.present ? showInChannel.value : this.showInChannel, - shadowed: shadowed ?? this.shadowed, - command: command.present ? command.value : this.command, - localCreatedAt: - localCreatedAt.present ? localCreatedAt.value : this.localCreatedAt, - remoteCreatedAt: remoteCreatedAt.present - ? remoteCreatedAt.value - : this.remoteCreatedAt, - localUpdatedAt: - localUpdatedAt.present ? localUpdatedAt.value : this.localUpdatedAt, - remoteUpdatedAt: remoteUpdatedAt.present - ? remoteUpdatedAt.value - : this.remoteUpdatedAt, - localDeletedAt: - localDeletedAt.present ? localDeletedAt.value : this.localDeletedAt, - remoteDeletedAt: remoteDeletedAt.present - ? remoteDeletedAt.value - : this.remoteDeletedAt, - messageTextUpdatedAt: messageTextUpdatedAt.present - ? messageTextUpdatedAt.value - : this.messageTextUpdatedAt, - userId: userId.present ? userId.value : this.userId, - pinned: pinned ?? this.pinned, - pinnedAt: pinnedAt.present ? pinnedAt.value : this.pinnedAt, - pinExpires: pinExpires.present ? pinExpires.value : this.pinExpires, - pinnedByUserId: - pinnedByUserId.present ? pinnedByUserId.value : this.pinnedByUserId, - channelCid: channelCid ?? this.channelCid, - i18n: i18n.present ? i18n.value : this.i18n, - extraData: extraData.present ? extraData.value : this.extraData, - ); - PinnedMessageEntity copyWithCompanion(PinnedMessagesCompanion data) { - return PinnedMessageEntity( - id: data.id.present ? data.id.value : this.id, - messageText: - data.messageText.present ? data.messageText.value : this.messageText, - attachments: - data.attachments.present ? data.attachments.value : this.attachments, - state: data.state.present ? data.state.value : this.state, - type: data.type.present ? data.type.value : this.type, - mentionedUsers: data.mentionedUsers.present - ? data.mentionedUsers.value - : this.mentionedUsers, - reactionCounts: data.reactionCounts.present - ? data.reactionCounts.value - : this.reactionCounts, - reactionScores: data.reactionScores.present - ? data.reactionScores.value - : this.reactionScores, - parentId: data.parentId.present ? data.parentId.value : this.parentId, - quotedMessageId: data.quotedMessageId.present - ? data.quotedMessageId.value - : this.quotedMessageId, - pollId: data.pollId.present ? data.pollId.value : this.pollId, - replyCount: - data.replyCount.present ? data.replyCount.value : this.replyCount, - showInChannel: data.showInChannel.present - ? data.showInChannel.value - : this.showInChannel, - shadowed: data.shadowed.present ? data.shadowed.value : this.shadowed, - command: data.command.present ? data.command.value : this.command, - localCreatedAt: data.localCreatedAt.present - ? data.localCreatedAt.value - : this.localCreatedAt, - remoteCreatedAt: data.remoteCreatedAt.present - ? data.remoteCreatedAt.value - : this.remoteCreatedAt, - localUpdatedAt: data.localUpdatedAt.present - ? data.localUpdatedAt.value - : this.localUpdatedAt, - remoteUpdatedAt: data.remoteUpdatedAt.present - ? data.remoteUpdatedAt.value - : this.remoteUpdatedAt, - localDeletedAt: data.localDeletedAt.present - ? data.localDeletedAt.value - : this.localDeletedAt, - remoteDeletedAt: data.remoteDeletedAt.present - ? data.remoteDeletedAt.value - : this.remoteDeletedAt, - messageTextUpdatedAt: data.messageTextUpdatedAt.present - ? data.messageTextUpdatedAt.value - : this.messageTextUpdatedAt, - userId: data.userId.present ? data.userId.value : this.userId, - pinned: data.pinned.present ? data.pinned.value : this.pinned, - pinnedAt: data.pinnedAt.present ? data.pinnedAt.value : this.pinnedAt, - pinExpires: - data.pinExpires.present ? data.pinExpires.value : this.pinExpires, - pinnedByUserId: data.pinnedByUserId.present - ? data.pinnedByUserId.value - : this.pinnedByUserId, - channelCid: - data.channelCid.present ? data.channelCid.value : this.channelCid, - i18n: data.i18n.present ? data.i18n.value : this.i18n, - extraData: data.extraData.present ? data.extraData.value : this.extraData, - ); - } - - @override - String toString() { - return (StringBuffer('PinnedMessageEntity(') - ..write('id: $id, ') - ..write('messageText: $messageText, ') - ..write('attachments: $attachments, ') - ..write('state: $state, ') - ..write('type: $type, ') - ..write('mentionedUsers: $mentionedUsers, ') - ..write('reactionCounts: $reactionCounts, ') - ..write('reactionScores: $reactionScores, ') - ..write('parentId: $parentId, ') - ..write('quotedMessageId: $quotedMessageId, ') - ..write('pollId: $pollId, ') - ..write('replyCount: $replyCount, ') - ..write('showInChannel: $showInChannel, ') - ..write('shadowed: $shadowed, ') - ..write('command: $command, ') - ..write('localCreatedAt: $localCreatedAt, ') - ..write('remoteCreatedAt: $remoteCreatedAt, ') - ..write('localUpdatedAt: $localUpdatedAt, ') - ..write('remoteUpdatedAt: $remoteUpdatedAt, ') - ..write('localDeletedAt: $localDeletedAt, ') - ..write('remoteDeletedAt: $remoteDeletedAt, ') - ..write('messageTextUpdatedAt: $messageTextUpdatedAt, ') - ..write('userId: $userId, ') - ..write('pinned: $pinned, ') - ..write('pinnedAt: $pinnedAt, ') - ..write('pinExpires: $pinExpires, ') - ..write('pinnedByUserId: $pinnedByUserId, ') - ..write('channelCid: $channelCid, ') - ..write('i18n: $i18n, ') - ..write('extraData: $extraData') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hashAll([ - id, - messageText, - attachments, - state, - type, - mentionedUsers, - reactionCounts, - reactionScores, - parentId, - quotedMessageId, - pollId, - replyCount, - showInChannel, - shadowed, - command, - localCreatedAt, - remoteCreatedAt, - localUpdatedAt, - remoteUpdatedAt, - localDeletedAt, - remoteDeletedAt, - messageTextUpdatedAt, - userId, - pinned, - pinnedAt, - pinExpires, - pinnedByUserId, - channelCid, - i18n, - extraData - ]); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is PinnedMessageEntity && - other.id == this.id && - other.messageText == this.messageText && - other.attachments == this.attachments && - other.state == this.state && - other.type == this.type && - other.mentionedUsers == this.mentionedUsers && - other.reactionCounts == this.reactionCounts && - other.reactionScores == this.reactionScores && - other.parentId == this.parentId && - other.quotedMessageId == this.quotedMessageId && - other.pollId == this.pollId && - other.replyCount == this.replyCount && - other.showInChannel == this.showInChannel && - other.shadowed == this.shadowed && - other.command == this.command && - other.localCreatedAt == this.localCreatedAt && - other.remoteCreatedAt == this.remoteCreatedAt && - other.localUpdatedAt == this.localUpdatedAt && - other.remoteUpdatedAt == this.remoteUpdatedAt && - other.localDeletedAt == this.localDeletedAt && - other.remoteDeletedAt == this.remoteDeletedAt && - other.messageTextUpdatedAt == this.messageTextUpdatedAt && - other.userId == this.userId && - other.pinned == this.pinned && - other.pinnedAt == this.pinnedAt && - other.pinExpires == this.pinExpires && - other.pinnedByUserId == this.pinnedByUserId && - other.channelCid == this.channelCid && - other.i18n == this.i18n && - other.extraData == this.extraData); -} - -class PinnedMessagesCompanion extends UpdateCompanion { - final Value id; - final Value messageText; - final Value> attachments; - final Value state; - final Value type; - final Value> mentionedUsers; - final Value?> reactionCounts; - final Value?> reactionScores; - final Value parentId; - final Value quotedMessageId; - final Value pollId; - final Value replyCount; - final Value showInChannel; - final Value shadowed; - final Value command; - final Value localCreatedAt; - final Value remoteCreatedAt; - final Value localUpdatedAt; - final Value remoteUpdatedAt; - final Value localDeletedAt; - final Value remoteDeletedAt; - final Value messageTextUpdatedAt; - final Value userId; - final Value pinned; - final Value pinnedAt; - final Value pinExpires; - final Value pinnedByUserId; - final Value channelCid; - final Value?> i18n; - final Value?> extraData; - final Value rowid; - const PinnedMessagesCompanion({ - this.id = const Value.absent(), - this.messageText = const Value.absent(), - this.attachments = const Value.absent(), - this.state = const Value.absent(), - this.type = const Value.absent(), - this.mentionedUsers = const Value.absent(), - this.reactionCounts = const Value.absent(), - this.reactionScores = const Value.absent(), - this.parentId = const Value.absent(), - this.quotedMessageId = const Value.absent(), - this.pollId = const Value.absent(), - this.replyCount = const Value.absent(), - this.showInChannel = const Value.absent(), - this.shadowed = const Value.absent(), - this.command = const Value.absent(), - this.localCreatedAt = const Value.absent(), - this.remoteCreatedAt = const Value.absent(), - this.localUpdatedAt = const Value.absent(), - this.remoteUpdatedAt = const Value.absent(), - this.localDeletedAt = const Value.absent(), - this.remoteDeletedAt = const Value.absent(), - this.messageTextUpdatedAt = const Value.absent(), - this.userId = const Value.absent(), - this.pinned = const Value.absent(), - this.pinnedAt = const Value.absent(), - this.pinExpires = const Value.absent(), - this.pinnedByUserId = const Value.absent(), - this.channelCid = const Value.absent(), - this.i18n = const Value.absent(), - this.extraData = const Value.absent(), - this.rowid = const Value.absent(), - }); - PinnedMessagesCompanion.insert({ - required String id, - this.messageText = const Value.absent(), - required List attachments, - required String state, - this.type = const Value.absent(), - required List mentionedUsers, - this.reactionCounts = const Value.absent(), - this.reactionScores = const Value.absent(), - this.parentId = const Value.absent(), - this.quotedMessageId = const Value.absent(), - this.pollId = const Value.absent(), - this.replyCount = const Value.absent(), - this.showInChannel = const Value.absent(), - this.shadowed = const Value.absent(), - this.command = const Value.absent(), - this.localCreatedAt = const Value.absent(), - this.remoteCreatedAt = const Value.absent(), - this.localUpdatedAt = const Value.absent(), - this.remoteUpdatedAt = const Value.absent(), - this.localDeletedAt = const Value.absent(), - this.remoteDeletedAt = const Value.absent(), - this.messageTextUpdatedAt = const Value.absent(), - this.userId = const Value.absent(), - this.pinned = const Value.absent(), - this.pinnedAt = const Value.absent(), - this.pinExpires = const Value.absent(), - this.pinnedByUserId = const Value.absent(), - required String channelCid, - this.i18n = const Value.absent(), - this.extraData = const Value.absent(), - this.rowid = const Value.absent(), - }) : id = Value(id), - attachments = Value(attachments), - state = Value(state), - mentionedUsers = Value(mentionedUsers), - channelCid = Value(channelCid); - static Insertable custom({ - Expression? id, - Expression? messageText, - Expression? attachments, - Expression? state, - Expression? type, - Expression? mentionedUsers, - Expression? reactionCounts, - Expression? reactionScores, - Expression? parentId, - Expression? quotedMessageId, - Expression? pollId, - Expression? replyCount, - Expression? showInChannel, - Expression? shadowed, - Expression? command, - Expression? localCreatedAt, - Expression? remoteCreatedAt, - Expression? localUpdatedAt, - Expression? remoteUpdatedAt, - Expression? localDeletedAt, - Expression? remoteDeletedAt, - Expression? messageTextUpdatedAt, - Expression? userId, - Expression? pinned, - Expression? pinnedAt, - Expression? pinExpires, - Expression? pinnedByUserId, - Expression? channelCid, - Expression? i18n, - Expression? extraData, - Expression? rowid, - }) { - return RawValuesInsertable({ - if (id != null) 'id': id, - if (messageText != null) 'message_text': messageText, - if (attachments != null) 'attachments': attachments, - if (state != null) 'state': state, - if (type != null) 'type': type, - if (mentionedUsers != null) 'mentioned_users': mentionedUsers, - if (reactionCounts != null) 'reaction_counts': reactionCounts, - if (reactionScores != null) 'reaction_scores': reactionScores, - if (parentId != null) 'parent_id': parentId, - if (quotedMessageId != null) 'quoted_message_id': quotedMessageId, - if (pollId != null) 'poll_id': pollId, - if (replyCount != null) 'reply_count': replyCount, - if (showInChannel != null) 'show_in_channel': showInChannel, - if (shadowed != null) 'shadowed': shadowed, - if (command != null) 'command': command, - if (localCreatedAt != null) 'local_created_at': localCreatedAt, - if (remoteCreatedAt != null) 'remote_created_at': remoteCreatedAt, - if (localUpdatedAt != null) 'local_updated_at': localUpdatedAt, - if (remoteUpdatedAt != null) 'remote_updated_at': remoteUpdatedAt, - if (localDeletedAt != null) 'local_deleted_at': localDeletedAt, - if (remoteDeletedAt != null) 'remote_deleted_at': remoteDeletedAt, - if (messageTextUpdatedAt != null) - 'message_text_updated_at': messageTextUpdatedAt, - if (userId != null) 'user_id': userId, - if (pinned != null) 'pinned': pinned, - if (pinnedAt != null) 'pinned_at': pinnedAt, - if (pinExpires != null) 'pin_expires': pinExpires, - if (pinnedByUserId != null) 'pinned_by_user_id': pinnedByUserId, - if (channelCid != null) 'channel_cid': channelCid, - if (i18n != null) 'i18n': i18n, - if (extraData != null) 'extra_data': extraData, - if (rowid != null) 'rowid': rowid, - }); - } - - PinnedMessagesCompanion copyWith( - {Value? id, - Value? messageText, - Value>? attachments, - Value? state, - Value? type, - Value>? mentionedUsers, - Value?>? reactionCounts, - Value?>? reactionScores, - Value? parentId, - Value? quotedMessageId, - Value? pollId, - Value? replyCount, - Value? showInChannel, - Value? shadowed, - Value? command, - Value? localCreatedAt, - Value? remoteCreatedAt, - Value? localUpdatedAt, - Value? remoteUpdatedAt, - Value? localDeletedAt, - Value? remoteDeletedAt, - Value? messageTextUpdatedAt, - Value? userId, - Value? pinned, - Value? pinnedAt, - Value? pinExpires, - Value? pinnedByUserId, - Value? channelCid, - Value?>? i18n, - Value?>? extraData, - Value? rowid}) { - return PinnedMessagesCompanion( - id: id ?? this.id, - messageText: messageText ?? this.messageText, - attachments: attachments ?? this.attachments, - state: state ?? this.state, - type: type ?? this.type, - mentionedUsers: mentionedUsers ?? this.mentionedUsers, - reactionCounts: reactionCounts ?? this.reactionCounts, - reactionScores: reactionScores ?? this.reactionScores, - parentId: parentId ?? this.parentId, - quotedMessageId: quotedMessageId ?? this.quotedMessageId, - pollId: pollId ?? this.pollId, - replyCount: replyCount ?? this.replyCount, - showInChannel: showInChannel ?? this.showInChannel, - shadowed: shadowed ?? this.shadowed, - command: command ?? this.command, - localCreatedAt: localCreatedAt ?? this.localCreatedAt, - remoteCreatedAt: remoteCreatedAt ?? this.remoteCreatedAt, - localUpdatedAt: localUpdatedAt ?? this.localUpdatedAt, - remoteUpdatedAt: remoteUpdatedAt ?? this.remoteUpdatedAt, - localDeletedAt: localDeletedAt ?? this.localDeletedAt, - remoteDeletedAt: remoteDeletedAt ?? this.remoteDeletedAt, - messageTextUpdatedAt: messageTextUpdatedAt ?? this.messageTextUpdatedAt, - userId: userId ?? this.userId, - pinned: pinned ?? this.pinned, - pinnedAt: pinnedAt ?? this.pinnedAt, - pinExpires: pinExpires ?? this.pinExpires, - pinnedByUserId: pinnedByUserId ?? this.pinnedByUserId, - channelCid: channelCid ?? this.channelCid, - i18n: i18n ?? this.i18n, - extraData: extraData ?? this.extraData, - rowid: rowid ?? this.rowid, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (id.present) { - map['id'] = Variable(id.value); - } - if (messageText.present) { - map['message_text'] = Variable(messageText.value); - } - if (attachments.present) { - map['attachments'] = Variable( - $PinnedMessagesTable.$converterattachments.toSql(attachments.value)); - } - if (state.present) { - map['state'] = Variable(state.value); - } - if (type.present) { - map['type'] = Variable(type.value); - } - if (mentionedUsers.present) { - map['mentioned_users'] = Variable($PinnedMessagesTable - .$convertermentionedUsers - .toSql(mentionedUsers.value)); - } - if (reactionCounts.present) { - map['reaction_counts'] = Variable($PinnedMessagesTable - .$converterreactionCountsn - .toSql(reactionCounts.value)); - } - if (reactionScores.present) { - map['reaction_scores'] = Variable($PinnedMessagesTable - .$converterreactionScoresn - .toSql(reactionScores.value)); - } - if (parentId.present) { - map['parent_id'] = Variable(parentId.value); - } - if (quotedMessageId.present) { - map['quoted_message_id'] = Variable(quotedMessageId.value); - } - if (pollId.present) { - map['poll_id'] = Variable(pollId.value); - } - if (replyCount.present) { - map['reply_count'] = Variable(replyCount.value); - } - if (showInChannel.present) { - map['show_in_channel'] = Variable(showInChannel.value); - } - if (shadowed.present) { - map['shadowed'] = Variable(shadowed.value); - } - if (command.present) { - map['command'] = Variable(command.value); - } - if (localCreatedAt.present) { - map['local_created_at'] = Variable(localCreatedAt.value); - } - if (remoteCreatedAt.present) { - map['remote_created_at'] = Variable(remoteCreatedAt.value); - } - if (localUpdatedAt.present) { - map['local_updated_at'] = Variable(localUpdatedAt.value); - } - if (remoteUpdatedAt.present) { - map['remote_updated_at'] = Variable(remoteUpdatedAt.value); - } - if (localDeletedAt.present) { - map['local_deleted_at'] = Variable(localDeletedAt.value); - } - if (remoteDeletedAt.present) { - map['remote_deleted_at'] = Variable(remoteDeletedAt.value); - } - if (messageTextUpdatedAt.present) { - map['message_text_updated_at'] = - Variable(messageTextUpdatedAt.value); - } - if (userId.present) { - map['user_id'] = Variable(userId.value); - } - if (pinned.present) { - map['pinned'] = Variable(pinned.value); - } - if (pinnedAt.present) { - map['pinned_at'] = Variable(pinnedAt.value); - } - if (pinExpires.present) { - map['pin_expires'] = Variable(pinExpires.value); - } - if (pinnedByUserId.present) { - map['pinned_by_user_id'] = Variable(pinnedByUserId.value); - } - if (channelCid.present) { - map['channel_cid'] = Variable(channelCid.value); - } - if (i18n.present) { - map['i18n'] = Variable( - $PinnedMessagesTable.$converteri18n.toSql(i18n.value)); - } - if (extraData.present) { - map['extra_data'] = Variable( - $PinnedMessagesTable.$converterextraDatan.toSql(extraData.value)); - } - if (rowid.present) { - map['rowid'] = Variable(rowid.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('PinnedMessagesCompanion(') - ..write('id: $id, ') - ..write('messageText: $messageText, ') - ..write('attachments: $attachments, ') - ..write('state: $state, ') - ..write('type: $type, ') - ..write('mentionedUsers: $mentionedUsers, ') - ..write('reactionCounts: $reactionCounts, ') - ..write('reactionScores: $reactionScores, ') - ..write('parentId: $parentId, ') - ..write('quotedMessageId: $quotedMessageId, ') - ..write('pollId: $pollId, ') - ..write('replyCount: $replyCount, ') - ..write('showInChannel: $showInChannel, ') - ..write('shadowed: $shadowed, ') - ..write('command: $command, ') - ..write('localCreatedAt: $localCreatedAt, ') - ..write('remoteCreatedAt: $remoteCreatedAt, ') - ..write('localUpdatedAt: $localUpdatedAt, ') - ..write('remoteUpdatedAt: $remoteUpdatedAt, ') - ..write('localDeletedAt: $localDeletedAt, ') - ..write('remoteDeletedAt: $remoteDeletedAt, ') - ..write('messageTextUpdatedAt: $messageTextUpdatedAt, ') - ..write('userId: $userId, ') - ..write('pinned: $pinned, ') - ..write('pinnedAt: $pinnedAt, ') - ..write('pinExpires: $pinExpires, ') - ..write('pinnedByUserId: $pinnedByUserId, ') - ..write('channelCid: $channelCid, ') - ..write('i18n: $i18n, ') - ..write('extraData: $extraData, ') - ..write('rowid: $rowid') - ..write(')')) - .toString(); - } -} - -class $PollsTable extends Polls with TableInfo<$PollsTable, PollEntity> { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - $PollsTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _idMeta = const VerificationMeta('id'); - @override - late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - static const VerificationMeta _nameMeta = const VerificationMeta('name'); - @override - late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - static const VerificationMeta _descriptionMeta = - const VerificationMeta('description'); - @override - late final GeneratedColumn description = GeneratedColumn( - 'description', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _optionsMeta = - const VerificationMeta('options'); - @override - late final GeneratedColumnWithTypeConverter, String> options = - GeneratedColumn('options', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true) - .withConverter>($PollsTable.$converteroptions); - static const VerificationMeta _votingVisibilityMeta = - const VerificationMeta('votingVisibility'); - @override - late final GeneratedColumnWithTypeConverter - votingVisibility = GeneratedColumn( - 'voting_visibility', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultValue: const Constant('public')) - .withConverter( - $PollsTable.$convertervotingVisibility); - static const VerificationMeta _enforceUniqueVoteMeta = - const VerificationMeta('enforceUniqueVote'); - @override - late final GeneratedColumn enforceUniqueVote = GeneratedColumn( - 'enforce_unique_vote', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("enforce_unique_vote" IN (0, 1))'), - defaultValue: const Constant(false)); - static const VerificationMeta _maxVotesAllowedMeta = - const VerificationMeta('maxVotesAllowed'); - @override - late final GeneratedColumn maxVotesAllowed = GeneratedColumn( - 'max_votes_allowed', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - static const VerificationMeta _allowUserSuggestedOptionsMeta = - const VerificationMeta('allowUserSuggestedOptions'); - @override - late final GeneratedColumn allowUserSuggestedOptions = - GeneratedColumn('allow_user_suggested_options', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("allow_user_suggested_options" IN (0, 1))'), - defaultValue: const Constant(false)); - static const VerificationMeta _allowAnswersMeta = - const VerificationMeta('allowAnswers'); - @override - late final GeneratedColumn allowAnswers = GeneratedColumn( - 'allow_answers', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("allow_answers" IN (0, 1))'), - defaultValue: const Constant(false)); - static const VerificationMeta _isClosedMeta = - const VerificationMeta('isClosed'); - @override - late final GeneratedColumn isClosed = GeneratedColumn( - 'is_closed', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_closed" IN (0, 1))'), - defaultValue: const Constant(false)); - static const VerificationMeta _answersCountMeta = - const VerificationMeta('answersCount'); - @override - late final GeneratedColumn answersCount = GeneratedColumn( - 'answers_count', aliasedName, false, - type: DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const Constant(0)); - static const VerificationMeta _voteCountsByOptionMeta = - const VerificationMeta('voteCountsByOption'); - @override - late final GeneratedColumnWithTypeConverter, String> - voteCountsByOption = GeneratedColumn( - 'vote_counts_by_option', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true) - .withConverter>( - $PollsTable.$convertervoteCountsByOption); - static const VerificationMeta _voteCountMeta = - const VerificationMeta('voteCount'); - @override - late final GeneratedColumn voteCount = GeneratedColumn( - 'vote_count', aliasedName, false, - type: DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const Constant(0)); - static const VerificationMeta _createdByIdMeta = - const VerificationMeta('createdById'); - @override - late final GeneratedColumn createdById = GeneratedColumn( - 'created_by_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _createdAtMeta = - const VerificationMeta('createdAt'); - @override - late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: currentDateAndTime); - static const VerificationMeta _updatedAtMeta = - const VerificationMeta('updatedAt'); - @override - late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: currentDateAndTime); - static const VerificationMeta _extraDataMeta = - const VerificationMeta('extraData'); - @override - late final GeneratedColumnWithTypeConverter?, String> - extraData = GeneratedColumn('extra_data', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false) - .withConverter?>( - $PollsTable.$converterextraDatan); - @override - List get $columns => [ - id, - name, - description, - options, - votingVisibility, - enforceUniqueVote, - maxVotesAllowed, - allowUserSuggestedOptions, - allowAnswers, - isClosed, - answersCount, - voteCountsByOption, - voteCount, - createdById, - createdAt, - updatedAt, - extraData - ]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'polls'; - @override - VerificationContext validateIntegrity(Insertable instance, - {bool isInserting = false}) { - final context = VerificationContext(); - final data = instance.toColumns(true); - if (data.containsKey('id')) { - context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); - } else if (isInserting) { - context.missing(_idMeta); - } - if (data.containsKey('name')) { - context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); - } else if (isInserting) { - context.missing(_nameMeta); - } - if (data.containsKey('description')) { - context.handle( - _descriptionMeta, - description.isAcceptableOrUnknown( - data['description']!, _descriptionMeta)); - } - context.handle(_optionsMeta, const VerificationResult.success()); - context.handle(_votingVisibilityMeta, const VerificationResult.success()); - if (data.containsKey('enforce_unique_vote')) { - context.handle( - _enforceUniqueVoteMeta, - enforceUniqueVote.isAcceptableOrUnknown( - data['enforce_unique_vote']!, _enforceUniqueVoteMeta)); - } - if (data.containsKey('max_votes_allowed')) { - context.handle( - _maxVotesAllowedMeta, - maxVotesAllowed.isAcceptableOrUnknown( - data['max_votes_allowed']!, _maxVotesAllowedMeta)); - } - if (data.containsKey('allow_user_suggested_options')) { - context.handle( - _allowUserSuggestedOptionsMeta, - allowUserSuggestedOptions.isAcceptableOrUnknown( - data['allow_user_suggested_options']!, - _allowUserSuggestedOptionsMeta)); - } - if (data.containsKey('allow_answers')) { - context.handle( - _allowAnswersMeta, - allowAnswers.isAcceptableOrUnknown( - data['allow_answers']!, _allowAnswersMeta)); - } - if (data.containsKey('is_closed')) { - context.handle(_isClosedMeta, - isClosed.isAcceptableOrUnknown(data['is_closed']!, _isClosedMeta)); - } - if (data.containsKey('answers_count')) { - context.handle( - _answersCountMeta, - answersCount.isAcceptableOrUnknown( - data['answers_count']!, _answersCountMeta)); - } - context.handle(_voteCountsByOptionMeta, const VerificationResult.success()); - if (data.containsKey('vote_count')) { - context.handle(_voteCountMeta, - voteCount.isAcceptableOrUnknown(data['vote_count']!, _voteCountMeta)); - } - if (data.containsKey('created_by_id')) { - context.handle( - _createdByIdMeta, - createdById.isAcceptableOrUnknown( - data['created_by_id']!, _createdByIdMeta)); - } - if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); - } - if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); - } - context.handle(_extraDataMeta, const VerificationResult.success()); - return context; - } - - @override - Set get $primaryKey => {id}; - @override - PollEntity map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return PollEntity( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - description: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}description']), - options: $PollsTable.$converteroptions.fromSql(attachedDatabase - .typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}options'])!), - votingVisibility: $PollsTable.$convertervotingVisibility.fromSql( - attachedDatabase.typeMapping.read(DriftSqlType.string, - data['${effectivePrefix}voting_visibility'])!), - enforceUniqueVote: attachedDatabase.typeMapping.read( - DriftSqlType.bool, data['${effectivePrefix}enforce_unique_vote'])!, - maxVotesAllowed: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}max_votes_allowed']), - allowUserSuggestedOptions: attachedDatabase.typeMapping.read( - DriftSqlType.bool, - data['${effectivePrefix}allow_user_suggested_options'])!, - allowAnswers: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}allow_answers'])!, - isClosed: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_closed'])!, - answersCount: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}answers_count'])!, - voteCountsByOption: $PollsTable.$convertervoteCountsByOption.fromSql( - attachedDatabase.typeMapping.read(DriftSqlType.string, - data['${effectivePrefix}vote_counts_by_option'])!), - voteCount: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}vote_count'])!, - createdById: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}created_by_id']), - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - extraData: $PollsTable.$converterextraDatan.fromSql(attachedDatabase - .typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}extra_data'])), - ); - } - - @override - $PollsTable createAlias(String alias) { - return $PollsTable(attachedDatabase, alias); - } - - static TypeConverter, String> $converteroptions = - ListConverter(); - static TypeConverter $convertervotingVisibility = - const VotingVisibilityConverter(); - static TypeConverter, String> $convertervoteCountsByOption = - MapConverter(); - static TypeConverter, String> $converterextraData = - MapConverter(); - static TypeConverter?, String?> $converterextraDatan = - NullAwareTypeConverter.wrap($converterextraData); -} - -class PollEntity extends DataClass implements Insertable { - /// The unique identifier of the poll. - final String id; - - /// The name of the poll. - final String name; - - /// The description of the poll. - final String? description; - - /// The list of options available for the poll. - final List options; - - /// Represents the visibility of the voting process. - /// - /// Defaults to 'public'. - final VotingVisibility votingVisibility; - - /// If true, only unique votes are allowed. - /// - /// Defaults to false. - final bool enforceUniqueVote; - - /// The maximum number of votes allowed per user. - final int? maxVotesAllowed; - - /// If true, users can suggest their own options. - /// - /// Defaults to false. - final bool allowUserSuggestedOptions; - - /// If true, users can provide their own answers/comments. - /// - /// Defaults to false. - final bool allowAnswers; - - /// Indicates if the poll is closed. - final bool isClosed; - - /// The total number of answers received by the poll. - final int answersCount; - - /// Map of vote counts by option. - final Map voteCountsByOption; - - /// The total number of votes received by the poll. - final int voteCount; - - /// The id of the user who created the poll. - final String? createdById; - - /// The date when the poll was created. - final DateTime createdAt; - - /// The date when the poll was last updated. - final DateTime updatedAt; - - /// Map of custom poll extraData - final Map? extraData; - const PollEntity( - {required this.id, - required this.name, - this.description, - required this.options, - required this.votingVisibility, - required this.enforceUniqueVote, - this.maxVotesAllowed, - required this.allowUserSuggestedOptions, - required this.allowAnswers, - required this.isClosed, - required this.answersCount, - required this.voteCountsByOption, - required this.voteCount, - this.createdById, - required this.createdAt, - required this.updatedAt, - this.extraData}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['id'] = Variable(id); - map['name'] = Variable(name); - if (!nullToAbsent || description != null) { - map['description'] = Variable(description); - } - { - map['options'] = - Variable($PollsTable.$converteroptions.toSql(options)); - } - { - map['voting_visibility'] = Variable( - $PollsTable.$convertervotingVisibility.toSql(votingVisibility)); - } - map['enforce_unique_vote'] = Variable(enforceUniqueVote); - if (!nullToAbsent || maxVotesAllowed != null) { - map['max_votes_allowed'] = Variable(maxVotesAllowed); - } - map['allow_user_suggested_options'] = - Variable(allowUserSuggestedOptions); - map['allow_answers'] = Variable(allowAnswers); - map['is_closed'] = Variable(isClosed); - map['answers_count'] = Variable(answersCount); - { - map['vote_counts_by_option'] = Variable( - $PollsTable.$convertervoteCountsByOption.toSql(voteCountsByOption)); - } - map['vote_count'] = Variable(voteCount); - if (!nullToAbsent || createdById != null) { - map['created_by_id'] = Variable(createdById); - } - map['created_at'] = Variable(createdAt); - map['updated_at'] = Variable(updatedAt); - if (!nullToAbsent || extraData != null) { - map['extra_data'] = - Variable($PollsTable.$converterextraDatan.toSql(extraData)); - } - return map; - } - - factory PollEntity.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return PollEntity( - id: serializer.fromJson(json['id']), - name: serializer.fromJson(json['name']), - description: serializer.fromJson(json['description']), - options: serializer.fromJson>(json['options']), - votingVisibility: - serializer.fromJson(json['votingVisibility']), - enforceUniqueVote: serializer.fromJson(json['enforceUniqueVote']), - maxVotesAllowed: serializer.fromJson(json['maxVotesAllowed']), - allowUserSuggestedOptions: - serializer.fromJson(json['allowUserSuggestedOptions']), - allowAnswers: serializer.fromJson(json['allowAnswers']), - isClosed: serializer.fromJson(json['isClosed']), - answersCount: serializer.fromJson(json['answersCount']), - voteCountsByOption: - serializer.fromJson>(json['voteCountsByOption']), - voteCount: serializer.fromJson(json['voteCount']), - createdById: serializer.fromJson(json['createdById']), - createdAt: serializer.fromJson(json['createdAt']), - updatedAt: serializer.fromJson(json['updatedAt']), - extraData: serializer.fromJson?>(json['extraData']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'id': serializer.toJson(id), - 'name': serializer.toJson(name), - 'description': serializer.toJson(description), - 'options': serializer.toJson>(options), - 'votingVisibility': serializer.toJson(votingVisibility), - 'enforceUniqueVote': serializer.toJson(enforceUniqueVote), - 'maxVotesAllowed': serializer.toJson(maxVotesAllowed), - 'allowUserSuggestedOptions': - serializer.toJson(allowUserSuggestedOptions), - 'allowAnswers': serializer.toJson(allowAnswers), - 'isClosed': serializer.toJson(isClosed), - 'answersCount': serializer.toJson(answersCount), - 'voteCountsByOption': - serializer.toJson>(voteCountsByOption), - 'voteCount': serializer.toJson(voteCount), - 'createdById': serializer.toJson(createdById), - 'createdAt': serializer.toJson(createdAt), - 'updatedAt': serializer.toJson(updatedAt), - 'extraData': serializer.toJson?>(extraData), - }; - } - - PollEntity copyWith( - {String? id, - String? name, - Value description = const Value.absent(), - List? options, - VotingVisibility? votingVisibility, - bool? enforceUniqueVote, - Value maxVotesAllowed = const Value.absent(), - bool? allowUserSuggestedOptions, - bool? allowAnswers, - bool? isClosed, - int? answersCount, - Map? voteCountsByOption, - int? voteCount, - Value createdById = const Value.absent(), - DateTime? createdAt, - DateTime? updatedAt, - Value?> extraData = const Value.absent()}) => - PollEntity( - id: id ?? this.id, - name: name ?? this.name, - description: description.present ? description.value : this.description, - options: options ?? this.options, - votingVisibility: votingVisibility ?? this.votingVisibility, - enforceUniqueVote: enforceUniqueVote ?? this.enforceUniqueVote, - maxVotesAllowed: maxVotesAllowed.present - ? maxVotesAllowed.value - : this.maxVotesAllowed, - allowUserSuggestedOptions: - allowUserSuggestedOptions ?? this.allowUserSuggestedOptions, - allowAnswers: allowAnswers ?? this.allowAnswers, - isClosed: isClosed ?? this.isClosed, - answersCount: answersCount ?? this.answersCount, - voteCountsByOption: voteCountsByOption ?? this.voteCountsByOption, - voteCount: voteCount ?? this.voteCount, - createdById: createdById.present ? createdById.value : this.createdById, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - extraData: extraData.present ? extraData.value : this.extraData, - ); - PollEntity copyWithCompanion(PollsCompanion data) { - return PollEntity( - id: data.id.present ? data.id.value : this.id, - name: data.name.present ? data.name.value : this.name, - description: - data.description.present ? data.description.value : this.description, - options: data.options.present ? data.options.value : this.options, - votingVisibility: data.votingVisibility.present - ? data.votingVisibility.value - : this.votingVisibility, - enforceUniqueVote: data.enforceUniqueVote.present - ? data.enforceUniqueVote.value - : this.enforceUniqueVote, - maxVotesAllowed: data.maxVotesAllowed.present - ? data.maxVotesAllowed.value - : this.maxVotesAllowed, - allowUserSuggestedOptions: data.allowUserSuggestedOptions.present - ? data.allowUserSuggestedOptions.value - : this.allowUserSuggestedOptions, - allowAnswers: data.allowAnswers.present - ? data.allowAnswers.value - : this.allowAnswers, - isClosed: data.isClosed.present ? data.isClosed.value : this.isClosed, - answersCount: data.answersCount.present - ? data.answersCount.value - : this.answersCount, - voteCountsByOption: data.voteCountsByOption.present - ? data.voteCountsByOption.value - : this.voteCountsByOption, - voteCount: data.voteCount.present ? data.voteCount.value : this.voteCount, - createdById: - data.createdById.present ? data.createdById.value : this.createdById, - createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, - updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - extraData: data.extraData.present ? data.extraData.value : this.extraData, - ); - } - - @override - String toString() { - return (StringBuffer('PollEntity(') - ..write('id: $id, ') - ..write('name: $name, ') - ..write('description: $description, ') - ..write('options: $options, ') - ..write('votingVisibility: $votingVisibility, ') - ..write('enforceUniqueVote: $enforceUniqueVote, ') - ..write('maxVotesAllowed: $maxVotesAllowed, ') - ..write('allowUserSuggestedOptions: $allowUserSuggestedOptions, ') - ..write('allowAnswers: $allowAnswers, ') - ..write('isClosed: $isClosed, ') - ..write('answersCount: $answersCount, ') - ..write('voteCountsByOption: $voteCountsByOption, ') - ..write('voteCount: $voteCount, ') - ..write('createdById: $createdById, ') - ..write('createdAt: $createdAt, ') - ..write('updatedAt: $updatedAt, ') - ..write('extraData: $extraData') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash( - id, - name, - description, - options, - votingVisibility, - enforceUniqueVote, - maxVotesAllowed, - allowUserSuggestedOptions, - allowAnswers, - isClosed, - answersCount, - voteCountsByOption, - voteCount, - createdById, - createdAt, - updatedAt, - extraData); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is PollEntity && - other.id == this.id && - other.name == this.name && - other.description == this.description && - other.options == this.options && - other.votingVisibility == this.votingVisibility && - other.enforceUniqueVote == this.enforceUniqueVote && - other.maxVotesAllowed == this.maxVotesAllowed && - other.allowUserSuggestedOptions == this.allowUserSuggestedOptions && - other.allowAnswers == this.allowAnswers && - other.isClosed == this.isClosed && - other.answersCount == this.answersCount && - other.voteCountsByOption == this.voteCountsByOption && - other.voteCount == this.voteCount && - other.createdById == this.createdById && - other.createdAt == this.createdAt && - other.updatedAt == this.updatedAt && - other.extraData == this.extraData); -} - -class PollsCompanion extends UpdateCompanion { - final Value id; - final Value name; - final Value description; - final Value> options; - final Value votingVisibility; - final Value enforceUniqueVote; - final Value maxVotesAllowed; - final Value allowUserSuggestedOptions; - final Value allowAnswers; - final Value isClosed; - final Value answersCount; - final Value> voteCountsByOption; - final Value voteCount; - final Value createdById; - final Value createdAt; - final Value updatedAt; - final Value?> extraData; - final Value rowid; - const PollsCompanion({ - this.id = const Value.absent(), - this.name = const Value.absent(), - this.description = const Value.absent(), - this.options = const Value.absent(), - this.votingVisibility = const Value.absent(), - this.enforceUniqueVote = const Value.absent(), - this.maxVotesAllowed = const Value.absent(), - this.allowUserSuggestedOptions = const Value.absent(), - this.allowAnswers = const Value.absent(), - this.isClosed = const Value.absent(), - this.answersCount = const Value.absent(), - this.voteCountsByOption = const Value.absent(), - this.voteCount = const Value.absent(), - this.createdById = const Value.absent(), - this.createdAt = const Value.absent(), - this.updatedAt = const Value.absent(), - this.extraData = const Value.absent(), - this.rowid = const Value.absent(), - }); - PollsCompanion.insert({ - required String id, - required String name, - this.description = const Value.absent(), - required List options, - this.votingVisibility = const Value.absent(), - this.enforceUniqueVote = const Value.absent(), - this.maxVotesAllowed = const Value.absent(), - this.allowUserSuggestedOptions = const Value.absent(), - this.allowAnswers = const Value.absent(), - this.isClosed = const Value.absent(), - this.answersCount = const Value.absent(), - required Map voteCountsByOption, - this.voteCount = const Value.absent(), - this.createdById = const Value.absent(), - this.createdAt = const Value.absent(), - this.updatedAt = const Value.absent(), - this.extraData = const Value.absent(), - this.rowid = const Value.absent(), - }) : id = Value(id), - name = Value(name), - options = Value(options), - voteCountsByOption = Value(voteCountsByOption); - static Insertable custom({ - Expression? id, - Expression? name, - Expression? description, - Expression? options, - Expression? votingVisibility, - Expression? enforceUniqueVote, - Expression? maxVotesAllowed, - Expression? allowUserSuggestedOptions, - Expression? allowAnswers, - Expression? isClosed, - Expression? answersCount, - Expression? voteCountsByOption, - Expression? voteCount, - Expression? createdById, - Expression? createdAt, - Expression? updatedAt, - Expression? extraData, - Expression? rowid, - }) { - return RawValuesInsertable({ - if (id != null) 'id': id, - if (name != null) 'name': name, - if (description != null) 'description': description, - if (options != null) 'options': options, - if (votingVisibility != null) 'voting_visibility': votingVisibility, - if (enforceUniqueVote != null) 'enforce_unique_vote': enforceUniqueVote, - if (maxVotesAllowed != null) 'max_votes_allowed': maxVotesAllowed, - if (allowUserSuggestedOptions != null) - 'allow_user_suggested_options': allowUserSuggestedOptions, - if (allowAnswers != null) 'allow_answers': allowAnswers, - if (isClosed != null) 'is_closed': isClosed, - if (answersCount != null) 'answers_count': answersCount, - if (voteCountsByOption != null) - 'vote_counts_by_option': voteCountsByOption, - if (voteCount != null) 'vote_count': voteCount, - if (createdById != null) 'created_by_id': createdById, - if (createdAt != null) 'created_at': createdAt, - if (updatedAt != null) 'updated_at': updatedAt, - if (extraData != null) 'extra_data': extraData, - if (rowid != null) 'rowid': rowid, - }); - } - - PollsCompanion copyWith( - {Value? id, - Value? name, - Value? description, - Value>? options, - Value? votingVisibility, - Value? enforceUniqueVote, - Value? maxVotesAllowed, - Value? allowUserSuggestedOptions, - Value? allowAnswers, - Value? isClosed, - Value? answersCount, - Value>? voteCountsByOption, - Value? voteCount, - Value? createdById, - Value? createdAt, - Value? updatedAt, - Value?>? extraData, - Value? rowid}) { - return PollsCompanion( - id: id ?? this.id, - name: name ?? this.name, - description: description ?? this.description, - options: options ?? this.options, - votingVisibility: votingVisibility ?? this.votingVisibility, - enforceUniqueVote: enforceUniqueVote ?? this.enforceUniqueVote, - maxVotesAllowed: maxVotesAllowed ?? this.maxVotesAllowed, - allowUserSuggestedOptions: - allowUserSuggestedOptions ?? this.allowUserSuggestedOptions, - allowAnswers: allowAnswers ?? this.allowAnswers, - isClosed: isClosed ?? this.isClosed, - answersCount: answersCount ?? this.answersCount, - voteCountsByOption: voteCountsByOption ?? this.voteCountsByOption, - voteCount: voteCount ?? this.voteCount, - createdById: createdById ?? this.createdById, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - extraData: extraData ?? this.extraData, - rowid: rowid ?? this.rowid, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (id.present) { - map['id'] = Variable(id.value); - } - if (name.present) { - map['name'] = Variable(name.value); - } - if (description.present) { - map['description'] = Variable(description.value); - } - if (options.present) { - map['options'] = - Variable($PollsTable.$converteroptions.toSql(options.value)); - } - if (votingVisibility.present) { - map['voting_visibility'] = Variable( - $PollsTable.$convertervotingVisibility.toSql(votingVisibility.value)); - } - if (enforceUniqueVote.present) { - map['enforce_unique_vote'] = Variable(enforceUniqueVote.value); - } - if (maxVotesAllowed.present) { - map['max_votes_allowed'] = Variable(maxVotesAllowed.value); - } - if (allowUserSuggestedOptions.present) { - map['allow_user_suggested_options'] = - Variable(allowUserSuggestedOptions.value); - } - if (allowAnswers.present) { - map['allow_answers'] = Variable(allowAnswers.value); - } - if (isClosed.present) { - map['is_closed'] = Variable(isClosed.value); - } - if (answersCount.present) { - map['answers_count'] = Variable(answersCount.value); - } - if (voteCountsByOption.present) { - map['vote_counts_by_option'] = Variable($PollsTable - .$convertervoteCountsByOption - .toSql(voteCountsByOption.value)); - } - if (voteCount.present) { - map['vote_count'] = Variable(voteCount.value); - } - if (createdById.present) { - map['created_by_id'] = Variable(createdById.value); - } - if (createdAt.present) { - map['created_at'] = Variable(createdAt.value); - } - if (updatedAt.present) { - map['updated_at'] = Variable(updatedAt.value); - } - if (extraData.present) { - map['extra_data'] = Variable( - $PollsTable.$converterextraDatan.toSql(extraData.value)); - } - if (rowid.present) { - map['rowid'] = Variable(rowid.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('PollsCompanion(') - ..write('id: $id, ') - ..write('name: $name, ') - ..write('description: $description, ') - ..write('options: $options, ') - ..write('votingVisibility: $votingVisibility, ') - ..write('enforceUniqueVote: $enforceUniqueVote, ') - ..write('maxVotesAllowed: $maxVotesAllowed, ') - ..write('allowUserSuggestedOptions: $allowUserSuggestedOptions, ') - ..write('allowAnswers: $allowAnswers, ') - ..write('isClosed: $isClosed, ') - ..write('answersCount: $answersCount, ') - ..write('voteCountsByOption: $voteCountsByOption, ') - ..write('voteCount: $voteCount, ') - ..write('createdById: $createdById, ') - ..write('createdAt: $createdAt, ') - ..write('updatedAt: $updatedAt, ') - ..write('extraData: $extraData, ') - ..write('rowid: $rowid') - ..write(')')) - .toString(); - } -} - -class $PollVotesTable extends PollVotes - with TableInfo<$PollVotesTable, PollVoteEntity> { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - $PollVotesTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _idMeta = const VerificationMeta('id'); - @override - late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _pollIdMeta = const VerificationMeta('pollId'); - @override - late final GeneratedColumn pollId = GeneratedColumn( - 'poll_id', aliasedName, true, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES polls (id) ON DELETE CASCADE')); - static const VerificationMeta _optionIdMeta = - const VerificationMeta('optionId'); - @override - late final GeneratedColumn optionId = GeneratedColumn( - 'option_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _answerTextMeta = - const VerificationMeta('answerText'); - @override - late final GeneratedColumn answerText = GeneratedColumn( - 'answer_text', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _createdAtMeta = - const VerificationMeta('createdAt'); - @override - late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: currentDateAndTime); - static const VerificationMeta _updatedAtMeta = - const VerificationMeta('updatedAt'); - @override - late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: currentDateAndTime); - static const VerificationMeta _userIdMeta = const VerificationMeta('userId'); - @override - late final GeneratedColumn userId = GeneratedColumn( - 'user_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - @override - List get $columns => - [id, pollId, optionId, answerText, createdAt, updatedAt, userId]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'poll_votes'; - @override - VerificationContext validateIntegrity(Insertable instance, - {bool isInserting = false}) { - final context = VerificationContext(); - final data = instance.toColumns(true); - if (data.containsKey('id')) { - context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); - } - if (data.containsKey('poll_id')) { - context.handle(_pollIdMeta, - pollId.isAcceptableOrUnknown(data['poll_id']!, _pollIdMeta)); - } - if (data.containsKey('option_id')) { - context.handle(_optionIdMeta, - optionId.isAcceptableOrUnknown(data['option_id']!, _optionIdMeta)); - } - if (data.containsKey('answer_text')) { - context.handle( - _answerTextMeta, - answerText.isAcceptableOrUnknown( - data['answer_text']!, _answerTextMeta)); - } - if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); - } - if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); - } - if (data.containsKey('user_id')) { - context.handle(_userIdMeta, - userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); - } - return context; - } - - @override - Set get $primaryKey => {id, pollId}; - @override - PollVoteEntity map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return PollVoteEntity( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id']), - pollId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}poll_id']), - optionId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}option_id']), - answerText: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}answer_text']), - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - userId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}user_id']), - ); - } - - @override - $PollVotesTable createAlias(String alias) { - return $PollVotesTable(attachedDatabase, alias); - } -} - -class PollVoteEntity extends DataClass implements Insertable { - /// The unique identifier of the poll vote. - final String? id; - - /// The unique identifier of the poll the vote belongs to. - final String? pollId; - - /// The unique identifier of the option selected in the poll. - /// - /// Nullable if the user provided an answer. - final String? optionId; - - /// The text of the answer provided in the poll. - /// - /// Nullable if the user selected an option. - final String? answerText; - - /// The date when the poll vote was created. - final DateTime createdAt; - - /// The date when the poll vote was last updated. - final DateTime updatedAt; - - /// The unique identifier of the user who voted. - /// - /// Nullable if the poll is anonymous. - final String? userId; - const PollVoteEntity( - {this.id, - this.pollId, - this.optionId, - this.answerText, - required this.createdAt, - required this.updatedAt, - this.userId}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (!nullToAbsent || id != null) { - map['id'] = Variable(id); - } - if (!nullToAbsent || pollId != null) { - map['poll_id'] = Variable(pollId); - } - if (!nullToAbsent || optionId != null) { - map['option_id'] = Variable(optionId); - } - if (!nullToAbsent || answerText != null) { - map['answer_text'] = Variable(answerText); - } - map['created_at'] = Variable(createdAt); - map['updated_at'] = Variable(updatedAt); - if (!nullToAbsent || userId != null) { - map['user_id'] = Variable(userId); - } - return map; - } - - factory PollVoteEntity.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return PollVoteEntity( - id: serializer.fromJson(json['id']), - pollId: serializer.fromJson(json['pollId']), - optionId: serializer.fromJson(json['optionId']), - answerText: serializer.fromJson(json['answerText']), - createdAt: serializer.fromJson(json['createdAt']), - updatedAt: serializer.fromJson(json['updatedAt']), - userId: serializer.fromJson(json['userId']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'id': serializer.toJson(id), - 'pollId': serializer.toJson(pollId), - 'optionId': serializer.toJson(optionId), - 'answerText': serializer.toJson(answerText), - 'createdAt': serializer.toJson(createdAt), - 'updatedAt': serializer.toJson(updatedAt), - 'userId': serializer.toJson(userId), - }; - } - - PollVoteEntity copyWith( - {Value id = const Value.absent(), - Value pollId = const Value.absent(), - Value optionId = const Value.absent(), - Value answerText = const Value.absent(), - DateTime? createdAt, - DateTime? updatedAt, - Value userId = const Value.absent()}) => - PollVoteEntity( - id: id.present ? id.value : this.id, - pollId: pollId.present ? pollId.value : this.pollId, - optionId: optionId.present ? optionId.value : this.optionId, - answerText: answerText.present ? answerText.value : this.answerText, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - userId: userId.present ? userId.value : this.userId, - ); - PollVoteEntity copyWithCompanion(PollVotesCompanion data) { - return PollVoteEntity( - id: data.id.present ? data.id.value : this.id, - pollId: data.pollId.present ? data.pollId.value : this.pollId, - optionId: data.optionId.present ? data.optionId.value : this.optionId, - answerText: - data.answerText.present ? data.answerText.value : this.answerText, - createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, - updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - userId: data.userId.present ? data.userId.value : this.userId, - ); - } - - @override - String toString() { - return (StringBuffer('PollVoteEntity(') - ..write('id: $id, ') - ..write('pollId: $pollId, ') - ..write('optionId: $optionId, ') - ..write('answerText: $answerText, ') - ..write('createdAt: $createdAt, ') - ..write('updatedAt: $updatedAt, ') - ..write('userId: $userId') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash( - id, pollId, optionId, answerText, createdAt, updatedAt, userId); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is PollVoteEntity && - other.id == this.id && - other.pollId == this.pollId && - other.optionId == this.optionId && - other.answerText == this.answerText && - other.createdAt == this.createdAt && - other.updatedAt == this.updatedAt && - other.userId == this.userId); -} - -class PollVotesCompanion extends UpdateCompanion { - final Value id; - final Value pollId; - final Value optionId; - final Value answerText; - final Value createdAt; - final Value updatedAt; - final Value userId; - final Value rowid; - const PollVotesCompanion({ - this.id = const Value.absent(), - this.pollId = const Value.absent(), - this.optionId = const Value.absent(), - this.answerText = const Value.absent(), - this.createdAt = const Value.absent(), - this.updatedAt = const Value.absent(), - this.userId = const Value.absent(), - this.rowid = const Value.absent(), - }); - PollVotesCompanion.insert({ - this.id = const Value.absent(), - this.pollId = const Value.absent(), - this.optionId = const Value.absent(), - this.answerText = const Value.absent(), - this.createdAt = const Value.absent(), - this.updatedAt = const Value.absent(), - this.userId = const Value.absent(), - this.rowid = const Value.absent(), - }); - static Insertable custom({ - Expression? id, - Expression? pollId, - Expression? optionId, - Expression? answerText, - Expression? createdAt, - Expression? updatedAt, - Expression? userId, - Expression? rowid, - }) { - return RawValuesInsertable({ - if (id != null) 'id': id, - if (pollId != null) 'poll_id': pollId, - if (optionId != null) 'option_id': optionId, - if (answerText != null) 'answer_text': answerText, - if (createdAt != null) 'created_at': createdAt, - if (updatedAt != null) 'updated_at': updatedAt, - if (userId != null) 'user_id': userId, - if (rowid != null) 'rowid': rowid, - }); - } - - PollVotesCompanion copyWith( - {Value? id, - Value? pollId, - Value? optionId, - Value? answerText, - Value? createdAt, - Value? updatedAt, - Value? userId, - Value? rowid}) { - return PollVotesCompanion( - id: id ?? this.id, - pollId: pollId ?? this.pollId, - optionId: optionId ?? this.optionId, - answerText: answerText ?? this.answerText, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - userId: userId ?? this.userId, - rowid: rowid ?? this.rowid, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (id.present) { - map['id'] = Variable(id.value); - } - if (pollId.present) { - map['poll_id'] = Variable(pollId.value); - } - if (optionId.present) { - map['option_id'] = Variable(optionId.value); - } - if (answerText.present) { - map['answer_text'] = Variable(answerText.value); - } - if (createdAt.present) { - map['created_at'] = Variable(createdAt.value); - } - if (updatedAt.present) { - map['updated_at'] = Variable(updatedAt.value); - } - if (userId.present) { - map['user_id'] = Variable(userId.value); - } - if (rowid.present) { - map['rowid'] = Variable(rowid.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('PollVotesCompanion(') - ..write('id: $id, ') - ..write('pollId: $pollId, ') - ..write('optionId: $optionId, ') - ..write('answerText: $answerText, ') - ..write('createdAt: $createdAt, ') - ..write('updatedAt: $updatedAt, ') - ..write('userId: $userId, ') - ..write('rowid: $rowid') - ..write(')')) - .toString(); - } -} - -class $PinnedMessageReactionsTable extends PinnedMessageReactions - with TableInfo<$PinnedMessageReactionsTable, PinnedMessageReactionEntity> { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - $PinnedMessageReactionsTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _userIdMeta = const VerificationMeta('userId'); - @override - late final GeneratedColumn userId = GeneratedColumn( - 'user_id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - static const VerificationMeta _messageIdMeta = - const VerificationMeta('messageId'); - @override - late final GeneratedColumn messageId = GeneratedColumn( - 'message_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES pinned_messages (id) ON DELETE CASCADE')); - static const VerificationMeta _typeMeta = const VerificationMeta('type'); - @override - late final GeneratedColumn type = GeneratedColumn( - 'type', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - static const VerificationMeta _createdAtMeta = - const VerificationMeta('createdAt'); - @override - late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: currentDateAndTime); - static const VerificationMeta _scoreMeta = const VerificationMeta('score'); - @override - late final GeneratedColumn score = GeneratedColumn( - 'score', aliasedName, false, - type: DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const Constant(0)); - static const VerificationMeta _extraDataMeta = - const VerificationMeta('extraData'); - @override - late final GeneratedColumnWithTypeConverter?, String> - extraData = GeneratedColumn('extra_data', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false) - .withConverter?>( - $PinnedMessageReactionsTable.$converterextraDatan); - @override - List get $columns => - [userId, messageId, type, createdAt, score, extraData]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'pinned_message_reactions'; - @override - VerificationContext validateIntegrity( - Insertable instance, - {bool isInserting = false}) { - final context = VerificationContext(); - final data = instance.toColumns(true); - if (data.containsKey('user_id')) { - context.handle(_userIdMeta, - userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); - } else if (isInserting) { - context.missing(_userIdMeta); - } - if (data.containsKey('message_id')) { - context.handle(_messageIdMeta, - messageId.isAcceptableOrUnknown(data['message_id']!, _messageIdMeta)); - } else if (isInserting) { - context.missing(_messageIdMeta); - } - if (data.containsKey('type')) { - context.handle( - _typeMeta, type.isAcceptableOrUnknown(data['type']!, _typeMeta)); - } else if (isInserting) { - context.missing(_typeMeta); - } - if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); - } - if (data.containsKey('score')) { - context.handle( - _scoreMeta, score.isAcceptableOrUnknown(data['score']!, _scoreMeta)); - } - context.handle(_extraDataMeta, const VerificationResult.success()); - return context; - } - - @override - Set get $primaryKey => {messageId, type, userId}; - @override - PinnedMessageReactionEntity map(Map data, - {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return PinnedMessageReactionEntity( - userId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - messageId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}message_id'])!, - type: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - score: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}score'])!, - extraData: $PinnedMessageReactionsTable.$converterextraDatan.fromSql( - attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}extra_data'])), - ); - } - - @override - $PinnedMessageReactionsTable createAlias(String alias) { - return $PinnedMessageReactionsTable(attachedDatabase, alias); - } - - static TypeConverter, String> $converterextraData = - MapConverter(); - static TypeConverter?, String?> $converterextraDatan = - NullAwareTypeConverter.wrap($converterextraData); -} - -class PinnedMessageReactionEntity extends DataClass - implements Insertable { - /// The id of the user that sent the reaction - final String userId; - - /// The messageId to which the reaction belongs - final String messageId; - - /// The type of the reaction - final String type; - - /// The DateTime on which the reaction is created - final DateTime createdAt; - - /// The score of the reaction (ie. number of reactions sent) - final int score; - - /// Reaction custom extraData - final Map? extraData; - const PinnedMessageReactionEntity( - {required this.userId, - required this.messageId, - required this.type, - required this.createdAt, - required this.score, - this.extraData}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['user_id'] = Variable(userId); - map['message_id'] = Variable(messageId); - map['type'] = Variable(type); - map['created_at'] = Variable(createdAt); - map['score'] = Variable(score); - if (!nullToAbsent || extraData != null) { - map['extra_data'] = Variable( - $PinnedMessageReactionsTable.$converterextraDatan.toSql(extraData)); - } - return map; - } - - factory PinnedMessageReactionEntity.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return PinnedMessageReactionEntity( - userId: serializer.fromJson(json['userId']), - messageId: serializer.fromJson(json['messageId']), - type: serializer.fromJson(json['type']), - createdAt: serializer.fromJson(json['createdAt']), - score: serializer.fromJson(json['score']), - extraData: serializer.fromJson?>(json['extraData']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'userId': serializer.toJson(userId), - 'messageId': serializer.toJson(messageId), - 'type': serializer.toJson(type), - 'createdAt': serializer.toJson(createdAt), - 'score': serializer.toJson(score), - 'extraData': serializer.toJson?>(extraData), - }; - } - - PinnedMessageReactionEntity copyWith( - {String? userId, - String? messageId, - String? type, - DateTime? createdAt, - int? score, - Value?> extraData = const Value.absent()}) => - PinnedMessageReactionEntity( - userId: userId ?? this.userId, - messageId: messageId ?? this.messageId, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - score: score ?? this.score, - extraData: extraData.present ? extraData.value : this.extraData, - ); - PinnedMessageReactionEntity copyWithCompanion( - PinnedMessageReactionsCompanion data) { - return PinnedMessageReactionEntity( - userId: data.userId.present ? data.userId.value : this.userId, - messageId: data.messageId.present ? data.messageId.value : this.messageId, - type: data.type.present ? data.type.value : this.type, - createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, - score: data.score.present ? data.score.value : this.score, - extraData: data.extraData.present ? data.extraData.value : this.extraData, - ); - } - - @override - String toString() { - return (StringBuffer('PinnedMessageReactionEntity(') - ..write('userId: $userId, ') - ..write('messageId: $messageId, ') - ..write('type: $type, ') - ..write('createdAt: $createdAt, ') - ..write('score: $score, ') - ..write('extraData: $extraData') - ..write(')')) - .toString(); - } - - @override - int get hashCode => - Object.hash(userId, messageId, type, createdAt, score, extraData); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is PinnedMessageReactionEntity && - other.userId == this.userId && - other.messageId == this.messageId && - other.type == this.type && - other.createdAt == this.createdAt && - other.score == this.score && - other.extraData == this.extraData); -} - -class PinnedMessageReactionsCompanion - extends UpdateCompanion { - final Value userId; - final Value messageId; - final Value type; - final Value createdAt; - final Value score; - final Value?> extraData; - final Value rowid; - const PinnedMessageReactionsCompanion({ - this.userId = const Value.absent(), - this.messageId = const Value.absent(), - this.type = const Value.absent(), - this.createdAt = const Value.absent(), - this.score = const Value.absent(), - this.extraData = const Value.absent(), - this.rowid = const Value.absent(), - }); - PinnedMessageReactionsCompanion.insert({ - required String userId, - required String messageId, - required String type, - this.createdAt = const Value.absent(), - this.score = const Value.absent(), - this.extraData = const Value.absent(), - this.rowid = const Value.absent(), - }) : userId = Value(userId), - messageId = Value(messageId), - type = Value(type); - static Insertable custom({ - Expression? userId, - Expression? messageId, - Expression? type, - Expression? createdAt, - Expression? score, - Expression? extraData, - Expression? rowid, - }) { - return RawValuesInsertable({ - if (userId != null) 'user_id': userId, - if (messageId != null) 'message_id': messageId, - if (type != null) 'type': type, - if (createdAt != null) 'created_at': createdAt, - if (score != null) 'score': score, - if (extraData != null) 'extra_data': extraData, - if (rowid != null) 'rowid': rowid, - }); - } - - PinnedMessageReactionsCompanion copyWith( - {Value? userId, - Value? messageId, - Value? type, - Value? createdAt, - Value? score, - Value?>? extraData, - Value? rowid}) { - return PinnedMessageReactionsCompanion( - userId: userId ?? this.userId, - messageId: messageId ?? this.messageId, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - score: score ?? this.score, - extraData: extraData ?? this.extraData, - rowid: rowid ?? this.rowid, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (userId.present) { - map['user_id'] = Variable(userId.value); - } - if (messageId.present) { - map['message_id'] = Variable(messageId.value); - } - if (type.present) { - map['type'] = Variable(type.value); - } - if (createdAt.present) { - map['created_at'] = Variable(createdAt.value); - } - if (score.present) { - map['score'] = Variable(score.value); - } - if (extraData.present) { - map['extra_data'] = Variable($PinnedMessageReactionsTable - .$converterextraDatan - .toSql(extraData.value)); - } - if (rowid.present) { - map['rowid'] = Variable(rowid.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('PinnedMessageReactionsCompanion(') - ..write('userId: $userId, ') - ..write('messageId: $messageId, ') - ..write('type: $type, ') - ..write('createdAt: $createdAt, ') - ..write('score: $score, ') - ..write('extraData: $extraData, ') - ..write('rowid: $rowid') - ..write(')')) - .toString(); - } -} - -class $ReactionsTable extends Reactions - with TableInfo<$ReactionsTable, ReactionEntity> { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - $ReactionsTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _userIdMeta = const VerificationMeta('userId'); - @override - late final GeneratedColumn userId = GeneratedColumn( - 'user_id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - static const VerificationMeta _messageIdMeta = - const VerificationMeta('messageId'); - @override - late final GeneratedColumn messageId = GeneratedColumn( - 'message_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES messages (id) ON DELETE CASCADE')); - static const VerificationMeta _typeMeta = const VerificationMeta('type'); - @override - late final GeneratedColumn type = GeneratedColumn( - 'type', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - static const VerificationMeta _createdAtMeta = - const VerificationMeta('createdAt'); - @override - late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: currentDateAndTime); - static const VerificationMeta _scoreMeta = const VerificationMeta('score'); - @override - late final GeneratedColumn score = GeneratedColumn( - 'score', aliasedName, false, - type: DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const Constant(0)); - static const VerificationMeta _extraDataMeta = - const VerificationMeta('extraData'); - @override - late final GeneratedColumnWithTypeConverter?, String> - extraData = GeneratedColumn('extra_data', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false) - .withConverter?>( - $ReactionsTable.$converterextraDatan); - @override - List get $columns => - [userId, messageId, type, createdAt, score, extraData]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'reactions'; - @override - VerificationContext validateIntegrity(Insertable instance, - {bool isInserting = false}) { - final context = VerificationContext(); - final data = instance.toColumns(true); - if (data.containsKey('user_id')) { - context.handle(_userIdMeta, - userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); - } else if (isInserting) { - context.missing(_userIdMeta); - } - if (data.containsKey('message_id')) { - context.handle(_messageIdMeta, - messageId.isAcceptableOrUnknown(data['message_id']!, _messageIdMeta)); - } else if (isInserting) { - context.missing(_messageIdMeta); - } - if (data.containsKey('type')) { - context.handle( - _typeMeta, type.isAcceptableOrUnknown(data['type']!, _typeMeta)); - } else if (isInserting) { - context.missing(_typeMeta); - } - if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); - } - if (data.containsKey('score')) { - context.handle( - _scoreMeta, score.isAcceptableOrUnknown(data['score']!, _scoreMeta)); - } - context.handle(_extraDataMeta, const VerificationResult.success()); - return context; - } - - @override - Set get $primaryKey => {messageId, type, userId}; - @override - ReactionEntity map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return ReactionEntity( - userId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - messageId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}message_id'])!, - type: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - score: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}score'])!, - extraData: $ReactionsTable.$converterextraDatan.fromSql(attachedDatabase - .typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}extra_data'])), - ); - } - - @override - $ReactionsTable createAlias(String alias) { - return $ReactionsTable(attachedDatabase, alias); - } - - static TypeConverter, String> $converterextraData = - MapConverter(); - static TypeConverter?, String?> $converterextraDatan = - NullAwareTypeConverter.wrap($converterextraData); -} - -class ReactionEntity extends DataClass implements Insertable { - /// The id of the user that sent the reaction - final String userId; - - /// The messageId to which the reaction belongs - final String messageId; - - /// The type of the reaction - final String type; - - /// The DateTime on which the reaction is created - final DateTime createdAt; - - /// The score of the reaction (ie. number of reactions sent) - final int score; - - /// Reaction custom extraData - final Map? extraData; - const ReactionEntity( - {required this.userId, - required this.messageId, - required this.type, - required this.createdAt, - required this.score, - this.extraData}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['user_id'] = Variable(userId); - map['message_id'] = Variable(messageId); - map['type'] = Variable(type); - map['created_at'] = Variable(createdAt); - map['score'] = Variable(score); - if (!nullToAbsent || extraData != null) { - map['extra_data'] = Variable( - $ReactionsTable.$converterextraDatan.toSql(extraData)); - } - return map; - } - - factory ReactionEntity.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return ReactionEntity( - userId: serializer.fromJson(json['userId']), - messageId: serializer.fromJson(json['messageId']), - type: serializer.fromJson(json['type']), - createdAt: serializer.fromJson(json['createdAt']), - score: serializer.fromJson(json['score']), - extraData: serializer.fromJson?>(json['extraData']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'userId': serializer.toJson(userId), - 'messageId': serializer.toJson(messageId), - 'type': serializer.toJson(type), - 'createdAt': serializer.toJson(createdAt), - 'score': serializer.toJson(score), - 'extraData': serializer.toJson?>(extraData), - }; - } - - ReactionEntity copyWith( - {String? userId, - String? messageId, - String? type, - DateTime? createdAt, - int? score, - Value?> extraData = const Value.absent()}) => - ReactionEntity( - userId: userId ?? this.userId, - messageId: messageId ?? this.messageId, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - score: score ?? this.score, - extraData: extraData.present ? extraData.value : this.extraData, - ); - ReactionEntity copyWithCompanion(ReactionsCompanion data) { - return ReactionEntity( - userId: data.userId.present ? data.userId.value : this.userId, - messageId: data.messageId.present ? data.messageId.value : this.messageId, - type: data.type.present ? data.type.value : this.type, - createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, - score: data.score.present ? data.score.value : this.score, - extraData: data.extraData.present ? data.extraData.value : this.extraData, - ); - } - - @override - String toString() { - return (StringBuffer('ReactionEntity(') - ..write('userId: $userId, ') - ..write('messageId: $messageId, ') - ..write('type: $type, ') - ..write('createdAt: $createdAt, ') - ..write('score: $score, ') - ..write('extraData: $extraData') - ..write(')')) - .toString(); - } - - @override - int get hashCode => - Object.hash(userId, messageId, type, createdAt, score, extraData); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is ReactionEntity && - other.userId == this.userId && - other.messageId == this.messageId && - other.type == this.type && - other.createdAt == this.createdAt && - other.score == this.score && - other.extraData == this.extraData); -} - -class ReactionsCompanion extends UpdateCompanion { - final Value userId; - final Value messageId; - final Value type; - final Value createdAt; - final Value score; - final Value?> extraData; - final Value rowid; - const ReactionsCompanion({ - this.userId = const Value.absent(), - this.messageId = const Value.absent(), - this.type = const Value.absent(), - this.createdAt = const Value.absent(), - this.score = const Value.absent(), - this.extraData = const Value.absent(), - this.rowid = const Value.absent(), - }); - ReactionsCompanion.insert({ - required String userId, - required String messageId, - required String type, - this.createdAt = const Value.absent(), - this.score = const Value.absent(), - this.extraData = const Value.absent(), - this.rowid = const Value.absent(), - }) : userId = Value(userId), - messageId = Value(messageId), - type = Value(type); - static Insertable custom({ - Expression? userId, - Expression? messageId, - Expression? type, - Expression? createdAt, - Expression? score, - Expression? extraData, - Expression? rowid, - }) { - return RawValuesInsertable({ - if (userId != null) 'user_id': userId, - if (messageId != null) 'message_id': messageId, - if (type != null) 'type': type, - if (createdAt != null) 'created_at': createdAt, - if (score != null) 'score': score, - if (extraData != null) 'extra_data': extraData, - if (rowid != null) 'rowid': rowid, - }); - } - - ReactionsCompanion copyWith( - {Value? userId, - Value? messageId, - Value? type, - Value? createdAt, - Value? score, - Value?>? extraData, - Value? rowid}) { - return ReactionsCompanion( - userId: userId ?? this.userId, - messageId: messageId ?? this.messageId, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - score: score ?? this.score, - extraData: extraData ?? this.extraData, - rowid: rowid ?? this.rowid, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (userId.present) { - map['user_id'] = Variable(userId.value); - } - if (messageId.present) { - map['message_id'] = Variable(messageId.value); - } - if (type.present) { - map['type'] = Variable(type.value); - } - if (createdAt.present) { - map['created_at'] = Variable(createdAt.value); - } - if (score.present) { - map['score'] = Variable(score.value); - } - if (extraData.present) { - map['extra_data'] = Variable( - $ReactionsTable.$converterextraDatan.toSql(extraData.value)); - } - if (rowid.present) { - map['rowid'] = Variable(rowid.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('ReactionsCompanion(') - ..write('userId: $userId, ') - ..write('messageId: $messageId, ') - ..write('type: $type, ') - ..write('createdAt: $createdAt, ') - ..write('score: $score, ') - ..write('extraData: $extraData, ') - ..write('rowid: $rowid') - ..write(')')) - .toString(); - } -} - -class $UsersTable extends Users with TableInfo<$UsersTable, UserEntity> { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - $UsersTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _idMeta = const VerificationMeta('id'); - @override - late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - static const VerificationMeta _roleMeta = const VerificationMeta('role'); - @override - late final GeneratedColumn role = GeneratedColumn( - 'role', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _languageMeta = - const VerificationMeta('language'); - @override - late final GeneratedColumn language = GeneratedColumn( - 'language', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _createdAtMeta = - const VerificationMeta('createdAt'); - @override - late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _updatedAtMeta = - const VerificationMeta('updatedAt'); - @override - late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _lastActiveMeta = - const VerificationMeta('lastActive'); - @override - late final GeneratedColumn lastActive = GeneratedColumn( - 'last_active', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _onlineMeta = const VerificationMeta('online'); - @override - late final GeneratedColumn online = GeneratedColumn( - 'online', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("online" IN (0, 1))'), - defaultValue: const Constant(false)); - static const VerificationMeta _bannedMeta = const VerificationMeta('banned'); - @override - late final GeneratedColumn banned = GeneratedColumn( - 'banned', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("banned" IN (0, 1))'), - defaultValue: const Constant(false)); - static const VerificationMeta _extraDataMeta = - const VerificationMeta('extraData'); - @override - late final GeneratedColumnWithTypeConverter, String> - extraData = GeneratedColumn('extra_data', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true) - .withConverter>($UsersTable.$converterextraData); - @override - List get $columns => [ - id, - role, - language, - createdAt, - updatedAt, - lastActive, - online, - banned, - extraData - ]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'users'; - @override - VerificationContext validateIntegrity(Insertable instance, - {bool isInserting = false}) { - final context = VerificationContext(); - final data = instance.toColumns(true); - if (data.containsKey('id')) { - context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); - } else if (isInserting) { - context.missing(_idMeta); - } - if (data.containsKey('role')) { - context.handle( - _roleMeta, role.isAcceptableOrUnknown(data['role']!, _roleMeta)); - } - if (data.containsKey('language')) { - context.handle(_languageMeta, - language.isAcceptableOrUnknown(data['language']!, _languageMeta)); - } - if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); - } - if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); - } - if (data.containsKey('last_active')) { - context.handle( - _lastActiveMeta, - lastActive.isAcceptableOrUnknown( - data['last_active']!, _lastActiveMeta)); - } - if (data.containsKey('online')) { - context.handle(_onlineMeta, - online.isAcceptableOrUnknown(data['online']!, _onlineMeta)); - } - if (data.containsKey('banned')) { - context.handle(_bannedMeta, - banned.isAcceptableOrUnknown(data['banned']!, _bannedMeta)); - } - context.handle(_extraDataMeta, const VerificationResult.success()); - return context; - } - - @override - Set get $primaryKey => {id}; - @override - UserEntity map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return UserEntity( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - role: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}role']), - language: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}language']), - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at']), - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at']), - lastActive: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}last_active']), - online: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}online'])!, - banned: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}banned'])!, - extraData: $UsersTable.$converterextraData.fromSql(attachedDatabase - .typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}extra_data'])!), - ); - } - - @override - $UsersTable createAlias(String alias) { - return $UsersTable(attachedDatabase, alias); - } - - static TypeConverter, String> $converterextraData = - MapConverter(); -} - -class UserEntity extends DataClass implements Insertable { - /// User id - final String id; - - /// User role - final String? role; - - /// The language this user prefers. - final String? language; - - /// Date of user creation - final DateTime? createdAt; - - /// Date of last user update - final DateTime? updatedAt; - - /// Date of last user connection - final DateTime? lastActive; - - /// True if user is online - final bool online; - - /// True if user is banned from the chat - final bool banned; - - /// Map of custom user extraData - final Map extraData; - const UserEntity( - {required this.id, - this.role, - this.language, - this.createdAt, - this.updatedAt, - this.lastActive, - required this.online, - required this.banned, - required this.extraData}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['id'] = Variable(id); - if (!nullToAbsent || role != null) { - map['role'] = Variable(role); - } - if (!nullToAbsent || language != null) { - map['language'] = Variable(language); - } - if (!nullToAbsent || createdAt != null) { - map['created_at'] = Variable(createdAt); - } - if (!nullToAbsent || updatedAt != null) { - map['updated_at'] = Variable(updatedAt); - } - if (!nullToAbsent || lastActive != null) { - map['last_active'] = Variable(lastActive); - } - map['online'] = Variable(online); - map['banned'] = Variable(banned); - { - map['extra_data'] = - Variable($UsersTable.$converterextraData.toSql(extraData)); - } - return map; - } - - factory UserEntity.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return UserEntity( - id: serializer.fromJson(json['id']), - role: serializer.fromJson(json['role']), - language: serializer.fromJson(json['language']), - createdAt: serializer.fromJson(json['createdAt']), - updatedAt: serializer.fromJson(json['updatedAt']), - lastActive: serializer.fromJson(json['lastActive']), - online: serializer.fromJson(json['online']), - banned: serializer.fromJson(json['banned']), - extraData: serializer.fromJson>(json['extraData']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'id': serializer.toJson(id), - 'role': serializer.toJson(role), - 'language': serializer.toJson(language), - 'createdAt': serializer.toJson(createdAt), - 'updatedAt': serializer.toJson(updatedAt), - 'lastActive': serializer.toJson(lastActive), - 'online': serializer.toJson(online), - 'banned': serializer.toJson(banned), - 'extraData': serializer.toJson>(extraData), - }; - } - - UserEntity copyWith( - {String? id, - Value role = const Value.absent(), - Value language = const Value.absent(), - Value createdAt = const Value.absent(), - Value updatedAt = const Value.absent(), - Value lastActive = const Value.absent(), - bool? online, - bool? banned, - Map? extraData}) => - UserEntity( - id: id ?? this.id, - role: role.present ? role.value : this.role, - language: language.present ? language.value : this.language, - createdAt: createdAt.present ? createdAt.value : this.createdAt, - updatedAt: updatedAt.present ? updatedAt.value : this.updatedAt, - lastActive: lastActive.present ? lastActive.value : this.lastActive, - online: online ?? this.online, - banned: banned ?? this.banned, - extraData: extraData ?? this.extraData, - ); - UserEntity copyWithCompanion(UsersCompanion data) { - return UserEntity( - id: data.id.present ? data.id.value : this.id, - role: data.role.present ? data.role.value : this.role, - language: data.language.present ? data.language.value : this.language, - createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, - updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - lastActive: - data.lastActive.present ? data.lastActive.value : this.lastActive, - online: data.online.present ? data.online.value : this.online, - banned: data.banned.present ? data.banned.value : this.banned, - extraData: data.extraData.present ? data.extraData.value : this.extraData, - ); - } - - @override - String toString() { - return (StringBuffer('UserEntity(') - ..write('id: $id, ') - ..write('role: $role, ') - ..write('language: $language, ') - ..write('createdAt: $createdAt, ') - ..write('updatedAt: $updatedAt, ') - ..write('lastActive: $lastActive, ') - ..write('online: $online, ') - ..write('banned: $banned, ') - ..write('extraData: $extraData') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash(id, role, language, createdAt, updatedAt, - lastActive, online, banned, extraData); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is UserEntity && - other.id == this.id && - other.role == this.role && - other.language == this.language && - other.createdAt == this.createdAt && - other.updatedAt == this.updatedAt && - other.lastActive == this.lastActive && - other.online == this.online && - other.banned == this.banned && - other.extraData == this.extraData); -} - -class UsersCompanion extends UpdateCompanion { - final Value id; - final Value role; - final Value language; - final Value createdAt; - final Value updatedAt; - final Value lastActive; - final Value online; - final Value banned; - final Value> extraData; - final Value rowid; - const UsersCompanion({ - this.id = const Value.absent(), - this.role = const Value.absent(), - this.language = const Value.absent(), - this.createdAt = const Value.absent(), - this.updatedAt = const Value.absent(), - this.lastActive = const Value.absent(), - this.online = const Value.absent(), - this.banned = const Value.absent(), - this.extraData = const Value.absent(), - this.rowid = const Value.absent(), - }); - UsersCompanion.insert({ - required String id, - this.role = const Value.absent(), - this.language = const Value.absent(), - this.createdAt = const Value.absent(), - this.updatedAt = const Value.absent(), - this.lastActive = const Value.absent(), - this.online = const Value.absent(), - this.banned = const Value.absent(), - required Map extraData, - this.rowid = const Value.absent(), - }) : id = Value(id), - extraData = Value(extraData); - static Insertable custom({ - Expression? id, - Expression? role, - Expression? language, - Expression? createdAt, - Expression? updatedAt, - Expression? lastActive, - Expression? online, - Expression? banned, - Expression? extraData, - Expression? rowid, - }) { - return RawValuesInsertable({ - if (id != null) 'id': id, - if (role != null) 'role': role, - if (language != null) 'language': language, - if (createdAt != null) 'created_at': createdAt, - if (updatedAt != null) 'updated_at': updatedAt, - if (lastActive != null) 'last_active': lastActive, - if (online != null) 'online': online, - if (banned != null) 'banned': banned, - if (extraData != null) 'extra_data': extraData, - if (rowid != null) 'rowid': rowid, - }); - } - - UsersCompanion copyWith( - {Value? id, - Value? role, - Value? language, - Value? createdAt, - Value? updatedAt, - Value? lastActive, - Value? online, - Value? banned, - Value>? extraData, - Value? rowid}) { - return UsersCompanion( - id: id ?? this.id, - role: role ?? this.role, - language: language ?? this.language, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - lastActive: lastActive ?? this.lastActive, - online: online ?? this.online, - banned: banned ?? this.banned, - extraData: extraData ?? this.extraData, - rowid: rowid ?? this.rowid, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (id.present) { - map['id'] = Variable(id.value); - } - if (role.present) { - map['role'] = Variable(role.value); - } - if (language.present) { - map['language'] = Variable(language.value); - } - if (createdAt.present) { - map['created_at'] = Variable(createdAt.value); - } - if (updatedAt.present) { - map['updated_at'] = Variable(updatedAt.value); - } - if (lastActive.present) { - map['last_active'] = Variable(lastActive.value); - } - if (online.present) { - map['online'] = Variable(online.value); - } - if (banned.present) { - map['banned'] = Variable(banned.value); - } - if (extraData.present) { - map['extra_data'] = Variable( - $UsersTable.$converterextraData.toSql(extraData.value)); - } - if (rowid.present) { - map['rowid'] = Variable(rowid.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('UsersCompanion(') - ..write('id: $id, ') - ..write('role: $role, ') - ..write('language: $language, ') - ..write('createdAt: $createdAt, ') - ..write('updatedAt: $updatedAt, ') - ..write('lastActive: $lastActive, ') - ..write('online: $online, ') - ..write('banned: $banned, ') - ..write('extraData: $extraData, ') - ..write('rowid: $rowid') - ..write(')')) - .toString(); - } -} - -class $MembersTable extends Members - with TableInfo<$MembersTable, MemberEntity> { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - $MembersTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _userIdMeta = const VerificationMeta('userId'); - @override - late final GeneratedColumn userId = GeneratedColumn( - 'user_id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - static const VerificationMeta _channelCidMeta = - const VerificationMeta('channelCid'); - @override - late final GeneratedColumn channelCid = GeneratedColumn( - 'channel_cid', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES channels (cid) ON DELETE CASCADE')); - static const VerificationMeta _channelRoleMeta = - const VerificationMeta('channelRole'); - @override - late final GeneratedColumn channelRole = GeneratedColumn( - 'channel_role', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - static const VerificationMeta _inviteAcceptedAtMeta = - const VerificationMeta('inviteAcceptedAt'); - @override - late final GeneratedColumn inviteAcceptedAt = - GeneratedColumn('invite_accepted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _inviteRejectedAtMeta = - const VerificationMeta('inviteRejectedAt'); - @override - late final GeneratedColumn inviteRejectedAt = - GeneratedColumn('invite_rejected_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _invitedMeta = - const VerificationMeta('invited'); - @override - late final GeneratedColumn invited = GeneratedColumn( - 'invited', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("invited" IN (0, 1))'), - defaultValue: const Constant(false)); - static const VerificationMeta _bannedMeta = const VerificationMeta('banned'); - @override - late final GeneratedColumn banned = GeneratedColumn( - 'banned', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("banned" IN (0, 1))'), - defaultValue: const Constant(false)); - static const VerificationMeta _shadowBannedMeta = - const VerificationMeta('shadowBanned'); - @override - late final GeneratedColumn shadowBanned = GeneratedColumn( - 'shadow_banned', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("shadow_banned" IN (0, 1))'), - defaultValue: const Constant(false)); - static const VerificationMeta _isModeratorMeta = - const VerificationMeta('isModerator'); - @override - late final GeneratedColumn isModerator = GeneratedColumn( - 'is_moderator', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("is_moderator" IN (0, 1))'), - defaultValue: const Constant(false)); - static const VerificationMeta _createdAtMeta = - const VerificationMeta('createdAt'); - @override - late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: currentDateAndTime); - static const VerificationMeta _updatedAtMeta = - const VerificationMeta('updatedAt'); - @override - late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: currentDateAndTime); - @override - List get $columns => [ - userId, - channelCid, - channelRole, - inviteAcceptedAt, - inviteRejectedAt, - invited, - banned, - shadowBanned, - isModerator, - createdAt, - updatedAt - ]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'members'; - @override - VerificationContext validateIntegrity(Insertable instance, - {bool isInserting = false}) { - final context = VerificationContext(); - final data = instance.toColumns(true); - if (data.containsKey('user_id')) { - context.handle(_userIdMeta, - userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); - } else if (isInserting) { - context.missing(_userIdMeta); - } - if (data.containsKey('channel_cid')) { - context.handle( - _channelCidMeta, - channelCid.isAcceptableOrUnknown( - data['channel_cid']!, _channelCidMeta)); - } else if (isInserting) { - context.missing(_channelCidMeta); - } - if (data.containsKey('channel_role')) { - context.handle( - _channelRoleMeta, - channelRole.isAcceptableOrUnknown( - data['channel_role']!, _channelRoleMeta)); - } - if (data.containsKey('invite_accepted_at')) { - context.handle( - _inviteAcceptedAtMeta, - inviteAcceptedAt.isAcceptableOrUnknown( - data['invite_accepted_at']!, _inviteAcceptedAtMeta)); - } - if (data.containsKey('invite_rejected_at')) { - context.handle( - _inviteRejectedAtMeta, - inviteRejectedAt.isAcceptableOrUnknown( - data['invite_rejected_at']!, _inviteRejectedAtMeta)); - } - if (data.containsKey('invited')) { - context.handle(_invitedMeta, - invited.isAcceptableOrUnknown(data['invited']!, _invitedMeta)); - } - if (data.containsKey('banned')) { - context.handle(_bannedMeta, - banned.isAcceptableOrUnknown(data['banned']!, _bannedMeta)); - } - if (data.containsKey('shadow_banned')) { - context.handle( - _shadowBannedMeta, - shadowBanned.isAcceptableOrUnknown( - data['shadow_banned']!, _shadowBannedMeta)); - } - if (data.containsKey('is_moderator')) { - context.handle( - _isModeratorMeta, - isModerator.isAcceptableOrUnknown( - data['is_moderator']!, _isModeratorMeta)); - } - if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); - } - if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); - } - return context; - } - - @override - Set get $primaryKey => {userId, channelCid}; - @override - MemberEntity map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return MemberEntity( - userId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - channelCid: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}channel_cid'])!, - channelRole: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}channel_role']), - inviteAcceptedAt: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}invite_accepted_at']), - inviteRejectedAt: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}invite_rejected_at']), - invited: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}invited'])!, - banned: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}banned'])!, - shadowBanned: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}shadow_banned'])!, - isModerator: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_moderator'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ); - } - - @override - $MembersTable createAlias(String alias) { - return $MembersTable(attachedDatabase, alias); - } -} - -class MemberEntity extends DataClass implements Insertable { - /// The interested user id - final String userId; - - /// The channel cid of which this user is part of - final String channelCid; - - /// The role of the user in the channel - final String? channelRole; - - /// The date on which the user accepted the invite to the channel - final DateTime? inviteAcceptedAt; - - /// The date on which the user rejected the invite to the channel - final DateTime? inviteRejectedAt; - - /// True if the user has been invited to the channel - final bool invited; - - /// True if the member is banned from the channel - final bool banned; - - /// True if the member is shadow banned from the channel - final bool shadowBanned; - - /// True if the user is a moderator of the channel - final bool isModerator; - - /// The date of creation - final DateTime createdAt; - - /// The last date of update - final DateTime updatedAt; - const MemberEntity( - {required this.userId, - required this.channelCid, - this.channelRole, - this.inviteAcceptedAt, - this.inviteRejectedAt, - required this.invited, - required this.banned, - required this.shadowBanned, - required this.isModerator, - required this.createdAt, - required this.updatedAt}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['user_id'] = Variable(userId); - map['channel_cid'] = Variable(channelCid); - if (!nullToAbsent || channelRole != null) { - map['channel_role'] = Variable(channelRole); - } - if (!nullToAbsent || inviteAcceptedAt != null) { - map['invite_accepted_at'] = Variable(inviteAcceptedAt); - } - if (!nullToAbsent || inviteRejectedAt != null) { - map['invite_rejected_at'] = Variable(inviteRejectedAt); - } - map['invited'] = Variable(invited); - map['banned'] = Variable(banned); - map['shadow_banned'] = Variable(shadowBanned); - map['is_moderator'] = Variable(isModerator); - map['created_at'] = Variable(createdAt); - map['updated_at'] = Variable(updatedAt); - return map; - } - - factory MemberEntity.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return MemberEntity( - userId: serializer.fromJson(json['userId']), - channelCid: serializer.fromJson(json['channelCid']), - channelRole: serializer.fromJson(json['channelRole']), - inviteAcceptedAt: - serializer.fromJson(json['inviteAcceptedAt']), - inviteRejectedAt: - serializer.fromJson(json['inviteRejectedAt']), - invited: serializer.fromJson(json['invited']), - banned: serializer.fromJson(json['banned']), - shadowBanned: serializer.fromJson(json['shadowBanned']), - isModerator: serializer.fromJson(json['isModerator']), - createdAt: serializer.fromJson(json['createdAt']), - updatedAt: serializer.fromJson(json['updatedAt']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'userId': serializer.toJson(userId), - 'channelCid': serializer.toJson(channelCid), - 'channelRole': serializer.toJson(channelRole), - 'inviteAcceptedAt': serializer.toJson(inviteAcceptedAt), - 'inviteRejectedAt': serializer.toJson(inviteRejectedAt), - 'invited': serializer.toJson(invited), - 'banned': serializer.toJson(banned), - 'shadowBanned': serializer.toJson(shadowBanned), - 'isModerator': serializer.toJson(isModerator), - 'createdAt': serializer.toJson(createdAt), - 'updatedAt': serializer.toJson(updatedAt), - }; - } - - MemberEntity copyWith( - {String? userId, - String? channelCid, - Value channelRole = const Value.absent(), - Value inviteAcceptedAt = const Value.absent(), - Value inviteRejectedAt = const Value.absent(), - bool? invited, - bool? banned, - bool? shadowBanned, - bool? isModerator, - DateTime? createdAt, - DateTime? updatedAt}) => - MemberEntity( - userId: userId ?? this.userId, - channelCid: channelCid ?? this.channelCid, - channelRole: channelRole.present ? channelRole.value : this.channelRole, - inviteAcceptedAt: inviteAcceptedAt.present - ? inviteAcceptedAt.value - : this.inviteAcceptedAt, - inviteRejectedAt: inviteRejectedAt.present - ? inviteRejectedAt.value - : this.inviteRejectedAt, - invited: invited ?? this.invited, - banned: banned ?? this.banned, - shadowBanned: shadowBanned ?? this.shadowBanned, - isModerator: isModerator ?? this.isModerator, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ); - MemberEntity copyWithCompanion(MembersCompanion data) { - return MemberEntity( - userId: data.userId.present ? data.userId.value : this.userId, - channelCid: - data.channelCid.present ? data.channelCid.value : this.channelCid, - channelRole: - data.channelRole.present ? data.channelRole.value : this.channelRole, - inviteAcceptedAt: data.inviteAcceptedAt.present - ? data.inviteAcceptedAt.value - : this.inviteAcceptedAt, - inviteRejectedAt: data.inviteRejectedAt.present - ? data.inviteRejectedAt.value - : this.inviteRejectedAt, - invited: data.invited.present ? data.invited.value : this.invited, - banned: data.banned.present ? data.banned.value : this.banned, - shadowBanned: data.shadowBanned.present - ? data.shadowBanned.value - : this.shadowBanned, - isModerator: - data.isModerator.present ? data.isModerator.value : this.isModerator, - createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, - updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - ); - } - - @override - String toString() { - return (StringBuffer('MemberEntity(') - ..write('userId: $userId, ') - ..write('channelCid: $channelCid, ') - ..write('channelRole: $channelRole, ') - ..write('inviteAcceptedAt: $inviteAcceptedAt, ') - ..write('inviteRejectedAt: $inviteRejectedAt, ') - ..write('invited: $invited, ') - ..write('banned: $banned, ') - ..write('shadowBanned: $shadowBanned, ') - ..write('isModerator: $isModerator, ') - ..write('createdAt: $createdAt, ') - ..write('updatedAt: $updatedAt') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash( - userId, - channelCid, - channelRole, - inviteAcceptedAt, - inviteRejectedAt, - invited, - banned, - shadowBanned, - isModerator, - createdAt, - updatedAt); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is MemberEntity && - other.userId == this.userId && - other.channelCid == this.channelCid && - other.channelRole == this.channelRole && - other.inviteAcceptedAt == this.inviteAcceptedAt && - other.inviteRejectedAt == this.inviteRejectedAt && - other.invited == this.invited && - other.banned == this.banned && - other.shadowBanned == this.shadowBanned && - other.isModerator == this.isModerator && - other.createdAt == this.createdAt && - other.updatedAt == this.updatedAt); -} - -class MembersCompanion extends UpdateCompanion { - final Value userId; - final Value channelCid; - final Value channelRole; - final Value inviteAcceptedAt; - final Value inviteRejectedAt; - final Value invited; - final Value banned; - final Value shadowBanned; - final Value isModerator; - final Value createdAt; - final Value updatedAt; - final Value rowid; - const MembersCompanion({ - this.userId = const Value.absent(), - this.channelCid = const Value.absent(), - this.channelRole = const Value.absent(), - this.inviteAcceptedAt = const Value.absent(), - this.inviteRejectedAt = const Value.absent(), - this.invited = const Value.absent(), - this.banned = const Value.absent(), - this.shadowBanned = const Value.absent(), - this.isModerator = const Value.absent(), - this.createdAt = const Value.absent(), - this.updatedAt = const Value.absent(), - this.rowid = const Value.absent(), - }); - MembersCompanion.insert({ - required String userId, - required String channelCid, - this.channelRole = const Value.absent(), - this.inviteAcceptedAt = const Value.absent(), - this.inviteRejectedAt = const Value.absent(), - this.invited = const Value.absent(), - this.banned = const Value.absent(), - this.shadowBanned = const Value.absent(), - this.isModerator = const Value.absent(), - this.createdAt = const Value.absent(), - this.updatedAt = const Value.absent(), - this.rowid = const Value.absent(), - }) : userId = Value(userId), - channelCid = Value(channelCid); - static Insertable custom({ - Expression? userId, - Expression? channelCid, - Expression? channelRole, - Expression? inviteAcceptedAt, - Expression? inviteRejectedAt, - Expression? invited, - Expression? banned, - Expression? shadowBanned, - Expression? isModerator, - Expression? createdAt, - Expression? updatedAt, - Expression? rowid, - }) { - return RawValuesInsertable({ - if (userId != null) 'user_id': userId, - if (channelCid != null) 'channel_cid': channelCid, - if (channelRole != null) 'channel_role': channelRole, - if (inviteAcceptedAt != null) 'invite_accepted_at': inviteAcceptedAt, - if (inviteRejectedAt != null) 'invite_rejected_at': inviteRejectedAt, - if (invited != null) 'invited': invited, - if (banned != null) 'banned': banned, - if (shadowBanned != null) 'shadow_banned': shadowBanned, - if (isModerator != null) 'is_moderator': isModerator, - if (createdAt != null) 'created_at': createdAt, - if (updatedAt != null) 'updated_at': updatedAt, - if (rowid != null) 'rowid': rowid, - }); - } - - MembersCompanion copyWith( - {Value? userId, - Value? channelCid, - Value? channelRole, - Value? inviteAcceptedAt, - Value? inviteRejectedAt, - Value? invited, - Value? banned, - Value? shadowBanned, - Value? isModerator, - Value? createdAt, - Value? updatedAt, - Value? rowid}) { - return MembersCompanion( - userId: userId ?? this.userId, - channelCid: channelCid ?? this.channelCid, - channelRole: channelRole ?? this.channelRole, - inviteAcceptedAt: inviteAcceptedAt ?? this.inviteAcceptedAt, - inviteRejectedAt: inviteRejectedAt ?? this.inviteRejectedAt, - invited: invited ?? this.invited, - banned: banned ?? this.banned, - shadowBanned: shadowBanned ?? this.shadowBanned, - isModerator: isModerator ?? this.isModerator, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - rowid: rowid ?? this.rowid, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (userId.present) { - map['user_id'] = Variable(userId.value); - } - if (channelCid.present) { - map['channel_cid'] = Variable(channelCid.value); - } - if (channelRole.present) { - map['channel_role'] = Variable(channelRole.value); - } - if (inviteAcceptedAt.present) { - map['invite_accepted_at'] = Variable(inviteAcceptedAt.value); - } - if (inviteRejectedAt.present) { - map['invite_rejected_at'] = Variable(inviteRejectedAt.value); - } - if (invited.present) { - map['invited'] = Variable(invited.value); - } - if (banned.present) { - map['banned'] = Variable(banned.value); - } - if (shadowBanned.present) { - map['shadow_banned'] = Variable(shadowBanned.value); - } - if (isModerator.present) { - map['is_moderator'] = Variable(isModerator.value); - } - if (createdAt.present) { - map['created_at'] = Variable(createdAt.value); - } - if (updatedAt.present) { - map['updated_at'] = Variable(updatedAt.value); - } - if (rowid.present) { - map['rowid'] = Variable(rowid.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('MembersCompanion(') - ..write('userId: $userId, ') - ..write('channelCid: $channelCid, ') - ..write('channelRole: $channelRole, ') - ..write('inviteAcceptedAt: $inviteAcceptedAt, ') - ..write('inviteRejectedAt: $inviteRejectedAt, ') - ..write('invited: $invited, ') - ..write('banned: $banned, ') - ..write('shadowBanned: $shadowBanned, ') - ..write('isModerator: $isModerator, ') - ..write('createdAt: $createdAt, ') - ..write('updatedAt: $updatedAt, ') - ..write('rowid: $rowid') - ..write(')')) - .toString(); - } -} - -class $ReadsTable extends Reads with TableInfo<$ReadsTable, ReadEntity> { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - $ReadsTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _lastReadMeta = - const VerificationMeta('lastRead'); - @override - late final GeneratedColumn lastRead = GeneratedColumn( - 'last_read', aliasedName, false, - type: DriftSqlType.dateTime, requiredDuringInsert: true); - static const VerificationMeta _userIdMeta = const VerificationMeta('userId'); - @override - late final GeneratedColumn userId = GeneratedColumn( - 'user_id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - static const VerificationMeta _channelCidMeta = - const VerificationMeta('channelCid'); - @override - late final GeneratedColumn channelCid = GeneratedColumn( - 'channel_cid', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES channels (cid) ON DELETE CASCADE')); - static const VerificationMeta _unreadMessagesMeta = - const VerificationMeta('unreadMessages'); - @override - late final GeneratedColumn unreadMessages = GeneratedColumn( - 'unread_messages', aliasedName, false, - type: DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const Constant(0)); - static const VerificationMeta _lastReadMessageIdMeta = - const VerificationMeta('lastReadMessageId'); - @override - late final GeneratedColumn lastReadMessageId = - GeneratedColumn('last_read_message_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); - @override - List get $columns => - [lastRead, userId, channelCid, unreadMessages, lastReadMessageId]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'reads'; - @override - VerificationContext validateIntegrity(Insertable instance, - {bool isInserting = false}) { - final context = VerificationContext(); - final data = instance.toColumns(true); - if (data.containsKey('last_read')) { - context.handle(_lastReadMeta, - lastRead.isAcceptableOrUnknown(data['last_read']!, _lastReadMeta)); - } else if (isInserting) { - context.missing(_lastReadMeta); - } - if (data.containsKey('user_id')) { - context.handle(_userIdMeta, - userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); - } else if (isInserting) { - context.missing(_userIdMeta); - } - if (data.containsKey('channel_cid')) { - context.handle( - _channelCidMeta, - channelCid.isAcceptableOrUnknown( - data['channel_cid']!, _channelCidMeta)); - } else if (isInserting) { - context.missing(_channelCidMeta); - } - if (data.containsKey('unread_messages')) { - context.handle( - _unreadMessagesMeta, - unreadMessages.isAcceptableOrUnknown( - data['unread_messages']!, _unreadMessagesMeta)); - } - if (data.containsKey('last_read_message_id')) { - context.handle( - _lastReadMessageIdMeta, - lastReadMessageId.isAcceptableOrUnknown( - data['last_read_message_id']!, _lastReadMessageIdMeta)); - } - return context; - } - - @override - Set get $primaryKey => {userId, channelCid}; - @override - ReadEntity map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return ReadEntity( - lastRead: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}last_read'])!, - userId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - channelCid: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}channel_cid'])!, - unreadMessages: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}unread_messages'])!, - lastReadMessageId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}last_read_message_id']), - ); - } - - @override - $ReadsTable createAlias(String alias) { - return $ReadsTable(attachedDatabase, alias); - } -} - -class ReadEntity extends DataClass implements Insertable { - /// Date of the read event - final DateTime lastRead; - - /// Id of the User who sent the event - final String userId; - - /// The channel cid of which this read belongs - final String channelCid; - - /// Number of unread messages - final int unreadMessages; - - /// Id of the last read message - final String? lastReadMessageId; - const ReadEntity( - {required this.lastRead, - required this.userId, - required this.channelCid, - required this.unreadMessages, - this.lastReadMessageId}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['last_read'] = Variable(lastRead); - map['user_id'] = Variable(userId); - map['channel_cid'] = Variable(channelCid); - map['unread_messages'] = Variable(unreadMessages); - if (!nullToAbsent || lastReadMessageId != null) { - map['last_read_message_id'] = Variable(lastReadMessageId); - } - return map; - } - - factory ReadEntity.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return ReadEntity( - lastRead: serializer.fromJson(json['lastRead']), - userId: serializer.fromJson(json['userId']), - channelCid: serializer.fromJson(json['channelCid']), - unreadMessages: serializer.fromJson(json['unreadMessages']), - lastReadMessageId: - serializer.fromJson(json['lastReadMessageId']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'lastRead': serializer.toJson(lastRead), - 'userId': serializer.toJson(userId), - 'channelCid': serializer.toJson(channelCid), - 'unreadMessages': serializer.toJson(unreadMessages), - 'lastReadMessageId': serializer.toJson(lastReadMessageId), - }; - } - - ReadEntity copyWith( - {DateTime? lastRead, - String? userId, - String? channelCid, - int? unreadMessages, - Value lastReadMessageId = const Value.absent()}) => - ReadEntity( - lastRead: lastRead ?? this.lastRead, - userId: userId ?? this.userId, - channelCid: channelCid ?? this.channelCid, - unreadMessages: unreadMessages ?? this.unreadMessages, - lastReadMessageId: lastReadMessageId.present - ? lastReadMessageId.value - : this.lastReadMessageId, - ); - ReadEntity copyWithCompanion(ReadsCompanion data) { - return ReadEntity( - lastRead: data.lastRead.present ? data.lastRead.value : this.lastRead, - userId: data.userId.present ? data.userId.value : this.userId, - channelCid: - data.channelCid.present ? data.channelCid.value : this.channelCid, - unreadMessages: data.unreadMessages.present - ? data.unreadMessages.value - : this.unreadMessages, - lastReadMessageId: data.lastReadMessageId.present - ? data.lastReadMessageId.value - : this.lastReadMessageId, - ); - } - - @override - String toString() { - return (StringBuffer('ReadEntity(') - ..write('lastRead: $lastRead, ') - ..write('userId: $userId, ') - ..write('channelCid: $channelCid, ') - ..write('unreadMessages: $unreadMessages, ') - ..write('lastReadMessageId: $lastReadMessageId') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash( - lastRead, userId, channelCid, unreadMessages, lastReadMessageId); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is ReadEntity && - other.lastRead == this.lastRead && - other.userId == this.userId && - other.channelCid == this.channelCid && - other.unreadMessages == this.unreadMessages && - other.lastReadMessageId == this.lastReadMessageId); -} - -class ReadsCompanion extends UpdateCompanion { - final Value lastRead; - final Value userId; - final Value channelCid; - final Value unreadMessages; - final Value lastReadMessageId; - final Value rowid; - const ReadsCompanion({ - this.lastRead = const Value.absent(), - this.userId = const Value.absent(), - this.channelCid = const Value.absent(), - this.unreadMessages = const Value.absent(), - this.lastReadMessageId = const Value.absent(), - this.rowid = const Value.absent(), - }); - ReadsCompanion.insert({ - required DateTime lastRead, - required String userId, - required String channelCid, - this.unreadMessages = const Value.absent(), - this.lastReadMessageId = const Value.absent(), - this.rowid = const Value.absent(), - }) : lastRead = Value(lastRead), - userId = Value(userId), - channelCid = Value(channelCid); - static Insertable custom({ - Expression? lastRead, - Expression? userId, - Expression? channelCid, - Expression? unreadMessages, - Expression? lastReadMessageId, - Expression? rowid, - }) { - return RawValuesInsertable({ - if (lastRead != null) 'last_read': lastRead, - if (userId != null) 'user_id': userId, - if (channelCid != null) 'channel_cid': channelCid, - if (unreadMessages != null) 'unread_messages': unreadMessages, - if (lastReadMessageId != null) 'last_read_message_id': lastReadMessageId, - if (rowid != null) 'rowid': rowid, - }); - } - - ReadsCompanion copyWith( - {Value? lastRead, - Value? userId, - Value? channelCid, - Value? unreadMessages, - Value? lastReadMessageId, - Value? rowid}) { - return ReadsCompanion( - lastRead: lastRead ?? this.lastRead, - userId: userId ?? this.userId, - channelCid: channelCid ?? this.channelCid, - unreadMessages: unreadMessages ?? this.unreadMessages, - lastReadMessageId: lastReadMessageId ?? this.lastReadMessageId, - rowid: rowid ?? this.rowid, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (lastRead.present) { - map['last_read'] = Variable(lastRead.value); - } - if (userId.present) { - map['user_id'] = Variable(userId.value); - } - if (channelCid.present) { - map['channel_cid'] = Variable(channelCid.value); - } - if (unreadMessages.present) { - map['unread_messages'] = Variable(unreadMessages.value); - } - if (lastReadMessageId.present) { - map['last_read_message_id'] = Variable(lastReadMessageId.value); - } - if (rowid.present) { - map['rowid'] = Variable(rowid.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('ReadsCompanion(') - ..write('lastRead: $lastRead, ') - ..write('userId: $userId, ') - ..write('channelCid: $channelCid, ') - ..write('unreadMessages: $unreadMessages, ') - ..write('lastReadMessageId: $lastReadMessageId, ') - ..write('rowid: $rowid') - ..write(')')) - .toString(); - } -} - -class $ChannelQueriesTable extends ChannelQueries - with TableInfo<$ChannelQueriesTable, ChannelQueryEntity> { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - $ChannelQueriesTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _queryHashMeta = - const VerificationMeta('queryHash'); - @override - late final GeneratedColumn queryHash = GeneratedColumn( - 'query_hash', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - static const VerificationMeta _channelCidMeta = - const VerificationMeta('channelCid'); - @override - late final GeneratedColumn channelCid = GeneratedColumn( - 'channel_cid', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - @override - List get $columns => [queryHash, channelCid]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'channel_queries'; - @override - VerificationContext validateIntegrity(Insertable instance, - {bool isInserting = false}) { - final context = VerificationContext(); - final data = instance.toColumns(true); - if (data.containsKey('query_hash')) { - context.handle(_queryHashMeta, - queryHash.isAcceptableOrUnknown(data['query_hash']!, _queryHashMeta)); - } else if (isInserting) { - context.missing(_queryHashMeta); - } - if (data.containsKey('channel_cid')) { - context.handle( - _channelCidMeta, - channelCid.isAcceptableOrUnknown( - data['channel_cid']!, _channelCidMeta)); - } else if (isInserting) { - context.missing(_channelCidMeta); - } - return context; - } - - @override - Set get $primaryKey => {queryHash, channelCid}; - @override - ChannelQueryEntity map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return ChannelQueryEntity( - queryHash: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}query_hash'])!, - channelCid: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}channel_cid'])!, - ); - } - - @override - $ChannelQueriesTable createAlias(String alias) { - return $ChannelQueriesTable(attachedDatabase, alias); - } -} - -class ChannelQueryEntity extends DataClass - implements Insertable { - /// The unique hash of this query - final String queryHash; - - /// The channel cid of this query - final String channelCid; - const ChannelQueryEntity({required this.queryHash, required this.channelCid}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['query_hash'] = Variable(queryHash); - map['channel_cid'] = Variable(channelCid); - return map; - } - - factory ChannelQueryEntity.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return ChannelQueryEntity( - queryHash: serializer.fromJson(json['queryHash']), - channelCid: serializer.fromJson(json['channelCid']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'queryHash': serializer.toJson(queryHash), - 'channelCid': serializer.toJson(channelCid), - }; - } - - ChannelQueryEntity copyWith({String? queryHash, String? channelCid}) => - ChannelQueryEntity( - queryHash: queryHash ?? this.queryHash, - channelCid: channelCid ?? this.channelCid, - ); - ChannelQueryEntity copyWithCompanion(ChannelQueriesCompanion data) { - return ChannelQueryEntity( - queryHash: data.queryHash.present ? data.queryHash.value : this.queryHash, - channelCid: - data.channelCid.present ? data.channelCid.value : this.channelCid, - ); - } - - @override - String toString() { - return (StringBuffer('ChannelQueryEntity(') - ..write('queryHash: $queryHash, ') - ..write('channelCid: $channelCid') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash(queryHash, channelCid); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is ChannelQueryEntity && - other.queryHash == this.queryHash && - other.channelCid == this.channelCid); -} - -class ChannelQueriesCompanion extends UpdateCompanion { - final Value queryHash; - final Value channelCid; - final Value rowid; - const ChannelQueriesCompanion({ - this.queryHash = const Value.absent(), - this.channelCid = const Value.absent(), - this.rowid = const Value.absent(), - }); - ChannelQueriesCompanion.insert({ - required String queryHash, - required String channelCid, - this.rowid = const Value.absent(), - }) : queryHash = Value(queryHash), - channelCid = Value(channelCid); - static Insertable custom({ - Expression? queryHash, - Expression? channelCid, - Expression? rowid, - }) { - return RawValuesInsertable({ - if (queryHash != null) 'query_hash': queryHash, - if (channelCid != null) 'channel_cid': channelCid, - if (rowid != null) 'rowid': rowid, - }); - } - - ChannelQueriesCompanion copyWith( - {Value? queryHash, - Value? channelCid, - Value? rowid}) { - return ChannelQueriesCompanion( - queryHash: queryHash ?? this.queryHash, - channelCid: channelCid ?? this.channelCid, - rowid: rowid ?? this.rowid, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (queryHash.present) { - map['query_hash'] = Variable(queryHash.value); - } - if (channelCid.present) { - map['channel_cid'] = Variable(channelCid.value); - } - if (rowid.present) { - map['rowid'] = Variable(rowid.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('ChannelQueriesCompanion(') - ..write('queryHash: $queryHash, ') - ..write('channelCid: $channelCid, ') - ..write('rowid: $rowid') - ..write(')')) - .toString(); - } -} - -class $ConnectionEventsTable extends ConnectionEvents - with TableInfo<$ConnectionEventsTable, ConnectionEventEntity> { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - $ConnectionEventsTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _idMeta = const VerificationMeta('id'); - @override - late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: false); - static const VerificationMeta _typeMeta = const VerificationMeta('type'); - @override - late final GeneratedColumn type = GeneratedColumn( - 'type', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); - static const VerificationMeta _ownUserMeta = - const VerificationMeta('ownUser'); - @override - late final GeneratedColumnWithTypeConverter?, String> - ownUser = GeneratedColumn('own_user', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false) - .withConverter?>( - $ConnectionEventsTable.$converterownUsern); - static const VerificationMeta _totalUnreadCountMeta = - const VerificationMeta('totalUnreadCount'); - @override - late final GeneratedColumn totalUnreadCount = GeneratedColumn( - 'total_unread_count', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - static const VerificationMeta _unreadChannelsMeta = - const VerificationMeta('unreadChannels'); - @override - late final GeneratedColumn unreadChannels = GeneratedColumn( - 'unread_channels', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); - static const VerificationMeta _lastEventAtMeta = - const VerificationMeta('lastEventAt'); - @override - late final GeneratedColumn lastEventAt = GeneratedColumn( - 'last_event_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - static const VerificationMeta _lastSyncAtMeta = - const VerificationMeta('lastSyncAt'); - @override - late final GeneratedColumn lastSyncAt = GeneratedColumn( - 'last_sync_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); - @override - List get $columns => [ - id, - type, - ownUser, - totalUnreadCount, - unreadChannels, - lastEventAt, - lastSyncAt - ]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'connection_events'; - @override - VerificationContext validateIntegrity( - Insertable instance, - {bool isInserting = false}) { - final context = VerificationContext(); - final data = instance.toColumns(true); - if (data.containsKey('id')) { - context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); - } - if (data.containsKey('type')) { - context.handle( - _typeMeta, type.isAcceptableOrUnknown(data['type']!, _typeMeta)); - } else if (isInserting) { - context.missing(_typeMeta); - } - context.handle(_ownUserMeta, const VerificationResult.success()); - if (data.containsKey('total_unread_count')) { - context.handle( - _totalUnreadCountMeta, - totalUnreadCount.isAcceptableOrUnknown( - data['total_unread_count']!, _totalUnreadCountMeta)); - } - if (data.containsKey('unread_channels')) { - context.handle( - _unreadChannelsMeta, - unreadChannels.isAcceptableOrUnknown( - data['unread_channels']!, _unreadChannelsMeta)); - } - if (data.containsKey('last_event_at')) { - context.handle( - _lastEventAtMeta, - lastEventAt.isAcceptableOrUnknown( - data['last_event_at']!, _lastEventAtMeta)); - } - if (data.containsKey('last_sync_at')) { - context.handle( - _lastSyncAtMeta, - lastSyncAt.isAcceptableOrUnknown( - data['last_sync_at']!, _lastSyncAtMeta)); - } - return context; - } - - @override - Set get $primaryKey => {id}; - @override - ConnectionEventEntity map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return ConnectionEventEntity( - id: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}id'])!, - type: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}type'])!, - ownUser: $ConnectionEventsTable.$converterownUsern.fromSql( - attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}own_user'])), - totalUnreadCount: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}total_unread_count']), - unreadChannels: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}unread_channels']), - lastEventAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}last_event_at']), - lastSyncAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}last_sync_at']), - ); - } - - @override - $ConnectionEventsTable createAlias(String alias) { - return $ConnectionEventsTable(attachedDatabase, alias); - } - - static TypeConverter, String> $converterownUser = - MapConverter(); - static TypeConverter?, String?> $converterownUsern = - NullAwareTypeConverter.wrap($converterownUser); -} - -class ConnectionEventEntity extends DataClass - implements Insertable { - /// event id - final int id; - - /// event type - final String type; - - /// User object of the current user - final Map? ownUser; - - /// The number of unread messages for current user - final int? totalUnreadCount; - - /// User total unread channels for current user - final int? unreadChannels; - - /// DateTime of the last event - final DateTime? lastEventAt; - - /// DateTime of the last sync - final DateTime? lastSyncAt; - const ConnectionEventEntity( - {required this.id, - required this.type, - this.ownUser, - this.totalUnreadCount, - this.unreadChannels, - this.lastEventAt, - this.lastSyncAt}); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['id'] = Variable(id); - map['type'] = Variable(type); - if (!nullToAbsent || ownUser != null) { - map['own_user'] = Variable( - $ConnectionEventsTable.$converterownUsern.toSql(ownUser)); - } - if (!nullToAbsent || totalUnreadCount != null) { - map['total_unread_count'] = Variable(totalUnreadCount); - } - if (!nullToAbsent || unreadChannels != null) { - map['unread_channels'] = Variable(unreadChannels); - } - if (!nullToAbsent || lastEventAt != null) { - map['last_event_at'] = Variable(lastEventAt); - } - if (!nullToAbsent || lastSyncAt != null) { - map['last_sync_at'] = Variable(lastSyncAt); - } - return map; - } - - factory ConnectionEventEntity.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return ConnectionEventEntity( - id: serializer.fromJson(json['id']), - type: serializer.fromJson(json['type']), - ownUser: serializer.fromJson?>(json['ownUser']), - totalUnreadCount: serializer.fromJson(json['totalUnreadCount']), - unreadChannels: serializer.fromJson(json['unreadChannels']), - lastEventAt: serializer.fromJson(json['lastEventAt']), - lastSyncAt: serializer.fromJson(json['lastSyncAt']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'id': serializer.toJson(id), - 'type': serializer.toJson(type), - 'ownUser': serializer.toJson?>(ownUser), - 'totalUnreadCount': serializer.toJson(totalUnreadCount), - 'unreadChannels': serializer.toJson(unreadChannels), - 'lastEventAt': serializer.toJson(lastEventAt), - 'lastSyncAt': serializer.toJson(lastSyncAt), - }; - } - - ConnectionEventEntity copyWith( - {int? id, - String? type, - Value?> ownUser = const Value.absent(), - Value totalUnreadCount = const Value.absent(), - Value unreadChannels = const Value.absent(), - Value lastEventAt = const Value.absent(), - Value lastSyncAt = const Value.absent()}) => - ConnectionEventEntity( - id: id ?? this.id, - type: type ?? this.type, - ownUser: ownUser.present ? ownUser.value : this.ownUser, - totalUnreadCount: totalUnreadCount.present - ? totalUnreadCount.value - : this.totalUnreadCount, - unreadChannels: - unreadChannels.present ? unreadChannels.value : this.unreadChannels, - lastEventAt: lastEventAt.present ? lastEventAt.value : this.lastEventAt, - lastSyncAt: lastSyncAt.present ? lastSyncAt.value : this.lastSyncAt, - ); - ConnectionEventEntity copyWithCompanion(ConnectionEventsCompanion data) { - return ConnectionEventEntity( - id: data.id.present ? data.id.value : this.id, - type: data.type.present ? data.type.value : this.type, - ownUser: data.ownUser.present ? data.ownUser.value : this.ownUser, - totalUnreadCount: data.totalUnreadCount.present - ? data.totalUnreadCount.value - : this.totalUnreadCount, - unreadChannels: data.unreadChannels.present - ? data.unreadChannels.value - : this.unreadChannels, - lastEventAt: - data.lastEventAt.present ? data.lastEventAt.value : this.lastEventAt, - lastSyncAt: - data.lastSyncAt.present ? data.lastSyncAt.value : this.lastSyncAt, - ); - } - - @override - String toString() { - return (StringBuffer('ConnectionEventEntity(') - ..write('id: $id, ') - ..write('type: $type, ') - ..write('ownUser: $ownUser, ') - ..write('totalUnreadCount: $totalUnreadCount, ') - ..write('unreadChannels: $unreadChannels, ') - ..write('lastEventAt: $lastEventAt, ') - ..write('lastSyncAt: $lastSyncAt') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash(id, type, ownUser, totalUnreadCount, - unreadChannels, lastEventAt, lastSyncAt); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is ConnectionEventEntity && - other.id == this.id && - other.type == this.type && - other.ownUser == this.ownUser && - other.totalUnreadCount == this.totalUnreadCount && - other.unreadChannels == this.unreadChannels && - other.lastEventAt == this.lastEventAt && - other.lastSyncAt == this.lastSyncAt); -} - -class ConnectionEventsCompanion extends UpdateCompanion { - final Value id; - final Value type; - final Value?> ownUser; - final Value totalUnreadCount; - final Value unreadChannels; - final Value lastEventAt; - final Value lastSyncAt; - const ConnectionEventsCompanion({ - this.id = const Value.absent(), - this.type = const Value.absent(), - this.ownUser = const Value.absent(), - this.totalUnreadCount = const Value.absent(), - this.unreadChannels = const Value.absent(), - this.lastEventAt = const Value.absent(), - this.lastSyncAt = const Value.absent(), - }); - ConnectionEventsCompanion.insert({ - this.id = const Value.absent(), - required String type, - this.ownUser = const Value.absent(), - this.totalUnreadCount = const Value.absent(), - this.unreadChannels = const Value.absent(), - this.lastEventAt = const Value.absent(), - this.lastSyncAt = const Value.absent(), - }) : type = Value(type); - static Insertable custom({ - Expression? id, - Expression? type, - Expression? ownUser, - Expression? totalUnreadCount, - Expression? unreadChannels, - Expression? lastEventAt, - Expression? lastSyncAt, - }) { - return RawValuesInsertable({ - if (id != null) 'id': id, - if (type != null) 'type': type, - if (ownUser != null) 'own_user': ownUser, - if (totalUnreadCount != null) 'total_unread_count': totalUnreadCount, - if (unreadChannels != null) 'unread_channels': unreadChannels, - if (lastEventAt != null) 'last_event_at': lastEventAt, - if (lastSyncAt != null) 'last_sync_at': lastSyncAt, - }); - } - - ConnectionEventsCompanion copyWith( - {Value? id, - Value? type, - Value?>? ownUser, - Value? totalUnreadCount, - Value? unreadChannels, - Value? lastEventAt, - Value? lastSyncAt}) { - return ConnectionEventsCompanion( - id: id ?? this.id, - type: type ?? this.type, - ownUser: ownUser ?? this.ownUser, - totalUnreadCount: totalUnreadCount ?? this.totalUnreadCount, - unreadChannels: unreadChannels ?? this.unreadChannels, - lastEventAt: lastEventAt ?? this.lastEventAt, - lastSyncAt: lastSyncAt ?? this.lastSyncAt, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (id.present) { - map['id'] = Variable(id.value); - } - if (type.present) { - map['type'] = Variable(type.value); - } - if (ownUser.present) { - map['own_user'] = Variable( - $ConnectionEventsTable.$converterownUsern.toSql(ownUser.value)); - } - if (totalUnreadCount.present) { - map['total_unread_count'] = Variable(totalUnreadCount.value); - } - if (unreadChannels.present) { - map['unread_channels'] = Variable(unreadChannels.value); - } - if (lastEventAt.present) { - map['last_event_at'] = Variable(lastEventAt.value); - } - if (lastSyncAt.present) { - map['last_sync_at'] = Variable(lastSyncAt.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('ConnectionEventsCompanion(') - ..write('id: $id, ') - ..write('type: $type, ') - ..write('ownUser: $ownUser, ') - ..write('totalUnreadCount: $totalUnreadCount, ') - ..write('unreadChannels: $unreadChannels, ') - ..write('lastEventAt: $lastEventAt, ') - ..write('lastSyncAt: $lastSyncAt') - ..write(')')) - .toString(); - } -} - -abstract class _$DriftChatDatabase extends GeneratedDatabase { - _$DriftChatDatabase(QueryExecutor e) : super(e); - $DriftChatDatabaseManager get managers => $DriftChatDatabaseManager(this); - late final $ChannelsTable channels = $ChannelsTable(this); - late final $MessagesTable messages = $MessagesTable(this); - late final $PinnedMessagesTable pinnedMessages = $PinnedMessagesTable(this); - late final $PollsTable polls = $PollsTable(this); - late final $PollVotesTable pollVotes = $PollVotesTable(this); - late final $PinnedMessageReactionsTable pinnedMessageReactions = - $PinnedMessageReactionsTable(this); - late final $ReactionsTable reactions = $ReactionsTable(this); - late final $UsersTable users = $UsersTable(this); - late final $MembersTable members = $MembersTable(this); - late final $ReadsTable reads = $ReadsTable(this); - late final $ChannelQueriesTable channelQueries = $ChannelQueriesTable(this); - late final $ConnectionEventsTable connectionEvents = - $ConnectionEventsTable(this); - late final UserDao userDao = UserDao(this as DriftChatDatabase); - late final ChannelDao channelDao = ChannelDao(this as DriftChatDatabase); - late final MessageDao messageDao = MessageDao(this as DriftChatDatabase); - late final PinnedMessageDao pinnedMessageDao = - PinnedMessageDao(this as DriftChatDatabase); - late final PinnedMessageReactionDao pinnedMessageReactionDao = - PinnedMessageReactionDao(this as DriftChatDatabase); - late final MemberDao memberDao = MemberDao(this as DriftChatDatabase); - late final PollDao pollDao = PollDao(this as DriftChatDatabase); - late final PollVoteDao pollVoteDao = PollVoteDao(this as DriftChatDatabase); - late final ReactionDao reactionDao = ReactionDao(this as DriftChatDatabase); - late final ReadDao readDao = ReadDao(this as DriftChatDatabase); - late final ChannelQueryDao channelQueryDao = - ChannelQueryDao(this as DriftChatDatabase); - late final ConnectionEventDao connectionEventDao = - ConnectionEventDao(this as DriftChatDatabase); - @override - Iterable> get allTables => - allSchemaEntities.whereType>(); - @override - List get allSchemaEntities => [ - channels, - messages, - pinnedMessages, - polls, - pollVotes, - pinnedMessageReactions, - reactions, - users, - members, - reads, - channelQueries, - connectionEvents - ]; - @override - StreamQueryUpdateRules get streamUpdateRules => const StreamQueryUpdateRules( - [ - WritePropagation( - on: TableUpdateQuery.onTableName('channels', - limitUpdateKind: UpdateKind.delete), - result: [ - TableUpdate('messages', kind: UpdateKind.delete), - ], - ), - WritePropagation( - on: TableUpdateQuery.onTableName('polls', - limitUpdateKind: UpdateKind.delete), - result: [ - TableUpdate('poll_votes', kind: UpdateKind.delete), - ], - ), - WritePropagation( - on: TableUpdateQuery.onTableName('pinned_messages', - limitUpdateKind: UpdateKind.delete), - result: [ - TableUpdate('pinned_message_reactions', kind: UpdateKind.delete), - ], - ), - WritePropagation( - on: TableUpdateQuery.onTableName('messages', - limitUpdateKind: UpdateKind.delete), - result: [ - TableUpdate('reactions', kind: UpdateKind.delete), - ], - ), - WritePropagation( - on: TableUpdateQuery.onTableName('channels', - limitUpdateKind: UpdateKind.delete), - result: [ - TableUpdate('members', kind: UpdateKind.delete), - ], - ), - WritePropagation( - on: TableUpdateQuery.onTableName('channels', - limitUpdateKind: UpdateKind.delete), - result: [ - TableUpdate('reads', kind: UpdateKind.delete), - ], - ), - ], - ); -} - -typedef $$ChannelsTableCreateCompanionBuilder = ChannelsCompanion Function({ - required String id, - required String type, - required String cid, - Value?> ownCapabilities, - required Map config, - Value frozen, - Value lastMessageAt, - Value createdAt, - Value updatedAt, - Value deletedAt, - Value memberCount, - Value createdById, - Value?> extraData, - Value rowid, -}); -typedef $$ChannelsTableUpdateCompanionBuilder = ChannelsCompanion Function({ - Value id, - Value type, - Value cid, - Value?> ownCapabilities, - Value> config, - Value frozen, - Value lastMessageAt, - Value createdAt, - Value updatedAt, - Value deletedAt, - Value memberCount, - Value createdById, - Value?> extraData, - Value rowid, -}); - -final class $$ChannelsTableReferences - extends BaseReferences<_$DriftChatDatabase, $ChannelsTable, ChannelEntity> { - $$ChannelsTableReferences(super.$_db, super.$_table, super.$_typedResult); - - static MultiTypedResultKey<$MessagesTable, List> - _messagesRefsTable(_$DriftChatDatabase db) => - MultiTypedResultKey.fromTable(db.messages, - aliasName: $_aliasNameGenerator( - db.channels.cid, db.messages.channelCid)); - - $$MessagesTableProcessedTableManager get messagesRefs { - final manager = $$MessagesTableTableManager($_db, $_db.messages) - .filter((f) => f.channelCid.cid($_item.cid)); - - final cache = $_typedResult.readTableOrNull(_messagesRefsTable($_db)); - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: cache)); - } - - static MultiTypedResultKey<$MembersTable, List> - _membersRefsTable(_$DriftChatDatabase db) => - MultiTypedResultKey.fromTable(db.members, - aliasName: - $_aliasNameGenerator(db.channels.cid, db.members.channelCid)); - - $$MembersTableProcessedTableManager get membersRefs { - final manager = $$MembersTableTableManager($_db, $_db.members) - .filter((f) => f.channelCid.cid($_item.cid)); - - final cache = $_typedResult.readTableOrNull(_membersRefsTable($_db)); - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: cache)); - } - - static MultiTypedResultKey<$ReadsTable, List> _readsRefsTable( - _$DriftChatDatabase db) => - MultiTypedResultKey.fromTable(db.reads, - aliasName: - $_aliasNameGenerator(db.channels.cid, db.reads.channelCid)); - - $$ReadsTableProcessedTableManager get readsRefs { - final manager = $$ReadsTableTableManager($_db, $_db.reads) - .filter((f) => f.channelCid.cid($_item.cid)); - - final cache = $_typedResult.readTableOrNull(_readsRefsTable($_db)); - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: cache)); - } -} - -class $$ChannelsTableFilterComposer - extends Composer<_$DriftChatDatabase, $ChannelsTable> { - $$ChannelsTableFilterComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => ColumnFilters(column)); - - ColumnFilters get type => $composableBuilder( - column: $table.type, builder: (column) => ColumnFilters(column)); - - ColumnFilters get cid => $composableBuilder( - column: $table.cid, builder: (column) => ColumnFilters(column)); - - ColumnWithTypeConverterFilters?, List, String> - get ownCapabilities => $composableBuilder( - column: $table.ownCapabilities, - builder: (column) => ColumnWithTypeConverterFilters(column)); - - ColumnWithTypeConverterFilters, Map, - String> - get config => $composableBuilder( - column: $table.config, - builder: (column) => ColumnWithTypeConverterFilters(column)); - - ColumnFilters get frozen => $composableBuilder( - column: $table.frozen, builder: (column) => ColumnFilters(column)); - - ColumnFilters get lastMessageAt => $composableBuilder( - column: $table.lastMessageAt, builder: (column) => ColumnFilters(column)); - - ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => ColumnFilters(column)); - - ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => ColumnFilters(column)); - - ColumnFilters get deletedAt => $composableBuilder( - column: $table.deletedAt, builder: (column) => ColumnFilters(column)); - - ColumnFilters get memberCount => $composableBuilder( - column: $table.memberCount, builder: (column) => ColumnFilters(column)); - - ColumnFilters get createdById => $composableBuilder( - column: $table.createdById, builder: (column) => ColumnFilters(column)); - - ColumnWithTypeConverterFilters?, Map, - String> - get extraData => $composableBuilder( - column: $table.extraData, - builder: (column) => ColumnWithTypeConverterFilters(column)); - - Expression messagesRefs( - Expression Function($$MessagesTableFilterComposer f) f) { - final $$MessagesTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.cid, - referencedTable: $db.messages, - getReferencedColumn: (t) => t.channelCid, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$MessagesTableFilterComposer( - $db: $db, - $table: $db.messages, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return f(composer); - } - - Expression membersRefs( - Expression Function($$MembersTableFilterComposer f) f) { - final $$MembersTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.cid, - referencedTable: $db.members, - getReferencedColumn: (t) => t.channelCid, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$MembersTableFilterComposer( - $db: $db, - $table: $db.members, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return f(composer); - } - - Expression readsRefs( - Expression Function($$ReadsTableFilterComposer f) f) { - final $$ReadsTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.cid, - referencedTable: $db.reads, - getReferencedColumn: (t) => t.channelCid, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$ReadsTableFilterComposer( - $db: $db, - $table: $db.reads, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return f(composer); - } -} - -class $$ChannelsTableOrderingComposer - extends Composer<_$DriftChatDatabase, $ChannelsTable> { - $$ChannelsTableOrderingComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get type => $composableBuilder( - column: $table.type, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get cid => $composableBuilder( - column: $table.cid, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get ownCapabilities => $composableBuilder( - column: $table.ownCapabilities, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get config => $composableBuilder( - column: $table.config, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get frozen => $composableBuilder( - column: $table.frozen, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get lastMessageAt => $composableBuilder( - column: $table.lastMessageAt, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get deletedAt => $composableBuilder( - column: $table.deletedAt, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get memberCount => $composableBuilder( - column: $table.memberCount, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get createdById => $composableBuilder( - column: $table.createdById, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get extraData => $composableBuilder( - column: $table.extraData, builder: (column) => ColumnOrderings(column)); -} - -class $$ChannelsTableAnnotationComposer - extends Composer<_$DriftChatDatabase, $ChannelsTable> { - $$ChannelsTableAnnotationComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - GeneratedColumn get id => - $composableBuilder(column: $table.id, builder: (column) => column); - - GeneratedColumn get type => - $composableBuilder(column: $table.type, builder: (column) => column); - - GeneratedColumn get cid => - $composableBuilder(column: $table.cid, builder: (column) => column); - - GeneratedColumnWithTypeConverter?, String> get ownCapabilities => - $composableBuilder( - column: $table.ownCapabilities, builder: (column) => column); - - GeneratedColumnWithTypeConverter, String> get config => - $composableBuilder(column: $table.config, builder: (column) => column); - - GeneratedColumn get frozen => - $composableBuilder(column: $table.frozen, builder: (column) => column); - - GeneratedColumn get lastMessageAt => $composableBuilder( - column: $table.lastMessageAt, builder: (column) => column); - - GeneratedColumn get createdAt => - $composableBuilder(column: $table.createdAt, builder: (column) => column); - - GeneratedColumn get updatedAt => - $composableBuilder(column: $table.updatedAt, builder: (column) => column); - - GeneratedColumn get deletedAt => - $composableBuilder(column: $table.deletedAt, builder: (column) => column); - - GeneratedColumn get memberCount => $composableBuilder( - column: $table.memberCount, builder: (column) => column); - - GeneratedColumn get createdById => $composableBuilder( - column: $table.createdById, builder: (column) => column); - - GeneratedColumnWithTypeConverter?, String> - get extraData => $composableBuilder( - column: $table.extraData, builder: (column) => column); - - Expression messagesRefs( - Expression Function($$MessagesTableAnnotationComposer a) f) { - final $$MessagesTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.cid, - referencedTable: $db.messages, - getReferencedColumn: (t) => t.channelCid, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$MessagesTableAnnotationComposer( - $db: $db, - $table: $db.messages, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return f(composer); - } - - Expression membersRefs( - Expression Function($$MembersTableAnnotationComposer a) f) { - final $$MembersTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.cid, - referencedTable: $db.members, - getReferencedColumn: (t) => t.channelCid, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$MembersTableAnnotationComposer( - $db: $db, - $table: $db.members, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return f(composer); - } - - Expression readsRefs( - Expression Function($$ReadsTableAnnotationComposer a) f) { - final $$ReadsTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.cid, - referencedTable: $db.reads, - getReferencedColumn: (t) => t.channelCid, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$ReadsTableAnnotationComposer( - $db: $db, - $table: $db.reads, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return f(composer); - } -} - -class $$ChannelsTableTableManager extends RootTableManager< - _$DriftChatDatabase, - $ChannelsTable, - ChannelEntity, - $$ChannelsTableFilterComposer, - $$ChannelsTableOrderingComposer, - $$ChannelsTableAnnotationComposer, - $$ChannelsTableCreateCompanionBuilder, - $$ChannelsTableUpdateCompanionBuilder, - (ChannelEntity, $$ChannelsTableReferences), - ChannelEntity, - PrefetchHooks Function( - {bool messagesRefs, bool membersRefs, bool readsRefs})> { - $$ChannelsTableTableManager(_$DriftChatDatabase db, $ChannelsTable table) - : super(TableManagerState( - db: db, - table: table, - createFilteringComposer: () => - $$ChannelsTableFilterComposer($db: db, $table: table), - createOrderingComposer: () => - $$ChannelsTableOrderingComposer($db: db, $table: table), - createComputedFieldComposer: () => - $$ChannelsTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - Value id = const Value.absent(), - Value type = const Value.absent(), - Value cid = const Value.absent(), - Value?> ownCapabilities = const Value.absent(), - Value> config = const Value.absent(), - Value frozen = const Value.absent(), - Value lastMessageAt = const Value.absent(), - Value createdAt = const Value.absent(), - Value updatedAt = const Value.absent(), - Value deletedAt = const Value.absent(), - Value memberCount = const Value.absent(), - Value createdById = const Value.absent(), - Value?> extraData = const Value.absent(), - Value rowid = const Value.absent(), - }) => - ChannelsCompanion( - id: id, - type: type, - cid: cid, - ownCapabilities: ownCapabilities, - config: config, - frozen: frozen, - lastMessageAt: lastMessageAt, - createdAt: createdAt, - updatedAt: updatedAt, - deletedAt: deletedAt, - memberCount: memberCount, - createdById: createdById, - extraData: extraData, - rowid: rowid, - ), - createCompanionCallback: ({ - required String id, - required String type, - required String cid, - Value?> ownCapabilities = const Value.absent(), - required Map config, - Value frozen = const Value.absent(), - Value lastMessageAt = const Value.absent(), - Value createdAt = const Value.absent(), - Value updatedAt = const Value.absent(), - Value deletedAt = const Value.absent(), - Value memberCount = const Value.absent(), - Value createdById = const Value.absent(), - Value?> extraData = const Value.absent(), - Value rowid = const Value.absent(), - }) => - ChannelsCompanion.insert( - id: id, - type: type, - cid: cid, - ownCapabilities: ownCapabilities, - config: config, - frozen: frozen, - lastMessageAt: lastMessageAt, - createdAt: createdAt, - updatedAt: updatedAt, - deletedAt: deletedAt, - memberCount: memberCount, - createdById: createdById, - extraData: extraData, - rowid: rowid, - ), - withReferenceMapper: (p0) => p0 - .map((e) => - (e.readTable(table), $$ChannelsTableReferences(db, table, e))) - .toList(), - prefetchHooksCallback: ( - {messagesRefs = false, membersRefs = false, readsRefs = false}) { - return PrefetchHooks( - db: db, - explicitlyWatchedTables: [ - if (messagesRefs) db.messages, - if (membersRefs) db.members, - if (readsRefs) db.reads - ], - addJoins: null, - getPrefetchedDataCallback: (items) async { - return [ - if (messagesRefs) - await $_getPrefetchedData( - currentTable: table, - referencedTable: - $$ChannelsTableReferences._messagesRefsTable(db), - managerFromTypedResult: (p0) => - $$ChannelsTableReferences(db, table, p0) - .messagesRefs, - referencedItemsForCurrentItem: - (item, referencedItems) => referencedItems - .where((e) => e.channelCid == item.cid), - typedResults: items), - if (membersRefs) - await $_getPrefetchedData( - currentTable: table, - referencedTable: - $$ChannelsTableReferences._membersRefsTable(db), - managerFromTypedResult: (p0) => - $$ChannelsTableReferences(db, table, p0) - .membersRefs, - referencedItemsForCurrentItem: - (item, referencedItems) => referencedItems - .where((e) => e.channelCid == item.cid), - typedResults: items), - if (readsRefs) - await $_getPrefetchedData( - currentTable: table, - referencedTable: - $$ChannelsTableReferences._readsRefsTable(db), - managerFromTypedResult: (p0) => - $$ChannelsTableReferences(db, table, p0).readsRefs, - referencedItemsForCurrentItem: - (item, referencedItems) => referencedItems - .where((e) => e.channelCid == item.cid), - typedResults: items) - ]; - }, - ); - }, - )); -} - -typedef $$ChannelsTableProcessedTableManager = ProcessedTableManager< - _$DriftChatDatabase, - $ChannelsTable, - ChannelEntity, - $$ChannelsTableFilterComposer, - $$ChannelsTableOrderingComposer, - $$ChannelsTableAnnotationComposer, - $$ChannelsTableCreateCompanionBuilder, - $$ChannelsTableUpdateCompanionBuilder, - (ChannelEntity, $$ChannelsTableReferences), - ChannelEntity, - PrefetchHooks Function( - {bool messagesRefs, bool membersRefs, bool readsRefs})>; -typedef $$MessagesTableCreateCompanionBuilder = MessagesCompanion Function({ - required String id, - Value messageText, - required List attachments, - required String state, - Value type, - required List mentionedUsers, - Value?> reactionCounts, - Value?> reactionScores, - Value parentId, - Value quotedMessageId, - Value pollId, - Value replyCount, - Value showInChannel, - Value shadowed, - Value command, - Value localCreatedAt, - Value remoteCreatedAt, - Value localUpdatedAt, - Value remoteUpdatedAt, - Value localDeletedAt, - Value remoteDeletedAt, - Value messageTextUpdatedAt, - Value userId, - Value pinned, - Value pinnedAt, - Value pinExpires, - Value pinnedByUserId, - required String channelCid, - Value?> i18n, - Value?> extraData, - Value rowid, -}); -typedef $$MessagesTableUpdateCompanionBuilder = MessagesCompanion Function({ - Value id, - Value messageText, - Value> attachments, - Value state, - Value type, - Value> mentionedUsers, - Value?> reactionCounts, - Value?> reactionScores, - Value parentId, - Value quotedMessageId, - Value pollId, - Value replyCount, - Value showInChannel, - Value shadowed, - Value command, - Value localCreatedAt, - Value remoteCreatedAt, - Value localUpdatedAt, - Value remoteUpdatedAt, - Value localDeletedAt, - Value remoteDeletedAt, - Value messageTextUpdatedAt, - Value userId, - Value pinned, - Value pinnedAt, - Value pinExpires, - Value pinnedByUserId, - Value channelCid, - Value?> i18n, - Value?> extraData, - Value rowid, -}); - -final class $$MessagesTableReferences - extends BaseReferences<_$DriftChatDatabase, $MessagesTable, MessageEntity> { - $$MessagesTableReferences(super.$_db, super.$_table, super.$_typedResult); - - static $ChannelsTable _channelCidTable(_$DriftChatDatabase db) => - db.channels.createAlias( - $_aliasNameGenerator(db.messages.channelCid, db.channels.cid)); - - $$ChannelsTableProcessedTableManager get channelCid { - final manager = $$ChannelsTableTableManager($_db, $_db.channels) - .filter((f) => f.cid($_item.channelCid!)); - final item = $_typedResult.readTableOrNull(_channelCidTable($_db)); - if (item == null) return manager; - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); - } - - static MultiTypedResultKey<$ReactionsTable, List> - _reactionsRefsTable(_$DriftChatDatabase db) => - MultiTypedResultKey.fromTable(db.reactions, - aliasName: - $_aliasNameGenerator(db.messages.id, db.reactions.messageId)); - - $$ReactionsTableProcessedTableManager get reactionsRefs { - final manager = $$ReactionsTableTableManager($_db, $_db.reactions) - .filter((f) => f.messageId.id($_item.id)); - - final cache = $_typedResult.readTableOrNull(_reactionsRefsTable($_db)); - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: cache)); - } -} - -class $$MessagesTableFilterComposer - extends Composer<_$DriftChatDatabase, $MessagesTable> { - $$MessagesTableFilterComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => ColumnFilters(column)); - - ColumnFilters get messageText => $composableBuilder( - column: $table.messageText, builder: (column) => ColumnFilters(column)); - - ColumnWithTypeConverterFilters, List, String> - get attachments => $composableBuilder( - column: $table.attachments, - builder: (column) => ColumnWithTypeConverterFilters(column)); - - ColumnFilters get state => $composableBuilder( - column: $table.state, builder: (column) => ColumnFilters(column)); - - ColumnFilters get type => $composableBuilder( - column: $table.type, builder: (column) => ColumnFilters(column)); - - ColumnWithTypeConverterFilters, List, String> - get mentionedUsers => $composableBuilder( - column: $table.mentionedUsers, - builder: (column) => ColumnWithTypeConverterFilters(column)); - - ColumnWithTypeConverterFilters?, Map, String> - get reactionCounts => $composableBuilder( - column: $table.reactionCounts, - builder: (column) => ColumnWithTypeConverterFilters(column)); - - ColumnWithTypeConverterFilters?, Map, String> - get reactionScores => $composableBuilder( - column: $table.reactionScores, - builder: (column) => ColumnWithTypeConverterFilters(column)); - - ColumnFilters get parentId => $composableBuilder( - column: $table.parentId, builder: (column) => ColumnFilters(column)); - - ColumnFilters get quotedMessageId => $composableBuilder( - column: $table.quotedMessageId, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get pollId => $composableBuilder( - column: $table.pollId, builder: (column) => ColumnFilters(column)); - - ColumnFilters get replyCount => $composableBuilder( - column: $table.replyCount, builder: (column) => ColumnFilters(column)); - - ColumnFilters get showInChannel => $composableBuilder( - column: $table.showInChannel, builder: (column) => ColumnFilters(column)); - - ColumnFilters get shadowed => $composableBuilder( - column: $table.shadowed, builder: (column) => ColumnFilters(column)); - - ColumnFilters get command => $composableBuilder( - column: $table.command, builder: (column) => ColumnFilters(column)); - - ColumnFilters get localCreatedAt => $composableBuilder( - column: $table.localCreatedAt, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get remoteCreatedAt => $composableBuilder( - column: $table.remoteCreatedAt, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get localUpdatedAt => $composableBuilder( - column: $table.localUpdatedAt, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get remoteUpdatedAt => $composableBuilder( - column: $table.remoteUpdatedAt, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get localDeletedAt => $composableBuilder( - column: $table.localDeletedAt, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get remoteDeletedAt => $composableBuilder( - column: $table.remoteDeletedAt, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get messageTextUpdatedAt => $composableBuilder( - column: $table.messageTextUpdatedAt, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get userId => $composableBuilder( - column: $table.userId, builder: (column) => ColumnFilters(column)); - - ColumnFilters get pinned => $composableBuilder( - column: $table.pinned, builder: (column) => ColumnFilters(column)); - - ColumnFilters get pinnedAt => $composableBuilder( - column: $table.pinnedAt, builder: (column) => ColumnFilters(column)); - - ColumnFilters get pinExpires => $composableBuilder( - column: $table.pinExpires, builder: (column) => ColumnFilters(column)); - - ColumnFilters get pinnedByUserId => $composableBuilder( - column: $table.pinnedByUserId, - builder: (column) => ColumnFilters(column)); - - ColumnWithTypeConverterFilters?, Map, - String> - get i18n => $composableBuilder( - column: $table.i18n, - builder: (column) => ColumnWithTypeConverterFilters(column)); - - ColumnWithTypeConverterFilters?, Map, - String> - get extraData => $composableBuilder( - column: $table.extraData, - builder: (column) => ColumnWithTypeConverterFilters(column)); - - $$ChannelsTableFilterComposer get channelCid { - final $$ChannelsTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.channelCid, - referencedTable: $db.channels, - getReferencedColumn: (t) => t.cid, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$ChannelsTableFilterComposer( - $db: $db, - $table: $db.channels, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } - - Expression reactionsRefs( - Expression Function($$ReactionsTableFilterComposer f) f) { - final $$ReactionsTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.id, - referencedTable: $db.reactions, - getReferencedColumn: (t) => t.messageId, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$ReactionsTableFilterComposer( - $db: $db, - $table: $db.reactions, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return f(composer); - } -} - -class $$MessagesTableOrderingComposer - extends Composer<_$DriftChatDatabase, $MessagesTable> { - $$MessagesTableOrderingComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get messageText => $composableBuilder( - column: $table.messageText, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get attachments => $composableBuilder( - column: $table.attachments, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get state => $composableBuilder( - column: $table.state, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get type => $composableBuilder( - column: $table.type, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get mentionedUsers => $composableBuilder( - column: $table.mentionedUsers, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get reactionCounts => $composableBuilder( - column: $table.reactionCounts, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get reactionScores => $composableBuilder( - column: $table.reactionScores, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get parentId => $composableBuilder( - column: $table.parentId, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get quotedMessageId => $composableBuilder( - column: $table.quotedMessageId, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get pollId => $composableBuilder( - column: $table.pollId, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get replyCount => $composableBuilder( - column: $table.replyCount, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get showInChannel => $composableBuilder( - column: $table.showInChannel, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get shadowed => $composableBuilder( - column: $table.shadowed, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get command => $composableBuilder( - column: $table.command, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get localCreatedAt => $composableBuilder( - column: $table.localCreatedAt, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get remoteCreatedAt => $composableBuilder( - column: $table.remoteCreatedAt, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get localUpdatedAt => $composableBuilder( - column: $table.localUpdatedAt, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get remoteUpdatedAt => $composableBuilder( - column: $table.remoteUpdatedAt, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get localDeletedAt => $composableBuilder( - column: $table.localDeletedAt, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get remoteDeletedAt => $composableBuilder( - column: $table.remoteDeletedAt, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get messageTextUpdatedAt => $composableBuilder( - column: $table.messageTextUpdatedAt, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get userId => $composableBuilder( - column: $table.userId, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get pinned => $composableBuilder( - column: $table.pinned, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get pinnedAt => $composableBuilder( - column: $table.pinnedAt, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get pinExpires => $composableBuilder( - column: $table.pinExpires, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get pinnedByUserId => $composableBuilder( - column: $table.pinnedByUserId, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get i18n => $composableBuilder( - column: $table.i18n, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get extraData => $composableBuilder( - column: $table.extraData, builder: (column) => ColumnOrderings(column)); - - $$ChannelsTableOrderingComposer get channelCid { - final $$ChannelsTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.channelCid, - referencedTable: $db.channels, - getReferencedColumn: (t) => t.cid, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$ChannelsTableOrderingComposer( - $db: $db, - $table: $db.channels, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } -} - -class $$MessagesTableAnnotationComposer - extends Composer<_$DriftChatDatabase, $MessagesTable> { - $$MessagesTableAnnotationComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - GeneratedColumn get id => - $composableBuilder(column: $table.id, builder: (column) => column); - - GeneratedColumn get messageText => $composableBuilder( - column: $table.messageText, builder: (column) => column); - - GeneratedColumnWithTypeConverter, String> get attachments => - $composableBuilder( - column: $table.attachments, builder: (column) => column); - - GeneratedColumn get state => - $composableBuilder(column: $table.state, builder: (column) => column); - - GeneratedColumn get type => - $composableBuilder(column: $table.type, builder: (column) => column); - - GeneratedColumnWithTypeConverter, String> get mentionedUsers => - $composableBuilder( - column: $table.mentionedUsers, builder: (column) => column); - - GeneratedColumnWithTypeConverter?, String> - get reactionCounts => $composableBuilder( - column: $table.reactionCounts, builder: (column) => column); - - GeneratedColumnWithTypeConverter?, String> - get reactionScores => $composableBuilder( - column: $table.reactionScores, builder: (column) => column); - - GeneratedColumn get parentId => - $composableBuilder(column: $table.parentId, builder: (column) => column); - - GeneratedColumn get quotedMessageId => $composableBuilder( - column: $table.quotedMessageId, builder: (column) => column); - - GeneratedColumn get pollId => - $composableBuilder(column: $table.pollId, builder: (column) => column); - - GeneratedColumn get replyCount => $composableBuilder( - column: $table.replyCount, builder: (column) => column); - - GeneratedColumn get showInChannel => $composableBuilder( - column: $table.showInChannel, builder: (column) => column); - - GeneratedColumn get shadowed => - $composableBuilder(column: $table.shadowed, builder: (column) => column); - - GeneratedColumn get command => - $composableBuilder(column: $table.command, builder: (column) => column); - - GeneratedColumn get localCreatedAt => $composableBuilder( - column: $table.localCreatedAt, builder: (column) => column); - - GeneratedColumn get remoteCreatedAt => $composableBuilder( - column: $table.remoteCreatedAt, builder: (column) => column); - - GeneratedColumn get localUpdatedAt => $composableBuilder( - column: $table.localUpdatedAt, builder: (column) => column); - - GeneratedColumn get remoteUpdatedAt => $composableBuilder( - column: $table.remoteUpdatedAt, builder: (column) => column); - - GeneratedColumn get localDeletedAt => $composableBuilder( - column: $table.localDeletedAt, builder: (column) => column); - - GeneratedColumn get remoteDeletedAt => $composableBuilder( - column: $table.remoteDeletedAt, builder: (column) => column); - - GeneratedColumn get messageTextUpdatedAt => $composableBuilder( - column: $table.messageTextUpdatedAt, builder: (column) => column); - - GeneratedColumn get userId => - $composableBuilder(column: $table.userId, builder: (column) => column); - - GeneratedColumn get pinned => - $composableBuilder(column: $table.pinned, builder: (column) => column); - - GeneratedColumn get pinnedAt => - $composableBuilder(column: $table.pinnedAt, builder: (column) => column); - - GeneratedColumn get pinExpires => $composableBuilder( - column: $table.pinExpires, builder: (column) => column); - - GeneratedColumn get pinnedByUserId => $composableBuilder( - column: $table.pinnedByUserId, builder: (column) => column); - - GeneratedColumnWithTypeConverter?, String> get i18n => - $composableBuilder(column: $table.i18n, builder: (column) => column); - - GeneratedColumnWithTypeConverter?, String> - get extraData => $composableBuilder( - column: $table.extraData, builder: (column) => column); - - $$ChannelsTableAnnotationComposer get channelCid { - final $$ChannelsTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.channelCid, - referencedTable: $db.channels, - getReferencedColumn: (t) => t.cid, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$ChannelsTableAnnotationComposer( - $db: $db, - $table: $db.channels, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } - - Expression reactionsRefs( - Expression Function($$ReactionsTableAnnotationComposer a) f) { - final $$ReactionsTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.id, - referencedTable: $db.reactions, - getReferencedColumn: (t) => t.messageId, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$ReactionsTableAnnotationComposer( - $db: $db, - $table: $db.reactions, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return f(composer); - } -} - -class $$MessagesTableTableManager extends RootTableManager< - _$DriftChatDatabase, - $MessagesTable, - MessageEntity, - $$MessagesTableFilterComposer, - $$MessagesTableOrderingComposer, - $$MessagesTableAnnotationComposer, - $$MessagesTableCreateCompanionBuilder, - $$MessagesTableUpdateCompanionBuilder, - (MessageEntity, $$MessagesTableReferences), - MessageEntity, - PrefetchHooks Function({bool channelCid, bool reactionsRefs})> { - $$MessagesTableTableManager(_$DriftChatDatabase db, $MessagesTable table) - : super(TableManagerState( - db: db, - table: table, - createFilteringComposer: () => - $$MessagesTableFilterComposer($db: db, $table: table), - createOrderingComposer: () => - $$MessagesTableOrderingComposer($db: db, $table: table), - createComputedFieldComposer: () => - $$MessagesTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - Value id = const Value.absent(), - Value messageText = const Value.absent(), - Value> attachments = const Value.absent(), - Value state = const Value.absent(), - Value type = const Value.absent(), - Value> mentionedUsers = const Value.absent(), - Value?> reactionCounts = const Value.absent(), - Value?> reactionScores = const Value.absent(), - Value parentId = const Value.absent(), - Value quotedMessageId = const Value.absent(), - Value pollId = const Value.absent(), - Value replyCount = const Value.absent(), - Value showInChannel = const Value.absent(), - Value shadowed = const Value.absent(), - Value command = const Value.absent(), - Value localCreatedAt = const Value.absent(), - Value remoteCreatedAt = const Value.absent(), - Value localUpdatedAt = const Value.absent(), - Value remoteUpdatedAt = const Value.absent(), - Value localDeletedAt = const Value.absent(), - Value remoteDeletedAt = const Value.absent(), - Value messageTextUpdatedAt = const Value.absent(), - Value userId = const Value.absent(), - Value pinned = const Value.absent(), - Value pinnedAt = const Value.absent(), - Value pinExpires = const Value.absent(), - Value pinnedByUserId = const Value.absent(), - Value channelCid = const Value.absent(), - Value?> i18n = const Value.absent(), - Value?> extraData = const Value.absent(), - Value rowid = const Value.absent(), - }) => - MessagesCompanion( - id: id, - messageText: messageText, - attachments: attachments, - state: state, - type: type, - mentionedUsers: mentionedUsers, - reactionCounts: reactionCounts, - reactionScores: reactionScores, - parentId: parentId, - quotedMessageId: quotedMessageId, - pollId: pollId, - replyCount: replyCount, - showInChannel: showInChannel, - shadowed: shadowed, - command: command, - localCreatedAt: localCreatedAt, - remoteCreatedAt: remoteCreatedAt, - localUpdatedAt: localUpdatedAt, - remoteUpdatedAt: remoteUpdatedAt, - localDeletedAt: localDeletedAt, - remoteDeletedAt: remoteDeletedAt, - messageTextUpdatedAt: messageTextUpdatedAt, - userId: userId, - pinned: pinned, - pinnedAt: pinnedAt, - pinExpires: pinExpires, - pinnedByUserId: pinnedByUserId, - channelCid: channelCid, - i18n: i18n, - extraData: extraData, - rowid: rowid, - ), - createCompanionCallback: ({ - required String id, - Value messageText = const Value.absent(), - required List attachments, - required String state, - Value type = const Value.absent(), - required List mentionedUsers, - Value?> reactionCounts = const Value.absent(), - Value?> reactionScores = const Value.absent(), - Value parentId = const Value.absent(), - Value quotedMessageId = const Value.absent(), - Value pollId = const Value.absent(), - Value replyCount = const Value.absent(), - Value showInChannel = const Value.absent(), - Value shadowed = const Value.absent(), - Value command = const Value.absent(), - Value localCreatedAt = const Value.absent(), - Value remoteCreatedAt = const Value.absent(), - Value localUpdatedAt = const Value.absent(), - Value remoteUpdatedAt = const Value.absent(), - Value localDeletedAt = const Value.absent(), - Value remoteDeletedAt = const Value.absent(), - Value messageTextUpdatedAt = const Value.absent(), - Value userId = const Value.absent(), - Value pinned = const Value.absent(), - Value pinnedAt = const Value.absent(), - Value pinExpires = const Value.absent(), - Value pinnedByUserId = const Value.absent(), - required String channelCid, - Value?> i18n = const Value.absent(), - Value?> extraData = const Value.absent(), - Value rowid = const Value.absent(), - }) => - MessagesCompanion.insert( - id: id, - messageText: messageText, - attachments: attachments, - state: state, - type: type, - mentionedUsers: mentionedUsers, - reactionCounts: reactionCounts, - reactionScores: reactionScores, - parentId: parentId, - quotedMessageId: quotedMessageId, - pollId: pollId, - replyCount: replyCount, - showInChannel: showInChannel, - shadowed: shadowed, - command: command, - localCreatedAt: localCreatedAt, - remoteCreatedAt: remoteCreatedAt, - localUpdatedAt: localUpdatedAt, - remoteUpdatedAt: remoteUpdatedAt, - localDeletedAt: localDeletedAt, - remoteDeletedAt: remoteDeletedAt, - messageTextUpdatedAt: messageTextUpdatedAt, - userId: userId, - pinned: pinned, - pinnedAt: pinnedAt, - pinExpires: pinExpires, - pinnedByUserId: pinnedByUserId, - channelCid: channelCid, - i18n: i18n, - extraData: extraData, - rowid: rowid, - ), - withReferenceMapper: (p0) => p0 - .map((e) => - (e.readTable(table), $$MessagesTableReferences(db, table, e))) - .toList(), - prefetchHooksCallback: ({channelCid = false, reactionsRefs = false}) { - return PrefetchHooks( - db: db, - explicitlyWatchedTables: [if (reactionsRefs) db.reactions], - addJoins: < - T extends TableManagerState< - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic>>(state) { - if (channelCid) { - state = state.withJoin( - currentTable: table, - currentColumn: table.channelCid, - referencedTable: - $$MessagesTableReferences._channelCidTable(db), - referencedColumn: - $$MessagesTableReferences._channelCidTable(db).cid, - ) as T; - } - - return state; - }, - getPrefetchedDataCallback: (items) async { - return [ - if (reactionsRefs) - await $_getPrefetchedData( - currentTable: table, - referencedTable: - $$MessagesTableReferences._reactionsRefsTable(db), - managerFromTypedResult: (p0) => - $$MessagesTableReferences(db, table, p0) - .reactionsRefs, - referencedItemsForCurrentItem: - (item, referencedItems) => referencedItems - .where((e) => e.messageId == item.id), - typedResults: items) - ]; - }, - ); - }, - )); -} - -typedef $$MessagesTableProcessedTableManager = ProcessedTableManager< - _$DriftChatDatabase, - $MessagesTable, - MessageEntity, - $$MessagesTableFilterComposer, - $$MessagesTableOrderingComposer, - $$MessagesTableAnnotationComposer, - $$MessagesTableCreateCompanionBuilder, - $$MessagesTableUpdateCompanionBuilder, - (MessageEntity, $$MessagesTableReferences), - MessageEntity, - PrefetchHooks Function({bool channelCid, bool reactionsRefs})>; -typedef $$PinnedMessagesTableCreateCompanionBuilder = PinnedMessagesCompanion - Function({ - required String id, - Value messageText, - required List attachments, - required String state, - Value type, - required List mentionedUsers, - Value?> reactionCounts, - Value?> reactionScores, - Value parentId, - Value quotedMessageId, - Value pollId, - Value replyCount, - Value showInChannel, - Value shadowed, - Value command, - Value localCreatedAt, - Value remoteCreatedAt, - Value localUpdatedAt, - Value remoteUpdatedAt, - Value localDeletedAt, - Value remoteDeletedAt, - Value messageTextUpdatedAt, - Value userId, - Value pinned, - Value pinnedAt, - Value pinExpires, - Value pinnedByUserId, - required String channelCid, - Value?> i18n, - Value?> extraData, - Value rowid, -}); -typedef $$PinnedMessagesTableUpdateCompanionBuilder = PinnedMessagesCompanion - Function({ - Value id, - Value messageText, - Value> attachments, - Value state, - Value type, - Value> mentionedUsers, - Value?> reactionCounts, - Value?> reactionScores, - Value parentId, - Value quotedMessageId, - Value pollId, - Value replyCount, - Value showInChannel, - Value shadowed, - Value command, - Value localCreatedAt, - Value remoteCreatedAt, - Value localUpdatedAt, - Value remoteUpdatedAt, - Value localDeletedAt, - Value remoteDeletedAt, - Value messageTextUpdatedAt, - Value userId, - Value pinned, - Value pinnedAt, - Value pinExpires, - Value pinnedByUserId, - Value channelCid, - Value?> i18n, - Value?> extraData, - Value rowid, -}); - -final class $$PinnedMessagesTableReferences extends BaseReferences< - _$DriftChatDatabase, $PinnedMessagesTable, PinnedMessageEntity> { - $$PinnedMessagesTableReferences( - super.$_db, super.$_table, super.$_typedResult); - - static MultiTypedResultKey<$PinnedMessageReactionsTable, - List> _pinnedMessageReactionsRefsTable( - _$DriftChatDatabase db) => - MultiTypedResultKey.fromTable(db.pinnedMessageReactions, - aliasName: $_aliasNameGenerator( - db.pinnedMessages.id, db.pinnedMessageReactions.messageId)); - - $$PinnedMessageReactionsTableProcessedTableManager - get pinnedMessageReactionsRefs { - final manager = $$PinnedMessageReactionsTableTableManager( - $_db, $_db.pinnedMessageReactions) - .filter((f) => f.messageId.id($_item.id)); - - final cache = - $_typedResult.readTableOrNull(_pinnedMessageReactionsRefsTable($_db)); - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: cache)); - } -} - -class $$PinnedMessagesTableFilterComposer - extends Composer<_$DriftChatDatabase, $PinnedMessagesTable> { - $$PinnedMessagesTableFilterComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => ColumnFilters(column)); - - ColumnFilters get messageText => $composableBuilder( - column: $table.messageText, builder: (column) => ColumnFilters(column)); - - ColumnWithTypeConverterFilters, List, String> - get attachments => $composableBuilder( - column: $table.attachments, - builder: (column) => ColumnWithTypeConverterFilters(column)); - - ColumnFilters get state => $composableBuilder( - column: $table.state, builder: (column) => ColumnFilters(column)); - - ColumnFilters get type => $composableBuilder( - column: $table.type, builder: (column) => ColumnFilters(column)); - - ColumnWithTypeConverterFilters, List, String> - get mentionedUsers => $composableBuilder( - column: $table.mentionedUsers, - builder: (column) => ColumnWithTypeConverterFilters(column)); - - ColumnWithTypeConverterFilters?, Map, String> - get reactionCounts => $composableBuilder( - column: $table.reactionCounts, - builder: (column) => ColumnWithTypeConverterFilters(column)); - - ColumnWithTypeConverterFilters?, Map, String> - get reactionScores => $composableBuilder( - column: $table.reactionScores, - builder: (column) => ColumnWithTypeConverterFilters(column)); - - ColumnFilters get parentId => $composableBuilder( - column: $table.parentId, builder: (column) => ColumnFilters(column)); - - ColumnFilters get quotedMessageId => $composableBuilder( - column: $table.quotedMessageId, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get pollId => $composableBuilder( - column: $table.pollId, builder: (column) => ColumnFilters(column)); - - ColumnFilters get replyCount => $composableBuilder( - column: $table.replyCount, builder: (column) => ColumnFilters(column)); - - ColumnFilters get showInChannel => $composableBuilder( - column: $table.showInChannel, builder: (column) => ColumnFilters(column)); - - ColumnFilters get shadowed => $composableBuilder( - column: $table.shadowed, builder: (column) => ColumnFilters(column)); - - ColumnFilters get command => $composableBuilder( - column: $table.command, builder: (column) => ColumnFilters(column)); - - ColumnFilters get localCreatedAt => $composableBuilder( - column: $table.localCreatedAt, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get remoteCreatedAt => $composableBuilder( - column: $table.remoteCreatedAt, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get localUpdatedAt => $composableBuilder( - column: $table.localUpdatedAt, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get remoteUpdatedAt => $composableBuilder( - column: $table.remoteUpdatedAt, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get localDeletedAt => $composableBuilder( - column: $table.localDeletedAt, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get remoteDeletedAt => $composableBuilder( - column: $table.remoteDeletedAt, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get messageTextUpdatedAt => $composableBuilder( - column: $table.messageTextUpdatedAt, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get userId => $composableBuilder( - column: $table.userId, builder: (column) => ColumnFilters(column)); - - ColumnFilters get pinned => $composableBuilder( - column: $table.pinned, builder: (column) => ColumnFilters(column)); - - ColumnFilters get pinnedAt => $composableBuilder( - column: $table.pinnedAt, builder: (column) => ColumnFilters(column)); - - ColumnFilters get pinExpires => $composableBuilder( - column: $table.pinExpires, builder: (column) => ColumnFilters(column)); - - ColumnFilters get pinnedByUserId => $composableBuilder( - column: $table.pinnedByUserId, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get channelCid => $composableBuilder( - column: $table.channelCid, builder: (column) => ColumnFilters(column)); - - ColumnWithTypeConverterFilters?, Map, - String> - get i18n => $composableBuilder( - column: $table.i18n, - builder: (column) => ColumnWithTypeConverterFilters(column)); - - ColumnWithTypeConverterFilters?, Map, - String> - get extraData => $composableBuilder( - column: $table.extraData, - builder: (column) => ColumnWithTypeConverterFilters(column)); - - Expression pinnedMessageReactionsRefs( - Expression Function($$PinnedMessageReactionsTableFilterComposer f) - f) { - final $$PinnedMessageReactionsTableFilterComposer composer = - $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.id, - referencedTable: $db.pinnedMessageReactions, - getReferencedColumn: (t) => t.messageId, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$PinnedMessageReactionsTableFilterComposer( - $db: $db, - $table: $db.pinnedMessageReactions, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return f(composer); - } -} - -class $$PinnedMessagesTableOrderingComposer - extends Composer<_$DriftChatDatabase, $PinnedMessagesTable> { - $$PinnedMessagesTableOrderingComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get messageText => $composableBuilder( - column: $table.messageText, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get attachments => $composableBuilder( - column: $table.attachments, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get state => $composableBuilder( - column: $table.state, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get type => $composableBuilder( - column: $table.type, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get mentionedUsers => $composableBuilder( - column: $table.mentionedUsers, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get reactionCounts => $composableBuilder( - column: $table.reactionCounts, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get reactionScores => $composableBuilder( - column: $table.reactionScores, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get parentId => $composableBuilder( - column: $table.parentId, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get quotedMessageId => $composableBuilder( - column: $table.quotedMessageId, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get pollId => $composableBuilder( - column: $table.pollId, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get replyCount => $composableBuilder( - column: $table.replyCount, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get showInChannel => $composableBuilder( - column: $table.showInChannel, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get shadowed => $composableBuilder( - column: $table.shadowed, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get command => $composableBuilder( - column: $table.command, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get localCreatedAt => $composableBuilder( - column: $table.localCreatedAt, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get remoteCreatedAt => $composableBuilder( - column: $table.remoteCreatedAt, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get localUpdatedAt => $composableBuilder( - column: $table.localUpdatedAt, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get remoteUpdatedAt => $composableBuilder( - column: $table.remoteUpdatedAt, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get localDeletedAt => $composableBuilder( - column: $table.localDeletedAt, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get remoteDeletedAt => $composableBuilder( - column: $table.remoteDeletedAt, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get messageTextUpdatedAt => $composableBuilder( - column: $table.messageTextUpdatedAt, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get userId => $composableBuilder( - column: $table.userId, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get pinned => $composableBuilder( - column: $table.pinned, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get pinnedAt => $composableBuilder( - column: $table.pinnedAt, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get pinExpires => $composableBuilder( - column: $table.pinExpires, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get pinnedByUserId => $composableBuilder( - column: $table.pinnedByUserId, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get channelCid => $composableBuilder( - column: $table.channelCid, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get i18n => $composableBuilder( - column: $table.i18n, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get extraData => $composableBuilder( - column: $table.extraData, builder: (column) => ColumnOrderings(column)); -} - -class $$PinnedMessagesTableAnnotationComposer - extends Composer<_$DriftChatDatabase, $PinnedMessagesTable> { - $$PinnedMessagesTableAnnotationComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - GeneratedColumn get id => - $composableBuilder(column: $table.id, builder: (column) => column); - - GeneratedColumn get messageText => $composableBuilder( - column: $table.messageText, builder: (column) => column); - - GeneratedColumnWithTypeConverter, String> get attachments => - $composableBuilder( - column: $table.attachments, builder: (column) => column); - - GeneratedColumn get state => - $composableBuilder(column: $table.state, builder: (column) => column); - - GeneratedColumn get type => - $composableBuilder(column: $table.type, builder: (column) => column); - - GeneratedColumnWithTypeConverter, String> get mentionedUsers => - $composableBuilder( - column: $table.mentionedUsers, builder: (column) => column); - - GeneratedColumnWithTypeConverter?, String> - get reactionCounts => $composableBuilder( - column: $table.reactionCounts, builder: (column) => column); - - GeneratedColumnWithTypeConverter?, String> - get reactionScores => $composableBuilder( - column: $table.reactionScores, builder: (column) => column); - - GeneratedColumn get parentId => - $composableBuilder(column: $table.parentId, builder: (column) => column); - - GeneratedColumn get quotedMessageId => $composableBuilder( - column: $table.quotedMessageId, builder: (column) => column); - - GeneratedColumn get pollId => - $composableBuilder(column: $table.pollId, builder: (column) => column); - - GeneratedColumn get replyCount => $composableBuilder( - column: $table.replyCount, builder: (column) => column); - - GeneratedColumn get showInChannel => $composableBuilder( - column: $table.showInChannel, builder: (column) => column); - - GeneratedColumn get shadowed => - $composableBuilder(column: $table.shadowed, builder: (column) => column); - - GeneratedColumn get command => - $composableBuilder(column: $table.command, builder: (column) => column); - - GeneratedColumn get localCreatedAt => $composableBuilder( - column: $table.localCreatedAt, builder: (column) => column); - - GeneratedColumn get remoteCreatedAt => $composableBuilder( - column: $table.remoteCreatedAt, builder: (column) => column); - - GeneratedColumn get localUpdatedAt => $composableBuilder( - column: $table.localUpdatedAt, builder: (column) => column); - - GeneratedColumn get remoteUpdatedAt => $composableBuilder( - column: $table.remoteUpdatedAt, builder: (column) => column); - - GeneratedColumn get localDeletedAt => $composableBuilder( - column: $table.localDeletedAt, builder: (column) => column); - - GeneratedColumn get remoteDeletedAt => $composableBuilder( - column: $table.remoteDeletedAt, builder: (column) => column); - - GeneratedColumn get messageTextUpdatedAt => $composableBuilder( - column: $table.messageTextUpdatedAt, builder: (column) => column); - - GeneratedColumn get userId => - $composableBuilder(column: $table.userId, builder: (column) => column); - - GeneratedColumn get pinned => - $composableBuilder(column: $table.pinned, builder: (column) => column); - - GeneratedColumn get pinnedAt => - $composableBuilder(column: $table.pinnedAt, builder: (column) => column); - - GeneratedColumn get pinExpires => $composableBuilder( - column: $table.pinExpires, builder: (column) => column); - - GeneratedColumn get pinnedByUserId => $composableBuilder( - column: $table.pinnedByUserId, builder: (column) => column); - - GeneratedColumn get channelCid => $composableBuilder( - column: $table.channelCid, builder: (column) => column); - - GeneratedColumnWithTypeConverter?, String> get i18n => - $composableBuilder(column: $table.i18n, builder: (column) => column); - - GeneratedColumnWithTypeConverter?, String> - get extraData => $composableBuilder( - column: $table.extraData, builder: (column) => column); - - Expression pinnedMessageReactionsRefs( - Expression Function($$PinnedMessageReactionsTableAnnotationComposer a) - f) { - final $$PinnedMessageReactionsTableAnnotationComposer composer = - $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.id, - referencedTable: $db.pinnedMessageReactions, - getReferencedColumn: (t) => t.messageId, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$PinnedMessageReactionsTableAnnotationComposer( - $db: $db, - $table: $db.pinnedMessageReactions, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return f(composer); - } -} - -class $$PinnedMessagesTableTableManager extends RootTableManager< - _$DriftChatDatabase, - $PinnedMessagesTable, - PinnedMessageEntity, - $$PinnedMessagesTableFilterComposer, - $$PinnedMessagesTableOrderingComposer, - $$PinnedMessagesTableAnnotationComposer, - $$PinnedMessagesTableCreateCompanionBuilder, - $$PinnedMessagesTableUpdateCompanionBuilder, - (PinnedMessageEntity, $$PinnedMessagesTableReferences), - PinnedMessageEntity, - PrefetchHooks Function({bool pinnedMessageReactionsRefs})> { - $$PinnedMessagesTableTableManager( - _$DriftChatDatabase db, $PinnedMessagesTable table) - : super(TableManagerState( - db: db, - table: table, - createFilteringComposer: () => - $$PinnedMessagesTableFilterComposer($db: db, $table: table), - createOrderingComposer: () => - $$PinnedMessagesTableOrderingComposer($db: db, $table: table), - createComputedFieldComposer: () => - $$PinnedMessagesTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - Value id = const Value.absent(), - Value messageText = const Value.absent(), - Value> attachments = const Value.absent(), - Value state = const Value.absent(), - Value type = const Value.absent(), - Value> mentionedUsers = const Value.absent(), - Value?> reactionCounts = const Value.absent(), - Value?> reactionScores = const Value.absent(), - Value parentId = const Value.absent(), - Value quotedMessageId = const Value.absent(), - Value pollId = const Value.absent(), - Value replyCount = const Value.absent(), - Value showInChannel = const Value.absent(), - Value shadowed = const Value.absent(), - Value command = const Value.absent(), - Value localCreatedAt = const Value.absent(), - Value remoteCreatedAt = const Value.absent(), - Value localUpdatedAt = const Value.absent(), - Value remoteUpdatedAt = const Value.absent(), - Value localDeletedAt = const Value.absent(), - Value remoteDeletedAt = const Value.absent(), - Value messageTextUpdatedAt = const Value.absent(), - Value userId = const Value.absent(), - Value pinned = const Value.absent(), - Value pinnedAt = const Value.absent(), - Value pinExpires = const Value.absent(), - Value pinnedByUserId = const Value.absent(), - Value channelCid = const Value.absent(), - Value?> i18n = const Value.absent(), - Value?> extraData = const Value.absent(), - Value rowid = const Value.absent(), - }) => - PinnedMessagesCompanion( - id: id, - messageText: messageText, - attachments: attachments, - state: state, - type: type, - mentionedUsers: mentionedUsers, - reactionCounts: reactionCounts, - reactionScores: reactionScores, - parentId: parentId, - quotedMessageId: quotedMessageId, - pollId: pollId, - replyCount: replyCount, - showInChannel: showInChannel, - shadowed: shadowed, - command: command, - localCreatedAt: localCreatedAt, - remoteCreatedAt: remoteCreatedAt, - localUpdatedAt: localUpdatedAt, - remoteUpdatedAt: remoteUpdatedAt, - localDeletedAt: localDeletedAt, - remoteDeletedAt: remoteDeletedAt, - messageTextUpdatedAt: messageTextUpdatedAt, - userId: userId, - pinned: pinned, - pinnedAt: pinnedAt, - pinExpires: pinExpires, - pinnedByUserId: pinnedByUserId, - channelCid: channelCid, - i18n: i18n, - extraData: extraData, - rowid: rowid, - ), - createCompanionCallback: ({ - required String id, - Value messageText = const Value.absent(), - required List attachments, - required String state, - Value type = const Value.absent(), - required List mentionedUsers, - Value?> reactionCounts = const Value.absent(), - Value?> reactionScores = const Value.absent(), - Value parentId = const Value.absent(), - Value quotedMessageId = const Value.absent(), - Value pollId = const Value.absent(), - Value replyCount = const Value.absent(), - Value showInChannel = const Value.absent(), - Value shadowed = const Value.absent(), - Value command = const Value.absent(), - Value localCreatedAt = const Value.absent(), - Value remoteCreatedAt = const Value.absent(), - Value localUpdatedAt = const Value.absent(), - Value remoteUpdatedAt = const Value.absent(), - Value localDeletedAt = const Value.absent(), - Value remoteDeletedAt = const Value.absent(), - Value messageTextUpdatedAt = const Value.absent(), - Value userId = const Value.absent(), - Value pinned = const Value.absent(), - Value pinnedAt = const Value.absent(), - Value pinExpires = const Value.absent(), - Value pinnedByUserId = const Value.absent(), - required String channelCid, - Value?> i18n = const Value.absent(), - Value?> extraData = const Value.absent(), - Value rowid = const Value.absent(), - }) => - PinnedMessagesCompanion.insert( - id: id, - messageText: messageText, - attachments: attachments, - state: state, - type: type, - mentionedUsers: mentionedUsers, - reactionCounts: reactionCounts, - reactionScores: reactionScores, - parentId: parentId, - quotedMessageId: quotedMessageId, - pollId: pollId, - replyCount: replyCount, - showInChannel: showInChannel, - shadowed: shadowed, - command: command, - localCreatedAt: localCreatedAt, - remoteCreatedAt: remoteCreatedAt, - localUpdatedAt: localUpdatedAt, - remoteUpdatedAt: remoteUpdatedAt, - localDeletedAt: localDeletedAt, - remoteDeletedAt: remoteDeletedAt, - messageTextUpdatedAt: messageTextUpdatedAt, - userId: userId, - pinned: pinned, - pinnedAt: pinnedAt, - pinExpires: pinExpires, - pinnedByUserId: pinnedByUserId, - channelCid: channelCid, - i18n: i18n, - extraData: extraData, - rowid: rowid, - ), - withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - $$PinnedMessagesTableReferences(db, table, e) - )) - .toList(), - prefetchHooksCallback: ({pinnedMessageReactionsRefs = false}) { - return PrefetchHooks( - db: db, - explicitlyWatchedTables: [ - if (pinnedMessageReactionsRefs) db.pinnedMessageReactions - ], - addJoins: null, - getPrefetchedDataCallback: (items) async { - return [ - if (pinnedMessageReactionsRefs) - await $_getPrefetchedData( - currentTable: table, - referencedTable: $$PinnedMessagesTableReferences - ._pinnedMessageReactionsRefsTable(db), - managerFromTypedResult: (p0) => - $$PinnedMessagesTableReferences(db, table, p0) - .pinnedMessageReactionsRefs, - referencedItemsForCurrentItem: - (item, referencedItems) => referencedItems - .where((e) => e.messageId == item.id), - typedResults: items) - ]; - }, - ); - }, - )); -} - -typedef $$PinnedMessagesTableProcessedTableManager = ProcessedTableManager< - _$DriftChatDatabase, - $PinnedMessagesTable, - PinnedMessageEntity, - $$PinnedMessagesTableFilterComposer, - $$PinnedMessagesTableOrderingComposer, - $$PinnedMessagesTableAnnotationComposer, - $$PinnedMessagesTableCreateCompanionBuilder, - $$PinnedMessagesTableUpdateCompanionBuilder, - (PinnedMessageEntity, $$PinnedMessagesTableReferences), - PinnedMessageEntity, - PrefetchHooks Function({bool pinnedMessageReactionsRefs})>; -typedef $$PollsTableCreateCompanionBuilder = PollsCompanion Function({ - required String id, - required String name, - Value description, - required List options, - Value votingVisibility, - Value enforceUniqueVote, - Value maxVotesAllowed, - Value allowUserSuggestedOptions, - Value allowAnswers, - Value isClosed, - Value answersCount, - required Map voteCountsByOption, - Value voteCount, - Value createdById, - Value createdAt, - Value updatedAt, - Value?> extraData, - Value rowid, -}); -typedef $$PollsTableUpdateCompanionBuilder = PollsCompanion Function({ - Value id, - Value name, - Value description, - Value> options, - Value votingVisibility, - Value enforceUniqueVote, - Value maxVotesAllowed, - Value allowUserSuggestedOptions, - Value allowAnswers, - Value isClosed, - Value answersCount, - Value> voteCountsByOption, - Value voteCount, - Value createdById, - Value createdAt, - Value updatedAt, - Value?> extraData, - Value rowid, -}); - -final class $$PollsTableReferences - extends BaseReferences<_$DriftChatDatabase, $PollsTable, PollEntity> { - $$PollsTableReferences(super.$_db, super.$_table, super.$_typedResult); - - static MultiTypedResultKey<$PollVotesTable, List> - _pollVotesRefsTable(_$DriftChatDatabase db) => - MultiTypedResultKey.fromTable(db.pollVotes, - aliasName: - $_aliasNameGenerator(db.polls.id, db.pollVotes.pollId)); - - $$PollVotesTableProcessedTableManager get pollVotesRefs { - final manager = $$PollVotesTableTableManager($_db, $_db.pollVotes) - .filter((f) => f.pollId.id($_item.id)); - - final cache = $_typedResult.readTableOrNull(_pollVotesRefsTable($_db)); - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: cache)); - } -} - -class $$PollsTableFilterComposer - extends Composer<_$DriftChatDatabase, $PollsTable> { - $$PollsTableFilterComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => ColumnFilters(column)); - - ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => ColumnFilters(column)); - - ColumnFilters get description => $composableBuilder( - column: $table.description, builder: (column) => ColumnFilters(column)); - - ColumnWithTypeConverterFilters, List, String> - get options => $composableBuilder( - column: $table.options, - builder: (column) => ColumnWithTypeConverterFilters(column)); - - ColumnWithTypeConverterFilters - get votingVisibility => $composableBuilder( - column: $table.votingVisibility, - builder: (column) => ColumnWithTypeConverterFilters(column)); - - ColumnFilters get enforceUniqueVote => $composableBuilder( - column: $table.enforceUniqueVote, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get maxVotesAllowed => $composableBuilder( - column: $table.maxVotesAllowed, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get allowUserSuggestedOptions => $composableBuilder( - column: $table.allowUserSuggestedOptions, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get allowAnswers => $composableBuilder( - column: $table.allowAnswers, builder: (column) => ColumnFilters(column)); - - ColumnFilters get isClosed => $composableBuilder( - column: $table.isClosed, builder: (column) => ColumnFilters(column)); - - ColumnFilters get answersCount => $composableBuilder( - column: $table.answersCount, builder: (column) => ColumnFilters(column)); - - ColumnWithTypeConverterFilters, Map, String> - get voteCountsByOption => $composableBuilder( - column: $table.voteCountsByOption, - builder: (column) => ColumnWithTypeConverterFilters(column)); - - ColumnFilters get voteCount => $composableBuilder( - column: $table.voteCount, builder: (column) => ColumnFilters(column)); - - ColumnFilters get createdById => $composableBuilder( - column: $table.createdById, builder: (column) => ColumnFilters(column)); - - ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => ColumnFilters(column)); - - ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => ColumnFilters(column)); - - ColumnWithTypeConverterFilters?, Map, - String> - get extraData => $composableBuilder( - column: $table.extraData, - builder: (column) => ColumnWithTypeConverterFilters(column)); - - Expression pollVotesRefs( - Expression Function($$PollVotesTableFilterComposer f) f) { - final $$PollVotesTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.id, - referencedTable: $db.pollVotes, - getReferencedColumn: (t) => t.pollId, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$PollVotesTableFilterComposer( - $db: $db, - $table: $db.pollVotes, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return f(composer); - } -} - -class $$PollsTableOrderingComposer - extends Composer<_$DriftChatDatabase, $PollsTable> { - $$PollsTableOrderingComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get description => $composableBuilder( - column: $table.description, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get options => $composableBuilder( - column: $table.options, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get votingVisibility => $composableBuilder( - column: $table.votingVisibility, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get enforceUniqueVote => $composableBuilder( - column: $table.enforceUniqueVote, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get maxVotesAllowed => $composableBuilder( - column: $table.maxVotesAllowed, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get allowUserSuggestedOptions => $composableBuilder( - column: $table.allowUserSuggestedOptions, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get allowAnswers => $composableBuilder( - column: $table.allowAnswers, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get isClosed => $composableBuilder( - column: $table.isClosed, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get answersCount => $composableBuilder( - column: $table.answersCount, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get voteCountsByOption => $composableBuilder( - column: $table.voteCountsByOption, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get voteCount => $composableBuilder( - column: $table.voteCount, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get createdById => $composableBuilder( - column: $table.createdById, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get extraData => $composableBuilder( - column: $table.extraData, builder: (column) => ColumnOrderings(column)); -} - -class $$PollsTableAnnotationComposer - extends Composer<_$DriftChatDatabase, $PollsTable> { - $$PollsTableAnnotationComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - GeneratedColumn get id => - $composableBuilder(column: $table.id, builder: (column) => column); - - GeneratedColumn get name => - $composableBuilder(column: $table.name, builder: (column) => column); - - GeneratedColumn get description => $composableBuilder( - column: $table.description, builder: (column) => column); - - GeneratedColumnWithTypeConverter, String> get options => - $composableBuilder(column: $table.options, builder: (column) => column); - - GeneratedColumnWithTypeConverter - get votingVisibility => $composableBuilder( - column: $table.votingVisibility, builder: (column) => column); - - GeneratedColumn get enforceUniqueVote => $composableBuilder( - column: $table.enforceUniqueVote, builder: (column) => column); - - GeneratedColumn get maxVotesAllowed => $composableBuilder( - column: $table.maxVotesAllowed, builder: (column) => column); - - GeneratedColumn get allowUserSuggestedOptions => $composableBuilder( - column: $table.allowUserSuggestedOptions, builder: (column) => column); - - GeneratedColumn get allowAnswers => $composableBuilder( - column: $table.allowAnswers, builder: (column) => column); - - GeneratedColumn get isClosed => - $composableBuilder(column: $table.isClosed, builder: (column) => column); - - GeneratedColumn get answersCount => $composableBuilder( - column: $table.answersCount, builder: (column) => column); - - GeneratedColumnWithTypeConverter, String> - get voteCountsByOption => $composableBuilder( - column: $table.voteCountsByOption, builder: (column) => column); - - GeneratedColumn get voteCount => - $composableBuilder(column: $table.voteCount, builder: (column) => column); - - GeneratedColumn get createdById => $composableBuilder( - column: $table.createdById, builder: (column) => column); - - GeneratedColumn get createdAt => - $composableBuilder(column: $table.createdAt, builder: (column) => column); - - GeneratedColumn get updatedAt => - $composableBuilder(column: $table.updatedAt, builder: (column) => column); - - GeneratedColumnWithTypeConverter?, String> - get extraData => $composableBuilder( - column: $table.extraData, builder: (column) => column); - - Expression pollVotesRefs( - Expression Function($$PollVotesTableAnnotationComposer a) f) { - final $$PollVotesTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.id, - referencedTable: $db.pollVotes, - getReferencedColumn: (t) => t.pollId, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$PollVotesTableAnnotationComposer( - $db: $db, - $table: $db.pollVotes, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return f(composer); - } -} - -class $$PollsTableTableManager extends RootTableManager< - _$DriftChatDatabase, - $PollsTable, - PollEntity, - $$PollsTableFilterComposer, - $$PollsTableOrderingComposer, - $$PollsTableAnnotationComposer, - $$PollsTableCreateCompanionBuilder, - $$PollsTableUpdateCompanionBuilder, - (PollEntity, $$PollsTableReferences), - PollEntity, - PrefetchHooks Function({bool pollVotesRefs})> { - $$PollsTableTableManager(_$DriftChatDatabase db, $PollsTable table) - : super(TableManagerState( - db: db, - table: table, - createFilteringComposer: () => - $$PollsTableFilterComposer($db: db, $table: table), - createOrderingComposer: () => - $$PollsTableOrderingComposer($db: db, $table: table), - createComputedFieldComposer: () => - $$PollsTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - Value id = const Value.absent(), - Value name = const Value.absent(), - Value description = const Value.absent(), - Value> options = const Value.absent(), - Value votingVisibility = const Value.absent(), - Value enforceUniqueVote = const Value.absent(), - Value maxVotesAllowed = const Value.absent(), - Value allowUserSuggestedOptions = const Value.absent(), - Value allowAnswers = const Value.absent(), - Value isClosed = const Value.absent(), - Value answersCount = const Value.absent(), - Value> voteCountsByOption = const Value.absent(), - Value voteCount = const Value.absent(), - Value createdById = const Value.absent(), - Value createdAt = const Value.absent(), - Value updatedAt = const Value.absent(), - Value?> extraData = const Value.absent(), - Value rowid = const Value.absent(), - }) => - PollsCompanion( - id: id, - name: name, - description: description, - options: options, - votingVisibility: votingVisibility, - enforceUniqueVote: enforceUniqueVote, - maxVotesAllowed: maxVotesAllowed, - allowUserSuggestedOptions: allowUserSuggestedOptions, - allowAnswers: allowAnswers, - isClosed: isClosed, - answersCount: answersCount, - voteCountsByOption: voteCountsByOption, - voteCount: voteCount, - createdById: createdById, - createdAt: createdAt, - updatedAt: updatedAt, - extraData: extraData, - rowid: rowid, - ), - createCompanionCallback: ({ - required String id, - required String name, - Value description = const Value.absent(), - required List options, - Value votingVisibility = const Value.absent(), - Value enforceUniqueVote = const Value.absent(), - Value maxVotesAllowed = const Value.absent(), - Value allowUserSuggestedOptions = const Value.absent(), - Value allowAnswers = const Value.absent(), - Value isClosed = const Value.absent(), - Value answersCount = const Value.absent(), - required Map voteCountsByOption, - Value voteCount = const Value.absent(), - Value createdById = const Value.absent(), - Value createdAt = const Value.absent(), - Value updatedAt = const Value.absent(), - Value?> extraData = const Value.absent(), - Value rowid = const Value.absent(), - }) => - PollsCompanion.insert( - id: id, - name: name, - description: description, - options: options, - votingVisibility: votingVisibility, - enforceUniqueVote: enforceUniqueVote, - maxVotesAllowed: maxVotesAllowed, - allowUserSuggestedOptions: allowUserSuggestedOptions, - allowAnswers: allowAnswers, - isClosed: isClosed, - answersCount: answersCount, - voteCountsByOption: voteCountsByOption, - voteCount: voteCount, - createdById: createdById, - createdAt: createdAt, - updatedAt: updatedAt, - extraData: extraData, - rowid: rowid, - ), - withReferenceMapper: (p0) => p0 - .map((e) => - (e.readTable(table), $$PollsTableReferences(db, table, e))) - .toList(), - prefetchHooksCallback: ({pollVotesRefs = false}) { - return PrefetchHooks( - db: db, - explicitlyWatchedTables: [if (pollVotesRefs) db.pollVotes], - addJoins: null, - getPrefetchedDataCallback: (items) async { - return [ - if (pollVotesRefs) - await $_getPrefetchedData( - currentTable: table, - referencedTable: - $$PollsTableReferences._pollVotesRefsTable(db), - managerFromTypedResult: (p0) => - $$PollsTableReferences(db, table, p0).pollVotesRefs, - referencedItemsForCurrentItem: (item, - referencedItems) => - referencedItems.where((e) => e.pollId == item.id), - typedResults: items) - ]; - }, - ); - }, - )); -} - -typedef $$PollsTableProcessedTableManager = ProcessedTableManager< - _$DriftChatDatabase, - $PollsTable, - PollEntity, - $$PollsTableFilterComposer, - $$PollsTableOrderingComposer, - $$PollsTableAnnotationComposer, - $$PollsTableCreateCompanionBuilder, - $$PollsTableUpdateCompanionBuilder, - (PollEntity, $$PollsTableReferences), - PollEntity, - PrefetchHooks Function({bool pollVotesRefs})>; -typedef $$PollVotesTableCreateCompanionBuilder = PollVotesCompanion Function({ - Value id, - Value pollId, - Value optionId, - Value answerText, - Value createdAt, - Value updatedAt, - Value userId, - Value rowid, -}); -typedef $$PollVotesTableUpdateCompanionBuilder = PollVotesCompanion Function({ - Value id, - Value pollId, - Value optionId, - Value answerText, - Value createdAt, - Value updatedAt, - Value userId, - Value rowid, -}); - -final class $$PollVotesTableReferences extends BaseReferences< - _$DriftChatDatabase, $PollVotesTable, PollVoteEntity> { - $$PollVotesTableReferences(super.$_db, super.$_table, super.$_typedResult); - - static $PollsTable _pollIdTable(_$DriftChatDatabase db) => db.polls - .createAlias($_aliasNameGenerator(db.pollVotes.pollId, db.polls.id)); - - $$PollsTableProcessedTableManager? get pollId { - if ($_item.pollId == null) return null; - final manager = $$PollsTableTableManager($_db, $_db.polls) - .filter((f) => f.id($_item.pollId!)); - final item = $_typedResult.readTableOrNull(_pollIdTable($_db)); - if (item == null) return manager; - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); - } -} - -class $$PollVotesTableFilterComposer - extends Composer<_$DriftChatDatabase, $PollVotesTable> { - $$PollVotesTableFilterComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => ColumnFilters(column)); - - ColumnFilters get optionId => $composableBuilder( - column: $table.optionId, builder: (column) => ColumnFilters(column)); - - ColumnFilters get answerText => $composableBuilder( - column: $table.answerText, builder: (column) => ColumnFilters(column)); - - ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => ColumnFilters(column)); - - ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => ColumnFilters(column)); - - ColumnFilters get userId => $composableBuilder( - column: $table.userId, builder: (column) => ColumnFilters(column)); - - $$PollsTableFilterComposer get pollId { - final $$PollsTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.pollId, - referencedTable: $db.polls, - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$PollsTableFilterComposer( - $db: $db, - $table: $db.polls, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } -} - -class $$PollVotesTableOrderingComposer - extends Composer<_$DriftChatDatabase, $PollVotesTable> { - $$PollVotesTableOrderingComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get optionId => $composableBuilder( - column: $table.optionId, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get answerText => $composableBuilder( - column: $table.answerText, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get userId => $composableBuilder( - column: $table.userId, builder: (column) => ColumnOrderings(column)); - - $$PollsTableOrderingComposer get pollId { - final $$PollsTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.pollId, - referencedTable: $db.polls, - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$PollsTableOrderingComposer( - $db: $db, - $table: $db.polls, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } -} - -class $$PollVotesTableAnnotationComposer - extends Composer<_$DriftChatDatabase, $PollVotesTable> { - $$PollVotesTableAnnotationComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - GeneratedColumn get id => - $composableBuilder(column: $table.id, builder: (column) => column); - - GeneratedColumn get optionId => - $composableBuilder(column: $table.optionId, builder: (column) => column); - - GeneratedColumn get answerText => $composableBuilder( - column: $table.answerText, builder: (column) => column); - - GeneratedColumn get createdAt => - $composableBuilder(column: $table.createdAt, builder: (column) => column); - - GeneratedColumn get updatedAt => - $composableBuilder(column: $table.updatedAt, builder: (column) => column); - - GeneratedColumn get userId => - $composableBuilder(column: $table.userId, builder: (column) => column); - - $$PollsTableAnnotationComposer get pollId { - final $$PollsTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.pollId, - referencedTable: $db.polls, - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$PollsTableAnnotationComposer( - $db: $db, - $table: $db.polls, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } -} - -class $$PollVotesTableTableManager extends RootTableManager< - _$DriftChatDatabase, - $PollVotesTable, - PollVoteEntity, - $$PollVotesTableFilterComposer, - $$PollVotesTableOrderingComposer, - $$PollVotesTableAnnotationComposer, - $$PollVotesTableCreateCompanionBuilder, - $$PollVotesTableUpdateCompanionBuilder, - (PollVoteEntity, $$PollVotesTableReferences), - PollVoteEntity, - PrefetchHooks Function({bool pollId})> { - $$PollVotesTableTableManager(_$DriftChatDatabase db, $PollVotesTable table) - : super(TableManagerState( - db: db, - table: table, - createFilteringComposer: () => - $$PollVotesTableFilterComposer($db: db, $table: table), - createOrderingComposer: () => - $$PollVotesTableOrderingComposer($db: db, $table: table), - createComputedFieldComposer: () => - $$PollVotesTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - Value id = const Value.absent(), - Value pollId = const Value.absent(), - Value optionId = const Value.absent(), - Value answerText = const Value.absent(), - Value createdAt = const Value.absent(), - Value updatedAt = const Value.absent(), - Value userId = const Value.absent(), - Value rowid = const Value.absent(), - }) => - PollVotesCompanion( - id: id, - pollId: pollId, - optionId: optionId, - answerText: answerText, - createdAt: createdAt, - updatedAt: updatedAt, - userId: userId, - rowid: rowid, - ), - createCompanionCallback: ({ - Value id = const Value.absent(), - Value pollId = const Value.absent(), - Value optionId = const Value.absent(), - Value answerText = const Value.absent(), - Value createdAt = const Value.absent(), - Value updatedAt = const Value.absent(), - Value userId = const Value.absent(), - Value rowid = const Value.absent(), - }) => - PollVotesCompanion.insert( - id: id, - pollId: pollId, - optionId: optionId, - answerText: answerText, - createdAt: createdAt, - updatedAt: updatedAt, - userId: userId, - rowid: rowid, - ), - withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - $$PollVotesTableReferences(db, table, e) - )) - .toList(), - prefetchHooksCallback: ({pollId = false}) { - return PrefetchHooks( - db: db, - explicitlyWatchedTables: [], - addJoins: < - T extends TableManagerState< - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic>>(state) { - if (pollId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.pollId, - referencedTable: - $$PollVotesTableReferences._pollIdTable(db), - referencedColumn: - $$PollVotesTableReferences._pollIdTable(db).id, - ) as T; - } - - return state; - }, - getPrefetchedDataCallback: (items) async { - return []; - }, - ); - }, - )); -} - -typedef $$PollVotesTableProcessedTableManager = ProcessedTableManager< - _$DriftChatDatabase, - $PollVotesTable, - PollVoteEntity, - $$PollVotesTableFilterComposer, - $$PollVotesTableOrderingComposer, - $$PollVotesTableAnnotationComposer, - $$PollVotesTableCreateCompanionBuilder, - $$PollVotesTableUpdateCompanionBuilder, - (PollVoteEntity, $$PollVotesTableReferences), - PollVoteEntity, - PrefetchHooks Function({bool pollId})>; -typedef $$PinnedMessageReactionsTableCreateCompanionBuilder - = PinnedMessageReactionsCompanion Function({ - required String userId, - required String messageId, - required String type, - Value createdAt, - Value score, - Value?> extraData, - Value rowid, -}); -typedef $$PinnedMessageReactionsTableUpdateCompanionBuilder - = PinnedMessageReactionsCompanion Function({ - Value userId, - Value messageId, - Value type, - Value createdAt, - Value score, - Value?> extraData, - Value rowid, -}); - -final class $$PinnedMessageReactionsTableReferences extends BaseReferences< - _$DriftChatDatabase, - $PinnedMessageReactionsTable, - PinnedMessageReactionEntity> { - $$PinnedMessageReactionsTableReferences( - super.$_db, super.$_table, super.$_typedResult); - - static $PinnedMessagesTable _messageIdTable(_$DriftChatDatabase db) => - db.pinnedMessages.createAlias($_aliasNameGenerator( - db.pinnedMessageReactions.messageId, db.pinnedMessages.id)); - - $$PinnedMessagesTableProcessedTableManager get messageId { - final manager = $$PinnedMessagesTableTableManager($_db, $_db.pinnedMessages) - .filter((f) => f.id($_item.messageId!)); - final item = $_typedResult.readTableOrNull(_messageIdTable($_db)); - if (item == null) return manager; - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); - } -} - -class $$PinnedMessageReactionsTableFilterComposer - extends Composer<_$DriftChatDatabase, $PinnedMessageReactionsTable> { - $$PinnedMessageReactionsTableFilterComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnFilters get userId => $composableBuilder( - column: $table.userId, builder: (column) => ColumnFilters(column)); - - ColumnFilters get type => $composableBuilder( - column: $table.type, builder: (column) => ColumnFilters(column)); - - ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => ColumnFilters(column)); - - ColumnFilters get score => $composableBuilder( - column: $table.score, builder: (column) => ColumnFilters(column)); - - ColumnWithTypeConverterFilters?, Map, - String> - get extraData => $composableBuilder( - column: $table.extraData, - builder: (column) => ColumnWithTypeConverterFilters(column)); - - $$PinnedMessagesTableFilterComposer get messageId { - final $$PinnedMessagesTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.messageId, - referencedTable: $db.pinnedMessages, - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$PinnedMessagesTableFilterComposer( - $db: $db, - $table: $db.pinnedMessages, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } -} - -class $$PinnedMessageReactionsTableOrderingComposer - extends Composer<_$DriftChatDatabase, $PinnedMessageReactionsTable> { - $$PinnedMessageReactionsTableOrderingComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnOrderings get userId => $composableBuilder( - column: $table.userId, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get type => $composableBuilder( - column: $table.type, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get score => $composableBuilder( - column: $table.score, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get extraData => $composableBuilder( - column: $table.extraData, builder: (column) => ColumnOrderings(column)); - - $$PinnedMessagesTableOrderingComposer get messageId { - final $$PinnedMessagesTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.messageId, - referencedTable: $db.pinnedMessages, - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$PinnedMessagesTableOrderingComposer( - $db: $db, - $table: $db.pinnedMessages, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } -} - -class $$PinnedMessageReactionsTableAnnotationComposer - extends Composer<_$DriftChatDatabase, $PinnedMessageReactionsTable> { - $$PinnedMessageReactionsTableAnnotationComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - GeneratedColumn get userId => - $composableBuilder(column: $table.userId, builder: (column) => column); - - GeneratedColumn get type => - $composableBuilder(column: $table.type, builder: (column) => column); - - GeneratedColumn get createdAt => - $composableBuilder(column: $table.createdAt, builder: (column) => column); - - GeneratedColumn get score => - $composableBuilder(column: $table.score, builder: (column) => column); - - GeneratedColumnWithTypeConverter?, String> - get extraData => $composableBuilder( - column: $table.extraData, builder: (column) => column); - - $$PinnedMessagesTableAnnotationComposer get messageId { - final $$PinnedMessagesTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.messageId, - referencedTable: $db.pinnedMessages, - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$PinnedMessagesTableAnnotationComposer( - $db: $db, - $table: $db.pinnedMessages, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } -} - -class $$PinnedMessageReactionsTableTableManager extends RootTableManager< - _$DriftChatDatabase, - $PinnedMessageReactionsTable, - PinnedMessageReactionEntity, - $$PinnedMessageReactionsTableFilterComposer, - $$PinnedMessageReactionsTableOrderingComposer, - $$PinnedMessageReactionsTableAnnotationComposer, - $$PinnedMessageReactionsTableCreateCompanionBuilder, - $$PinnedMessageReactionsTableUpdateCompanionBuilder, - (PinnedMessageReactionEntity, $$PinnedMessageReactionsTableReferences), - PinnedMessageReactionEntity, - PrefetchHooks Function({bool messageId})> { - $$PinnedMessageReactionsTableTableManager( - _$DriftChatDatabase db, $PinnedMessageReactionsTable table) - : super(TableManagerState( - db: db, - table: table, - createFilteringComposer: () => - $$PinnedMessageReactionsTableFilterComposer( - $db: db, $table: table), - createOrderingComposer: () => - $$PinnedMessageReactionsTableOrderingComposer( - $db: db, $table: table), - createComputedFieldComposer: () => - $$PinnedMessageReactionsTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - Value userId = const Value.absent(), - Value messageId = const Value.absent(), - Value type = const Value.absent(), - Value createdAt = const Value.absent(), - Value score = const Value.absent(), - Value?> extraData = const Value.absent(), - Value rowid = const Value.absent(), - }) => - PinnedMessageReactionsCompanion( - userId: userId, - messageId: messageId, - type: type, - createdAt: createdAt, - score: score, - extraData: extraData, - rowid: rowid, - ), - createCompanionCallback: ({ - required String userId, - required String messageId, - required String type, - Value createdAt = const Value.absent(), - Value score = const Value.absent(), - Value?> extraData = const Value.absent(), - Value rowid = const Value.absent(), - }) => - PinnedMessageReactionsCompanion.insert( - userId: userId, - messageId: messageId, - type: type, - createdAt: createdAt, - score: score, - extraData: extraData, - rowid: rowid, - ), - withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - $$PinnedMessageReactionsTableReferences(db, table, e) - )) - .toList(), - prefetchHooksCallback: ({messageId = false}) { - return PrefetchHooks( - db: db, - explicitlyWatchedTables: [], - addJoins: < - T extends TableManagerState< - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic>>(state) { - if (messageId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.messageId, - referencedTable: $$PinnedMessageReactionsTableReferences - ._messageIdTable(db), - referencedColumn: $$PinnedMessageReactionsTableReferences - ._messageIdTable(db) - .id, - ) as T; - } - - return state; - }, - getPrefetchedDataCallback: (items) async { - return []; - }, - ); - }, - )); -} - -typedef $$PinnedMessageReactionsTableProcessedTableManager - = ProcessedTableManager< - _$DriftChatDatabase, - $PinnedMessageReactionsTable, - PinnedMessageReactionEntity, - $$PinnedMessageReactionsTableFilterComposer, - $$PinnedMessageReactionsTableOrderingComposer, - $$PinnedMessageReactionsTableAnnotationComposer, - $$PinnedMessageReactionsTableCreateCompanionBuilder, - $$PinnedMessageReactionsTableUpdateCompanionBuilder, - (PinnedMessageReactionEntity, $$PinnedMessageReactionsTableReferences), - PinnedMessageReactionEntity, - PrefetchHooks Function({bool messageId})>; -typedef $$ReactionsTableCreateCompanionBuilder = ReactionsCompanion Function({ - required String userId, - required String messageId, - required String type, - Value createdAt, - Value score, - Value?> extraData, - Value rowid, -}); -typedef $$ReactionsTableUpdateCompanionBuilder = ReactionsCompanion Function({ - Value userId, - Value messageId, - Value type, - Value createdAt, - Value score, - Value?> extraData, - Value rowid, -}); - -final class $$ReactionsTableReferences extends BaseReferences< - _$DriftChatDatabase, $ReactionsTable, ReactionEntity> { - $$ReactionsTableReferences(super.$_db, super.$_table, super.$_typedResult); - - static $MessagesTable _messageIdTable(_$DriftChatDatabase db) => - db.messages.createAlias( - $_aliasNameGenerator(db.reactions.messageId, db.messages.id)); - - $$MessagesTableProcessedTableManager get messageId { - final manager = $$MessagesTableTableManager($_db, $_db.messages) - .filter((f) => f.id($_item.messageId!)); - final item = $_typedResult.readTableOrNull(_messageIdTable($_db)); - if (item == null) return manager; - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); - } -} - -class $$ReactionsTableFilterComposer - extends Composer<_$DriftChatDatabase, $ReactionsTable> { - $$ReactionsTableFilterComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnFilters get userId => $composableBuilder( - column: $table.userId, builder: (column) => ColumnFilters(column)); - - ColumnFilters get type => $composableBuilder( - column: $table.type, builder: (column) => ColumnFilters(column)); - - ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => ColumnFilters(column)); - - ColumnFilters get score => $composableBuilder( - column: $table.score, builder: (column) => ColumnFilters(column)); - - ColumnWithTypeConverterFilters?, Map, - String> - get extraData => $composableBuilder( - column: $table.extraData, - builder: (column) => ColumnWithTypeConverterFilters(column)); - - $$MessagesTableFilterComposer get messageId { - final $$MessagesTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.messageId, - referencedTable: $db.messages, - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$MessagesTableFilterComposer( - $db: $db, - $table: $db.messages, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } -} - -class $$ReactionsTableOrderingComposer - extends Composer<_$DriftChatDatabase, $ReactionsTable> { - $$ReactionsTableOrderingComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnOrderings get userId => $composableBuilder( - column: $table.userId, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get type => $composableBuilder( - column: $table.type, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get score => $composableBuilder( - column: $table.score, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get extraData => $composableBuilder( - column: $table.extraData, builder: (column) => ColumnOrderings(column)); - - $$MessagesTableOrderingComposer get messageId { - final $$MessagesTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.messageId, - referencedTable: $db.messages, - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$MessagesTableOrderingComposer( - $db: $db, - $table: $db.messages, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } -} - -class $$ReactionsTableAnnotationComposer - extends Composer<_$DriftChatDatabase, $ReactionsTable> { - $$ReactionsTableAnnotationComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - GeneratedColumn get userId => - $composableBuilder(column: $table.userId, builder: (column) => column); - - GeneratedColumn get type => - $composableBuilder(column: $table.type, builder: (column) => column); - - GeneratedColumn get createdAt => - $composableBuilder(column: $table.createdAt, builder: (column) => column); - - GeneratedColumn get score => - $composableBuilder(column: $table.score, builder: (column) => column); - - GeneratedColumnWithTypeConverter?, String> - get extraData => $composableBuilder( - column: $table.extraData, builder: (column) => column); - - $$MessagesTableAnnotationComposer get messageId { - final $$MessagesTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.messageId, - referencedTable: $db.messages, - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$MessagesTableAnnotationComposer( - $db: $db, - $table: $db.messages, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } -} - -class $$ReactionsTableTableManager extends RootTableManager< - _$DriftChatDatabase, - $ReactionsTable, - ReactionEntity, - $$ReactionsTableFilterComposer, - $$ReactionsTableOrderingComposer, - $$ReactionsTableAnnotationComposer, - $$ReactionsTableCreateCompanionBuilder, - $$ReactionsTableUpdateCompanionBuilder, - (ReactionEntity, $$ReactionsTableReferences), - ReactionEntity, - PrefetchHooks Function({bool messageId})> { - $$ReactionsTableTableManager(_$DriftChatDatabase db, $ReactionsTable table) - : super(TableManagerState( - db: db, - table: table, - createFilteringComposer: () => - $$ReactionsTableFilterComposer($db: db, $table: table), - createOrderingComposer: () => - $$ReactionsTableOrderingComposer($db: db, $table: table), - createComputedFieldComposer: () => - $$ReactionsTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - Value userId = const Value.absent(), - Value messageId = const Value.absent(), - Value type = const Value.absent(), - Value createdAt = const Value.absent(), - Value score = const Value.absent(), - Value?> extraData = const Value.absent(), - Value rowid = const Value.absent(), - }) => - ReactionsCompanion( - userId: userId, - messageId: messageId, - type: type, - createdAt: createdAt, - score: score, - extraData: extraData, - rowid: rowid, - ), - createCompanionCallback: ({ - required String userId, - required String messageId, - required String type, - Value createdAt = const Value.absent(), - Value score = const Value.absent(), - Value?> extraData = const Value.absent(), - Value rowid = const Value.absent(), - }) => - ReactionsCompanion.insert( - userId: userId, - messageId: messageId, - type: type, - createdAt: createdAt, - score: score, - extraData: extraData, - rowid: rowid, - ), - withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - $$ReactionsTableReferences(db, table, e) - )) - .toList(), - prefetchHooksCallback: ({messageId = false}) { - return PrefetchHooks( - db: db, - explicitlyWatchedTables: [], - addJoins: < - T extends TableManagerState< - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic>>(state) { - if (messageId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.messageId, - referencedTable: - $$ReactionsTableReferences._messageIdTable(db), - referencedColumn: - $$ReactionsTableReferences._messageIdTable(db).id, - ) as T; - } - - return state; - }, - getPrefetchedDataCallback: (items) async { - return []; - }, - ); - }, - )); -} - -typedef $$ReactionsTableProcessedTableManager = ProcessedTableManager< - _$DriftChatDatabase, - $ReactionsTable, - ReactionEntity, - $$ReactionsTableFilterComposer, - $$ReactionsTableOrderingComposer, - $$ReactionsTableAnnotationComposer, - $$ReactionsTableCreateCompanionBuilder, - $$ReactionsTableUpdateCompanionBuilder, - (ReactionEntity, $$ReactionsTableReferences), - ReactionEntity, - PrefetchHooks Function({bool messageId})>; -typedef $$UsersTableCreateCompanionBuilder = UsersCompanion Function({ - required String id, - Value role, - Value language, - Value createdAt, - Value updatedAt, - Value lastActive, - Value online, - Value banned, - required Map extraData, - Value rowid, -}); -typedef $$UsersTableUpdateCompanionBuilder = UsersCompanion Function({ - Value id, - Value role, - Value language, - Value createdAt, - Value updatedAt, - Value lastActive, - Value online, - Value banned, - Value> extraData, - Value rowid, -}); - -class $$UsersTableFilterComposer - extends Composer<_$DriftChatDatabase, $UsersTable> { - $$UsersTableFilterComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => ColumnFilters(column)); - - ColumnFilters get role => $composableBuilder( - column: $table.role, builder: (column) => ColumnFilters(column)); - - ColumnFilters get language => $composableBuilder( - column: $table.language, builder: (column) => ColumnFilters(column)); - - ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => ColumnFilters(column)); - - ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => ColumnFilters(column)); - - ColumnFilters get lastActive => $composableBuilder( - column: $table.lastActive, builder: (column) => ColumnFilters(column)); - - ColumnFilters get online => $composableBuilder( - column: $table.online, builder: (column) => ColumnFilters(column)); - - ColumnFilters get banned => $composableBuilder( - column: $table.banned, builder: (column) => ColumnFilters(column)); - - ColumnWithTypeConverterFilters, Map, - String> - get extraData => $composableBuilder( - column: $table.extraData, - builder: (column) => ColumnWithTypeConverterFilters(column)); -} - -class $$UsersTableOrderingComposer - extends Composer<_$DriftChatDatabase, $UsersTable> { - $$UsersTableOrderingComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get role => $composableBuilder( - column: $table.role, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get language => $composableBuilder( - column: $table.language, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get lastActive => $composableBuilder( - column: $table.lastActive, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get online => $composableBuilder( - column: $table.online, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get banned => $composableBuilder( - column: $table.banned, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get extraData => $composableBuilder( - column: $table.extraData, builder: (column) => ColumnOrderings(column)); -} - -class $$UsersTableAnnotationComposer - extends Composer<_$DriftChatDatabase, $UsersTable> { - $$UsersTableAnnotationComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - GeneratedColumn get id => - $composableBuilder(column: $table.id, builder: (column) => column); - - GeneratedColumn get role => - $composableBuilder(column: $table.role, builder: (column) => column); - - GeneratedColumn get language => - $composableBuilder(column: $table.language, builder: (column) => column); - - GeneratedColumn get createdAt => - $composableBuilder(column: $table.createdAt, builder: (column) => column); - - GeneratedColumn get updatedAt => - $composableBuilder(column: $table.updatedAt, builder: (column) => column); - - GeneratedColumn get lastActive => $composableBuilder( - column: $table.lastActive, builder: (column) => column); - - GeneratedColumn get online => - $composableBuilder(column: $table.online, builder: (column) => column); - - GeneratedColumn get banned => - $composableBuilder(column: $table.banned, builder: (column) => column); - - GeneratedColumnWithTypeConverter, String> - get extraData => $composableBuilder( - column: $table.extraData, builder: (column) => column); -} - -class $$UsersTableTableManager extends RootTableManager< - _$DriftChatDatabase, - $UsersTable, - UserEntity, - $$UsersTableFilterComposer, - $$UsersTableOrderingComposer, - $$UsersTableAnnotationComposer, - $$UsersTableCreateCompanionBuilder, - $$UsersTableUpdateCompanionBuilder, - (UserEntity, BaseReferences<_$DriftChatDatabase, $UsersTable, UserEntity>), - UserEntity, - PrefetchHooks Function()> { - $$UsersTableTableManager(_$DriftChatDatabase db, $UsersTable table) - : super(TableManagerState( - db: db, - table: table, - createFilteringComposer: () => - $$UsersTableFilterComposer($db: db, $table: table), - createOrderingComposer: () => - $$UsersTableOrderingComposer($db: db, $table: table), - createComputedFieldComposer: () => - $$UsersTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - Value id = const Value.absent(), - Value role = const Value.absent(), - Value language = const Value.absent(), - Value createdAt = const Value.absent(), - Value updatedAt = const Value.absent(), - Value lastActive = const Value.absent(), - Value online = const Value.absent(), - Value banned = const Value.absent(), - Value> extraData = const Value.absent(), - Value rowid = const Value.absent(), - }) => - UsersCompanion( - id: id, - role: role, - language: language, - createdAt: createdAt, - updatedAt: updatedAt, - lastActive: lastActive, - online: online, - banned: banned, - extraData: extraData, - rowid: rowid, - ), - createCompanionCallback: ({ - required String id, - Value role = const Value.absent(), - Value language = const Value.absent(), - Value createdAt = const Value.absent(), - Value updatedAt = const Value.absent(), - Value lastActive = const Value.absent(), - Value online = const Value.absent(), - Value banned = const Value.absent(), - required Map extraData, - Value rowid = const Value.absent(), - }) => - UsersCompanion.insert( - id: id, - role: role, - language: language, - createdAt: createdAt, - updatedAt: updatedAt, - lastActive: lastActive, - online: online, - banned: banned, - extraData: extraData, - rowid: rowid, - ), - withReferenceMapper: (p0) => p0 - .map((e) => (e.readTable(table), BaseReferences(db, table, e))) - .toList(), - prefetchHooksCallback: null, - )); -} - -typedef $$UsersTableProcessedTableManager = ProcessedTableManager< - _$DriftChatDatabase, - $UsersTable, - UserEntity, - $$UsersTableFilterComposer, - $$UsersTableOrderingComposer, - $$UsersTableAnnotationComposer, - $$UsersTableCreateCompanionBuilder, - $$UsersTableUpdateCompanionBuilder, - (UserEntity, BaseReferences<_$DriftChatDatabase, $UsersTable, UserEntity>), - UserEntity, - PrefetchHooks Function()>; -typedef $$MembersTableCreateCompanionBuilder = MembersCompanion Function({ - required String userId, - required String channelCid, - Value channelRole, - Value inviteAcceptedAt, - Value inviteRejectedAt, - Value invited, - Value banned, - Value shadowBanned, - Value isModerator, - Value createdAt, - Value updatedAt, - Value rowid, -}); -typedef $$MembersTableUpdateCompanionBuilder = MembersCompanion Function({ - Value userId, - Value channelCid, - Value channelRole, - Value inviteAcceptedAt, - Value inviteRejectedAt, - Value invited, - Value banned, - Value shadowBanned, - Value isModerator, - Value createdAt, - Value updatedAt, - Value rowid, -}); - -final class $$MembersTableReferences - extends BaseReferences<_$DriftChatDatabase, $MembersTable, MemberEntity> { - $$MembersTableReferences(super.$_db, super.$_table, super.$_typedResult); - - static $ChannelsTable _channelCidTable(_$DriftChatDatabase db) => - db.channels.createAlias( - $_aliasNameGenerator(db.members.channelCid, db.channels.cid)); - - $$ChannelsTableProcessedTableManager get channelCid { - final manager = $$ChannelsTableTableManager($_db, $_db.channels) - .filter((f) => f.cid($_item.channelCid!)); - final item = $_typedResult.readTableOrNull(_channelCidTable($_db)); - if (item == null) return manager; - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); - } -} - -class $$MembersTableFilterComposer - extends Composer<_$DriftChatDatabase, $MembersTable> { - $$MembersTableFilterComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnFilters get userId => $composableBuilder( - column: $table.userId, builder: (column) => ColumnFilters(column)); - - ColumnFilters get channelRole => $composableBuilder( - column: $table.channelRole, builder: (column) => ColumnFilters(column)); - - ColumnFilters get inviteAcceptedAt => $composableBuilder( - column: $table.inviteAcceptedAt, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get inviteRejectedAt => $composableBuilder( - column: $table.inviteRejectedAt, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get invited => $composableBuilder( - column: $table.invited, builder: (column) => ColumnFilters(column)); - - ColumnFilters get banned => $composableBuilder( - column: $table.banned, builder: (column) => ColumnFilters(column)); - - ColumnFilters get shadowBanned => $composableBuilder( - column: $table.shadowBanned, builder: (column) => ColumnFilters(column)); - - ColumnFilters get isModerator => $composableBuilder( - column: $table.isModerator, builder: (column) => ColumnFilters(column)); - - ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => ColumnFilters(column)); - - ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => ColumnFilters(column)); - - $$ChannelsTableFilterComposer get channelCid { - final $$ChannelsTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.channelCid, - referencedTable: $db.channels, - getReferencedColumn: (t) => t.cid, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$ChannelsTableFilterComposer( - $db: $db, - $table: $db.channels, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } -} - -class $$MembersTableOrderingComposer - extends Composer<_$DriftChatDatabase, $MembersTable> { - $$MembersTableOrderingComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnOrderings get userId => $composableBuilder( - column: $table.userId, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get channelRole => $composableBuilder( - column: $table.channelRole, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get inviteAcceptedAt => $composableBuilder( - column: $table.inviteAcceptedAt, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get inviteRejectedAt => $composableBuilder( - column: $table.inviteRejectedAt, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get invited => $composableBuilder( - column: $table.invited, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get banned => $composableBuilder( - column: $table.banned, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get shadowBanned => $composableBuilder( - column: $table.shadowBanned, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get isModerator => $composableBuilder( - column: $table.isModerator, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => ColumnOrderings(column)); - - $$ChannelsTableOrderingComposer get channelCid { - final $$ChannelsTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.channelCid, - referencedTable: $db.channels, - getReferencedColumn: (t) => t.cid, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$ChannelsTableOrderingComposer( - $db: $db, - $table: $db.channels, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } -} - -class $$MembersTableAnnotationComposer - extends Composer<_$DriftChatDatabase, $MembersTable> { - $$MembersTableAnnotationComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - GeneratedColumn get userId => - $composableBuilder(column: $table.userId, builder: (column) => column); - - GeneratedColumn get channelRole => $composableBuilder( - column: $table.channelRole, builder: (column) => column); - - GeneratedColumn get inviteAcceptedAt => $composableBuilder( - column: $table.inviteAcceptedAt, builder: (column) => column); - - GeneratedColumn get inviteRejectedAt => $composableBuilder( - column: $table.inviteRejectedAt, builder: (column) => column); - - GeneratedColumn get invited => - $composableBuilder(column: $table.invited, builder: (column) => column); - - GeneratedColumn get banned => - $composableBuilder(column: $table.banned, builder: (column) => column); - - GeneratedColumn get shadowBanned => $composableBuilder( - column: $table.shadowBanned, builder: (column) => column); - - GeneratedColumn get isModerator => $composableBuilder( - column: $table.isModerator, builder: (column) => column); - - GeneratedColumn get createdAt => - $composableBuilder(column: $table.createdAt, builder: (column) => column); - - GeneratedColumn get updatedAt => - $composableBuilder(column: $table.updatedAt, builder: (column) => column); - - $$ChannelsTableAnnotationComposer get channelCid { - final $$ChannelsTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.channelCid, - referencedTable: $db.channels, - getReferencedColumn: (t) => t.cid, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$ChannelsTableAnnotationComposer( - $db: $db, - $table: $db.channels, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } -} - -class $$MembersTableTableManager extends RootTableManager< - _$DriftChatDatabase, - $MembersTable, - MemberEntity, - $$MembersTableFilterComposer, - $$MembersTableOrderingComposer, - $$MembersTableAnnotationComposer, - $$MembersTableCreateCompanionBuilder, - $$MembersTableUpdateCompanionBuilder, - (MemberEntity, $$MembersTableReferences), - MemberEntity, - PrefetchHooks Function({bool channelCid})> { - $$MembersTableTableManager(_$DriftChatDatabase db, $MembersTable table) - : super(TableManagerState( - db: db, - table: table, - createFilteringComposer: () => - $$MembersTableFilterComposer($db: db, $table: table), - createOrderingComposer: () => - $$MembersTableOrderingComposer($db: db, $table: table), - createComputedFieldComposer: () => - $$MembersTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - Value userId = const Value.absent(), - Value channelCid = const Value.absent(), - Value channelRole = const Value.absent(), - Value inviteAcceptedAt = const Value.absent(), - Value inviteRejectedAt = const Value.absent(), - Value invited = const Value.absent(), - Value banned = const Value.absent(), - Value shadowBanned = const Value.absent(), - Value isModerator = const Value.absent(), - Value createdAt = const Value.absent(), - Value updatedAt = const Value.absent(), - Value rowid = const Value.absent(), - }) => - MembersCompanion( - userId: userId, - channelCid: channelCid, - channelRole: channelRole, - inviteAcceptedAt: inviteAcceptedAt, - inviteRejectedAt: inviteRejectedAt, - invited: invited, - banned: banned, - shadowBanned: shadowBanned, - isModerator: isModerator, - createdAt: createdAt, - updatedAt: updatedAt, - rowid: rowid, - ), - createCompanionCallback: ({ - required String userId, - required String channelCid, - Value channelRole = const Value.absent(), - Value inviteAcceptedAt = const Value.absent(), - Value inviteRejectedAt = const Value.absent(), - Value invited = const Value.absent(), - Value banned = const Value.absent(), - Value shadowBanned = const Value.absent(), - Value isModerator = const Value.absent(), - Value createdAt = const Value.absent(), - Value updatedAt = const Value.absent(), - Value rowid = const Value.absent(), - }) => - MembersCompanion.insert( - userId: userId, - channelCid: channelCid, - channelRole: channelRole, - inviteAcceptedAt: inviteAcceptedAt, - inviteRejectedAt: inviteRejectedAt, - invited: invited, - banned: banned, - shadowBanned: shadowBanned, - isModerator: isModerator, - createdAt: createdAt, - updatedAt: updatedAt, - rowid: rowid, - ), - withReferenceMapper: (p0) => p0 - .map((e) => - (e.readTable(table), $$MembersTableReferences(db, table, e))) - .toList(), - prefetchHooksCallback: ({channelCid = false}) { - return PrefetchHooks( - db: db, - explicitlyWatchedTables: [], - addJoins: < - T extends TableManagerState< - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic>>(state) { - if (channelCid) { - state = state.withJoin( - currentTable: table, - currentColumn: table.channelCid, - referencedTable: - $$MembersTableReferences._channelCidTable(db), - referencedColumn: - $$MembersTableReferences._channelCidTable(db).cid, - ) as T; - } - - return state; - }, - getPrefetchedDataCallback: (items) async { - return []; - }, - ); - }, - )); -} - -typedef $$MembersTableProcessedTableManager = ProcessedTableManager< - _$DriftChatDatabase, - $MembersTable, - MemberEntity, - $$MembersTableFilterComposer, - $$MembersTableOrderingComposer, - $$MembersTableAnnotationComposer, - $$MembersTableCreateCompanionBuilder, - $$MembersTableUpdateCompanionBuilder, - (MemberEntity, $$MembersTableReferences), - MemberEntity, - PrefetchHooks Function({bool channelCid})>; -typedef $$ReadsTableCreateCompanionBuilder = ReadsCompanion Function({ - required DateTime lastRead, - required String userId, - required String channelCid, - Value unreadMessages, - Value lastReadMessageId, - Value rowid, -}); -typedef $$ReadsTableUpdateCompanionBuilder = ReadsCompanion Function({ - Value lastRead, - Value userId, - Value channelCid, - Value unreadMessages, - Value lastReadMessageId, - Value rowid, -}); - -final class $$ReadsTableReferences - extends BaseReferences<_$DriftChatDatabase, $ReadsTable, ReadEntity> { - $$ReadsTableReferences(super.$_db, super.$_table, super.$_typedResult); - - static $ChannelsTable _channelCidTable(_$DriftChatDatabase db) => db.channels - .createAlias($_aliasNameGenerator(db.reads.channelCid, db.channels.cid)); - - $$ChannelsTableProcessedTableManager get channelCid { - final manager = $$ChannelsTableTableManager($_db, $_db.channels) - .filter((f) => f.cid($_item.channelCid!)); - final item = $_typedResult.readTableOrNull(_channelCidTable($_db)); - if (item == null) return manager; - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); - } -} - -class $$ReadsTableFilterComposer - extends Composer<_$DriftChatDatabase, $ReadsTable> { - $$ReadsTableFilterComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnFilters get lastRead => $composableBuilder( - column: $table.lastRead, builder: (column) => ColumnFilters(column)); - - ColumnFilters get userId => $composableBuilder( - column: $table.userId, builder: (column) => ColumnFilters(column)); - - ColumnFilters get unreadMessages => $composableBuilder( - column: $table.unreadMessages, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get lastReadMessageId => $composableBuilder( - column: $table.lastReadMessageId, - builder: (column) => ColumnFilters(column)); - - $$ChannelsTableFilterComposer get channelCid { - final $$ChannelsTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.channelCid, - referencedTable: $db.channels, - getReferencedColumn: (t) => t.cid, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$ChannelsTableFilterComposer( - $db: $db, - $table: $db.channels, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } -} - -class $$ReadsTableOrderingComposer - extends Composer<_$DriftChatDatabase, $ReadsTable> { - $$ReadsTableOrderingComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnOrderings get lastRead => $composableBuilder( - column: $table.lastRead, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get userId => $composableBuilder( - column: $table.userId, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get unreadMessages => $composableBuilder( - column: $table.unreadMessages, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get lastReadMessageId => $composableBuilder( - column: $table.lastReadMessageId, - builder: (column) => ColumnOrderings(column)); - - $$ChannelsTableOrderingComposer get channelCid { - final $$ChannelsTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.channelCid, - referencedTable: $db.channels, - getReferencedColumn: (t) => t.cid, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$ChannelsTableOrderingComposer( - $db: $db, - $table: $db.channels, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } -} - -class $$ReadsTableAnnotationComposer - extends Composer<_$DriftChatDatabase, $ReadsTable> { - $$ReadsTableAnnotationComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - GeneratedColumn get lastRead => - $composableBuilder(column: $table.lastRead, builder: (column) => column); - - GeneratedColumn get userId => - $composableBuilder(column: $table.userId, builder: (column) => column); - - GeneratedColumn get unreadMessages => $composableBuilder( - column: $table.unreadMessages, builder: (column) => column); - - GeneratedColumn get lastReadMessageId => $composableBuilder( - column: $table.lastReadMessageId, builder: (column) => column); - - $$ChannelsTableAnnotationComposer get channelCid { - final $$ChannelsTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.channelCid, - referencedTable: $db.channels, - getReferencedColumn: (t) => t.cid, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - $$ChannelsTableAnnotationComposer( - $db: $db, - $table: $db.channels, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } -} - -class $$ReadsTableTableManager extends RootTableManager< - _$DriftChatDatabase, - $ReadsTable, - ReadEntity, - $$ReadsTableFilterComposer, - $$ReadsTableOrderingComposer, - $$ReadsTableAnnotationComposer, - $$ReadsTableCreateCompanionBuilder, - $$ReadsTableUpdateCompanionBuilder, - (ReadEntity, $$ReadsTableReferences), - ReadEntity, - PrefetchHooks Function({bool channelCid})> { - $$ReadsTableTableManager(_$DriftChatDatabase db, $ReadsTable table) - : super(TableManagerState( - db: db, - table: table, - createFilteringComposer: () => - $$ReadsTableFilterComposer($db: db, $table: table), - createOrderingComposer: () => - $$ReadsTableOrderingComposer($db: db, $table: table), - createComputedFieldComposer: () => - $$ReadsTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - Value lastRead = const Value.absent(), - Value userId = const Value.absent(), - Value channelCid = const Value.absent(), - Value unreadMessages = const Value.absent(), - Value lastReadMessageId = const Value.absent(), - Value rowid = const Value.absent(), - }) => - ReadsCompanion( - lastRead: lastRead, - userId: userId, - channelCid: channelCid, - unreadMessages: unreadMessages, - lastReadMessageId: lastReadMessageId, - rowid: rowid, - ), - createCompanionCallback: ({ - required DateTime lastRead, - required String userId, - required String channelCid, - Value unreadMessages = const Value.absent(), - Value lastReadMessageId = const Value.absent(), - Value rowid = const Value.absent(), - }) => - ReadsCompanion.insert( - lastRead: lastRead, - userId: userId, - channelCid: channelCid, - unreadMessages: unreadMessages, - lastReadMessageId: lastReadMessageId, - rowid: rowid, - ), - withReferenceMapper: (p0) => p0 - .map((e) => - (e.readTable(table), $$ReadsTableReferences(db, table, e))) - .toList(), - prefetchHooksCallback: ({channelCid = false}) { - return PrefetchHooks( - db: db, - explicitlyWatchedTables: [], - addJoins: < - T extends TableManagerState< - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic>>(state) { - if (channelCid) { - state = state.withJoin( - currentTable: table, - currentColumn: table.channelCid, - referencedTable: - $$ReadsTableReferences._channelCidTable(db), - referencedColumn: - $$ReadsTableReferences._channelCidTable(db).cid, - ) as T; - } - - return state; - }, - getPrefetchedDataCallback: (items) async { - return []; - }, - ); - }, - )); -} - -typedef $$ReadsTableProcessedTableManager = ProcessedTableManager< - _$DriftChatDatabase, - $ReadsTable, - ReadEntity, - $$ReadsTableFilterComposer, - $$ReadsTableOrderingComposer, - $$ReadsTableAnnotationComposer, - $$ReadsTableCreateCompanionBuilder, - $$ReadsTableUpdateCompanionBuilder, - (ReadEntity, $$ReadsTableReferences), - ReadEntity, - PrefetchHooks Function({bool channelCid})>; -typedef $$ChannelQueriesTableCreateCompanionBuilder = ChannelQueriesCompanion - Function({ - required String queryHash, - required String channelCid, - Value rowid, -}); -typedef $$ChannelQueriesTableUpdateCompanionBuilder = ChannelQueriesCompanion - Function({ - Value queryHash, - Value channelCid, - Value rowid, -}); - -class $$ChannelQueriesTableFilterComposer - extends Composer<_$DriftChatDatabase, $ChannelQueriesTable> { - $$ChannelQueriesTableFilterComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnFilters get queryHash => $composableBuilder( - column: $table.queryHash, builder: (column) => ColumnFilters(column)); - - ColumnFilters get channelCid => $composableBuilder( - column: $table.channelCid, builder: (column) => ColumnFilters(column)); -} - -class $$ChannelQueriesTableOrderingComposer - extends Composer<_$DriftChatDatabase, $ChannelQueriesTable> { - $$ChannelQueriesTableOrderingComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnOrderings get queryHash => $composableBuilder( - column: $table.queryHash, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get channelCid => $composableBuilder( - column: $table.channelCid, builder: (column) => ColumnOrderings(column)); -} - -class $$ChannelQueriesTableAnnotationComposer - extends Composer<_$DriftChatDatabase, $ChannelQueriesTable> { - $$ChannelQueriesTableAnnotationComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - GeneratedColumn get queryHash => - $composableBuilder(column: $table.queryHash, builder: (column) => column); - - GeneratedColumn get channelCid => $composableBuilder( - column: $table.channelCid, builder: (column) => column); -} - -class $$ChannelQueriesTableTableManager extends RootTableManager< - _$DriftChatDatabase, - $ChannelQueriesTable, - ChannelQueryEntity, - $$ChannelQueriesTableFilterComposer, - $$ChannelQueriesTableOrderingComposer, - $$ChannelQueriesTableAnnotationComposer, - $$ChannelQueriesTableCreateCompanionBuilder, - $$ChannelQueriesTableUpdateCompanionBuilder, - ( - ChannelQueryEntity, - BaseReferences<_$DriftChatDatabase, $ChannelQueriesTable, - ChannelQueryEntity> - ), - ChannelQueryEntity, - PrefetchHooks Function()> { - $$ChannelQueriesTableTableManager( - _$DriftChatDatabase db, $ChannelQueriesTable table) - : super(TableManagerState( - db: db, - table: table, - createFilteringComposer: () => - $$ChannelQueriesTableFilterComposer($db: db, $table: table), - createOrderingComposer: () => - $$ChannelQueriesTableOrderingComposer($db: db, $table: table), - createComputedFieldComposer: () => - $$ChannelQueriesTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - Value queryHash = const Value.absent(), - Value channelCid = const Value.absent(), - Value rowid = const Value.absent(), - }) => - ChannelQueriesCompanion( - queryHash: queryHash, - channelCid: channelCid, - rowid: rowid, - ), - createCompanionCallback: ({ - required String queryHash, - required String channelCid, - Value rowid = const Value.absent(), - }) => - ChannelQueriesCompanion.insert( - queryHash: queryHash, - channelCid: channelCid, - rowid: rowid, - ), - withReferenceMapper: (p0) => p0 - .map((e) => (e.readTable(table), BaseReferences(db, table, e))) - .toList(), - prefetchHooksCallback: null, - )); -} - -typedef $$ChannelQueriesTableProcessedTableManager = ProcessedTableManager< - _$DriftChatDatabase, - $ChannelQueriesTable, - ChannelQueryEntity, - $$ChannelQueriesTableFilterComposer, - $$ChannelQueriesTableOrderingComposer, - $$ChannelQueriesTableAnnotationComposer, - $$ChannelQueriesTableCreateCompanionBuilder, - $$ChannelQueriesTableUpdateCompanionBuilder, - ( - ChannelQueryEntity, - BaseReferences<_$DriftChatDatabase, $ChannelQueriesTable, - ChannelQueryEntity> - ), - ChannelQueryEntity, - PrefetchHooks Function()>; -typedef $$ConnectionEventsTableCreateCompanionBuilder - = ConnectionEventsCompanion Function({ - Value id, - required String type, - Value?> ownUser, - Value totalUnreadCount, - Value unreadChannels, - Value lastEventAt, - Value lastSyncAt, -}); -typedef $$ConnectionEventsTableUpdateCompanionBuilder - = ConnectionEventsCompanion Function({ - Value id, - Value type, - Value?> ownUser, - Value totalUnreadCount, - Value unreadChannels, - Value lastEventAt, - Value lastSyncAt, -}); - -class $$ConnectionEventsTableFilterComposer - extends Composer<_$DriftChatDatabase, $ConnectionEventsTable> { - $$ConnectionEventsTableFilterComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => ColumnFilters(column)); - - ColumnFilters get type => $composableBuilder( - column: $table.type, builder: (column) => ColumnFilters(column)); - - ColumnWithTypeConverterFilters?, Map, - String> - get ownUser => $composableBuilder( - column: $table.ownUser, - builder: (column) => ColumnWithTypeConverterFilters(column)); - - ColumnFilters get totalUnreadCount => $composableBuilder( - column: $table.totalUnreadCount, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get unreadChannels => $composableBuilder( - column: $table.unreadChannels, - builder: (column) => ColumnFilters(column)); - - ColumnFilters get lastEventAt => $composableBuilder( - column: $table.lastEventAt, builder: (column) => ColumnFilters(column)); - - ColumnFilters get lastSyncAt => $composableBuilder( - column: $table.lastSyncAt, builder: (column) => ColumnFilters(column)); -} - -class $$ConnectionEventsTableOrderingComposer - extends Composer<_$DriftChatDatabase, $ConnectionEventsTable> { - $$ConnectionEventsTableOrderingComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get type => $composableBuilder( - column: $table.type, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get ownUser => $composableBuilder( - column: $table.ownUser, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get totalUnreadCount => $composableBuilder( - column: $table.totalUnreadCount, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get unreadChannels => $composableBuilder( - column: $table.unreadChannels, - builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get lastEventAt => $composableBuilder( - column: $table.lastEventAt, builder: (column) => ColumnOrderings(column)); - - ColumnOrderings get lastSyncAt => $composableBuilder( - column: $table.lastSyncAt, builder: (column) => ColumnOrderings(column)); -} - -class $$ConnectionEventsTableAnnotationComposer - extends Composer<_$DriftChatDatabase, $ConnectionEventsTable> { - $$ConnectionEventsTableAnnotationComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - GeneratedColumn get id => - $composableBuilder(column: $table.id, builder: (column) => column); - - GeneratedColumn get type => - $composableBuilder(column: $table.type, builder: (column) => column); - - GeneratedColumnWithTypeConverter?, String> get ownUser => - $composableBuilder(column: $table.ownUser, builder: (column) => column); - - GeneratedColumn get totalUnreadCount => $composableBuilder( - column: $table.totalUnreadCount, builder: (column) => column); - - GeneratedColumn get unreadChannels => $composableBuilder( - column: $table.unreadChannels, builder: (column) => column); - - GeneratedColumn get lastEventAt => $composableBuilder( - column: $table.lastEventAt, builder: (column) => column); - - GeneratedColumn get lastSyncAt => $composableBuilder( - column: $table.lastSyncAt, builder: (column) => column); -} - -class $$ConnectionEventsTableTableManager extends RootTableManager< - _$DriftChatDatabase, - $ConnectionEventsTable, - ConnectionEventEntity, - $$ConnectionEventsTableFilterComposer, - $$ConnectionEventsTableOrderingComposer, - $$ConnectionEventsTableAnnotationComposer, - $$ConnectionEventsTableCreateCompanionBuilder, - $$ConnectionEventsTableUpdateCompanionBuilder, - ( - ConnectionEventEntity, - BaseReferences<_$DriftChatDatabase, $ConnectionEventsTable, - ConnectionEventEntity> - ), - ConnectionEventEntity, - PrefetchHooks Function()> { - $$ConnectionEventsTableTableManager( - _$DriftChatDatabase db, $ConnectionEventsTable table) - : super(TableManagerState( - db: db, - table: table, - createFilteringComposer: () => - $$ConnectionEventsTableFilterComposer($db: db, $table: table), - createOrderingComposer: () => - $$ConnectionEventsTableOrderingComposer($db: db, $table: table), - createComputedFieldComposer: () => - $$ConnectionEventsTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - Value id = const Value.absent(), - Value type = const Value.absent(), - Value?> ownUser = const Value.absent(), - Value totalUnreadCount = const Value.absent(), - Value unreadChannels = const Value.absent(), - Value lastEventAt = const Value.absent(), - Value lastSyncAt = const Value.absent(), - }) => - ConnectionEventsCompanion( - id: id, - type: type, - ownUser: ownUser, - totalUnreadCount: totalUnreadCount, - unreadChannels: unreadChannels, - lastEventAt: lastEventAt, - lastSyncAt: lastSyncAt, - ), - createCompanionCallback: ({ - Value id = const Value.absent(), - required String type, - Value?> ownUser = const Value.absent(), - Value totalUnreadCount = const Value.absent(), - Value unreadChannels = const Value.absent(), - Value lastEventAt = const Value.absent(), - Value lastSyncAt = const Value.absent(), - }) => - ConnectionEventsCompanion.insert( - id: id, - type: type, - ownUser: ownUser, - totalUnreadCount: totalUnreadCount, - unreadChannels: unreadChannels, - lastEventAt: lastEventAt, - lastSyncAt: lastSyncAt, - ), - withReferenceMapper: (p0) => p0 - .map((e) => (e.readTable(table), BaseReferences(db, table, e))) - .toList(), - prefetchHooksCallback: null, - )); -} - -typedef $$ConnectionEventsTableProcessedTableManager = ProcessedTableManager< - _$DriftChatDatabase, - $ConnectionEventsTable, - ConnectionEventEntity, - $$ConnectionEventsTableFilterComposer, - $$ConnectionEventsTableOrderingComposer, - $$ConnectionEventsTableAnnotationComposer, - $$ConnectionEventsTableCreateCompanionBuilder, - $$ConnectionEventsTableUpdateCompanionBuilder, - ( - ConnectionEventEntity, - BaseReferences<_$DriftChatDatabase, $ConnectionEventsTable, - ConnectionEventEntity> - ), - ConnectionEventEntity, - PrefetchHooks Function()>; - -class $DriftChatDatabaseManager { - final _$DriftChatDatabase _db; - $DriftChatDatabaseManager(this._db); - $$ChannelsTableTableManager get channels => - $$ChannelsTableTableManager(_db, _db.channels); - $$MessagesTableTableManager get messages => - $$MessagesTableTableManager(_db, _db.messages); - $$PinnedMessagesTableTableManager get pinnedMessages => - $$PinnedMessagesTableTableManager(_db, _db.pinnedMessages); - $$PollsTableTableManager get polls => - $$PollsTableTableManager(_db, _db.polls); - $$PollVotesTableTableManager get pollVotes => - $$PollVotesTableTableManager(_db, _db.pollVotes); - $$PinnedMessageReactionsTableTableManager get pinnedMessageReactions => - $$PinnedMessageReactionsTableTableManager( - _db, _db.pinnedMessageReactions); - $$ReactionsTableTableManager get reactions => - $$ReactionsTableTableManager(_db, _db.reactions); - $$UsersTableTableManager get users => - $$UsersTableTableManager(_db, _db.users); - $$MembersTableTableManager get members => - $$MembersTableTableManager(_db, _db.members); - $$ReadsTableTableManager get reads => - $$ReadsTableTableManager(_db, _db.reads); - $$ChannelQueriesTableTableManager get channelQueries => - $$ChannelQueriesTableTableManager(_db, _db.channelQueries); - $$ConnectionEventsTableTableManager get connectionEvents => - $$ConnectionEventsTableTableManager(_db, _db.connectionEvents); -} diff --git a/packages/stream_chat_persistence/lib/src/db/shared/native_db.dart b/packages/stream_chat_persistence/lib/src/db/shared/native_db.dart deleted file mode 100644 index ca31c3bb2d..0000000000 --- a/packages/stream_chat_persistence/lib/src/db/shared/native_db.dart +++ /dev/null @@ -1,108 +0,0 @@ -// coverage:ignore-file -import 'dart:io'; -import 'dart:isolate'; - -import 'package:drift/drift.dart'; -import 'package:drift/isolate.dart'; -import 'package:drift/native.dart'; -import 'package:path/path.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/stream_chat_persistence_client.dart'; -import 'package:stream_chat_persistence/stream_chat_persistence.dart'; - -/// A Helper class to construct new instances of [DriftChatDatabase] -/// specifically for native platform applications. -class SharedDB { - /// Returns a new instance of [DriftChatDatabase]. - static Future constructDatabase( - String userId, { - bool logStatements = false, - ConnectionMode connectionMode = ConnectionMode.regular, - bool webUseIndexedDbIfSupported = false, // Ignored on native - }) async { - final dbName = 'db_$userId'; - if (connectionMode == ConnectionMode.background) { - return DriftChatDatabase( - userId, - DatabaseConnection.delayed(Future(() async { - final isolate = await _createMoorIsolate( - dbName, - logStatements: logStatements, - ); - return isolate.connect(); - })), - ); - } - - return DriftChatDatabase( - userId, - LazyDatabase( - () async => _constructDatabase( - dbName, - logStatements: logStatements, - ), - ), - ); - } - - static Future _constructDatabase( - String dbName, { - bool logStatements = false, - }) async { - if (Platform.isIOS || Platform.isAndroid) { - final dir = await getApplicationDocumentsDirectory(); - final path = join(dir.path, '$dbName.sqlite'); - final file = File(path); - return NativeDatabase(file, logStatements: logStatements); - } - if (Platform.isMacOS || Platform.isLinux) { - final file = File('$dbName.sqlite'); - return NativeDatabase(file, logStatements: logStatements); - } - return NativeDatabase.memory(logStatements: logStatements); - } - - static void _startBackground(_IsolateStartRequest request) { - final executor = LazyDatabase(() async => NativeDatabase( - File(request.targetPath), - logStatements: request.logStatements, - )); - final moorIsolate = DriftIsolate.inCurrent( - () => DatabaseConnection(executor), - ); - request.sendMoorIsolate.send(moorIsolate); - } - - static Future _createMoorIsolate( - String dbName, { - bool logStatements = false, - }) async { - final dir = await getApplicationDocumentsDirectory(); - final path = join(dir.path, '$dbName.sqlite'); - - final receivePort = ReceivePort(); - await Isolate.spawn( - _startBackground, - _IsolateStartRequest( - receivePort.sendPort, - path, - logStatements: logStatements, - ), - ); - - return await receivePort.first as DriftIsolate; - } -} - -class _IsolateStartRequest { - const _IsolateStartRequest( - this.sendMoorIsolate, - this.targetPath, { - this.logStatements = false, - }); - - final SendPort sendMoorIsolate; - final String targetPath; - final bool logStatements; -} diff --git a/packages/stream_chat_persistence/lib/src/db/shared/shared_db.dart b/packages/stream_chat_persistence/lib/src/db/shared/shared_db.dart deleted file mode 100644 index 25120fcff1..0000000000 --- a/packages/stream_chat_persistence/lib/src/db/shared/shared_db.dart +++ /dev/null @@ -1,3 +0,0 @@ -export 'unsupported_db.dart' - if (dart.library.io) 'native_db.dart' // implementation using dart:io - if (dart.library.html) 'web_db.dart'; diff --git a/packages/stream_chat_persistence/lib/src/db/shared/unsupported_db.dart b/packages/stream_chat_persistence/lib/src/db/shared/unsupported_db.dart deleted file mode 100644 index b92105c510..0000000000 --- a/packages/stream_chat_persistence/lib/src/db/shared/unsupported_db.dart +++ /dev/null @@ -1,18 +0,0 @@ -// coverage:ignore-file -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/stream_chat_persistence.dart'; - -/// A Helper class to construct new instances of [DriftChatDatabase] -class SharedDB { - /// Returns a new instance of [DriftChatDatabase]. - static Future constructDatabase( - String userId, { - bool logStatements = false, - ConnectionMode connectionMode = ConnectionMode.regular, - bool webUseIndexedDbIfSupported = false, - }) { - throw UnsupportedError( - 'No implementation of the constructDatabase api provided', - ); - } -} diff --git a/packages/stream_chat_persistence/lib/src/db/shared/web_db.dart b/packages/stream_chat_persistence/lib/src/db/shared/web_db.dart deleted file mode 100644 index 3f75e1e767..0000000000 --- a/packages/stream_chat_persistence/lib/src/db/shared/web_db.dart +++ /dev/null @@ -1,40 +0,0 @@ -// coverage:ignore-file -// TODO: Replace with WASM implementation -// ignore: deprecated_member_use -import 'package:drift/web.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/stream_chat_persistence_client.dart'; - -/// A Helper class to construct new instances of [DriftChatDatabase] -/// specifically for Web applications. -class SharedDB { - /// Returns a new instance of [DriftChatDatabase]. - static Future constructDatabase( - String userId, { - bool logStatements = false, - ConnectionMode connectionMode = ConnectionMode.regular, // Ignored on web - bool webUseIndexedDbIfSupported = false, - }) async { - final dbName = 'db_$userId'; - final queryExecutor = await _getQueryExecutor( - dbName, - useIndexedDbIfSupported: webUseIndexedDbIfSupported, - logStatements: logStatements, - ); - return DriftChatDatabase(userId, queryExecutor); - } - - static Future _getQueryExecutor( - String dbName, { - required bool useIndexedDbIfSupported, - required bool logStatements, - }) async { - if (useIndexedDbIfSupported) { - return WebDatabase.withStorage( - await DriftWebStorage.indexedDbIfSupported(dbName), - logStatements: logStatements, - ); - } - return WebDatabase(dbName, logStatements: logStatements); - } -} diff --git a/packages/stream_chat_persistence/lib/src/entity/channel_queries.dart b/packages/stream_chat_persistence/lib/src/entity/channel_queries.dart deleted file mode 100644 index a9d3dc6cc0..0000000000 --- a/packages/stream_chat_persistence/lib/src/entity/channel_queries.dart +++ /dev/null @@ -1,18 +0,0 @@ -// coverage:ignore-file -import 'package:drift/drift.dart'; - -/// Represents a [ChannelQueries] table in [MoorChatDatabase]. -@DataClassName('ChannelQueryEntity') -class ChannelQueries extends Table { - /// The unique hash of this query - TextColumn get queryHash => text()(); - - /// The channel cid of this query - TextColumn get channelCid => text()(); - - @override - Set get primaryKey => { - queryHash, - channelCid, - }; -} diff --git a/packages/stream_chat_persistence/lib/src/entity/channels.dart b/packages/stream_chat_persistence/lib/src/entity/channels.dart deleted file mode 100644 index 3518835787..0000000000 --- a/packages/stream_chat_persistence/lib/src/entity/channels.dart +++ /dev/null @@ -1,50 +0,0 @@ -// coverage:ignore-file -import 'package:drift/drift.dart'; -import 'package:stream_chat_persistence/src/converter/converter.dart'; - -/// Represents a [Channels] table in [MoorChatDatabase]. -@DataClassName('ChannelEntity') -class Channels extends Table { - /// The id of this channel - TextColumn get id => text()(); - - /// The type of this channel - TextColumn get type => text()(); - - /// The cid of this channel - TextColumn get cid => text()(); - - /// List of user permissions on this channel - TextColumn get ownCapabilities => - text().nullable().map(ListConverter())(); - - /// The channel configuration data - TextColumn get config => text().map(MapConverter())(); - - /// True if this channel entity is frozen - BoolColumn get frozen => boolean().withDefault(const Constant(false))(); - - /// The date of the last message - DateTimeColumn get lastMessageAt => dateTime().nullable()(); - - /// The date of channel creation - DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); - - /// The date of the last channel update - DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); - - /// The date of channel deletion - DateTimeColumn get deletedAt => dateTime().nullable()(); - - /// The count of this channel members - IntColumn get memberCount => integer().withDefault(const Constant(0))(); - - /// The id of the user that created this channel - TextColumn get createdById => text().nullable()(); - - /// Map of custom channel extraData - TextColumn get extraData => text().nullable().map(MapConverter())(); - - @override - Set get primaryKey => {cid}; -} diff --git a/packages/stream_chat_persistence/lib/src/entity/connection_events.dart b/packages/stream_chat_persistence/lib/src/entity/connection_events.dart deleted file mode 100644 index 7853e08ee9..0000000000 --- a/packages/stream_chat_persistence/lib/src/entity/connection_events.dart +++ /dev/null @@ -1,31 +0,0 @@ -// coverage:ignore-file -import 'package:drift/drift.dart'; -import 'package:stream_chat_persistence/src/converter/map_converter.dart'; - -/// Represents a [ConnectionEvents] table in [MoorChatDatabase]. -@DataClassName('ConnectionEventEntity') -class ConnectionEvents extends Table { - /// event id - IntColumn get id => integer()(); - - /// event type - TextColumn get type => text()(); - - /// User object of the current user - TextColumn get ownUser => text().nullable().map(MapConverter())(); - - /// The number of unread messages for current user - IntColumn get totalUnreadCount => integer().nullable()(); - - /// User total unread channels for current user - IntColumn get unreadChannels => integer().nullable()(); - - /// DateTime of the last event - DateTimeColumn get lastEventAt => dateTime().nullable()(); - - /// DateTime of the last sync - DateTimeColumn get lastSyncAt => dateTime().nullable()(); - - @override - Set get primaryKey => {id}; -} diff --git a/packages/stream_chat_persistence/lib/src/entity/entity.dart b/packages/stream_chat_persistence/lib/src/entity/entity.dart deleted file mode 100644 index 41778641da..0000000000 --- a/packages/stream_chat_persistence/lib/src/entity/entity.dart +++ /dev/null @@ -1,12 +0,0 @@ -export 'channel_queries.dart'; -export 'channels.dart'; -export 'connection_events.dart'; -export 'members.dart'; -export 'messages.dart'; -export 'pinned_message_reactions.dart'; -export 'pinned_messages.dart'; -export 'poll_votes.dart'; -export 'polls.dart'; -export 'reactions.dart'; -export 'reads.dart'; -export 'users.dart'; diff --git a/packages/stream_chat_persistence/lib/src/entity/members.dart b/packages/stream_chat_persistence/lib/src/entity/members.dart deleted file mode 100644 index 9401d8c587..0000000000 --- a/packages/stream_chat_persistence/lib/src/entity/members.dart +++ /dev/null @@ -1,44 +0,0 @@ -// coverage:ignore-file -import 'package:drift/drift.dart'; -import 'package:stream_chat_persistence/src/entity/channels.dart'; - -/// Represents a [Members] table in [MoorChatDatabase]. -@DataClassName('MemberEntity') -class Members extends Table { - /// The interested user id - TextColumn get userId => text()(); - - /// The channel cid of which this user is part of - TextColumn get channelCid => - text().references(Channels, #cid, onDelete: KeyAction.cascade)(); - - /// The role of the user in the channel - TextColumn get channelRole => text().nullable()(); - - /// The date on which the user accepted the invite to the channel - DateTimeColumn get inviteAcceptedAt => dateTime().nullable()(); - - /// The date on which the user rejected the invite to the channel - DateTimeColumn get inviteRejectedAt => dateTime().nullable()(); - - /// True if the user has been invited to the channel - BoolColumn get invited => boolean().withDefault(const Constant(false))(); - - /// True if the member is banned from the channel - BoolColumn get banned => boolean().withDefault(const Constant(false))(); - - /// True if the member is shadow banned from the channel - BoolColumn get shadowBanned => boolean().withDefault(const Constant(false))(); - - /// True if the user is a moderator of the channel - BoolColumn get isModerator => boolean().withDefault(const Constant(false))(); - - /// The date of creation - DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); - - /// The last date of update - DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); - - @override - Set get primaryKey => {userId, channelCid}; -} diff --git a/packages/stream_chat_persistence/lib/src/entity/messages.dart b/packages/stream_chat_persistence/lib/src/entity/messages.dart deleted file mode 100644 index 69c38f0c83..0000000000 --- a/packages/stream_chat_persistence/lib/src/entity/messages.dart +++ /dev/null @@ -1,134 +0,0 @@ -// coverage:ignore-file -import 'package:drift/drift.dart'; -import 'package:stream_chat_persistence/src/converter/list_converter.dart'; -import 'package:stream_chat_persistence/src/converter/map_converter.dart'; -import 'package:stream_chat_persistence/src/entity/channels.dart'; - -/// Represents a [Messages] table in [MoorChatDatabase]. -@DataClassName('MessageEntity') -class Messages extends Table { - /// The message id - TextColumn get id => text()(); - - /// The text of this message - TextColumn get messageText => text().nullable()(); - - /// The list of attachments, either provided by the user - /// or generated from a command or as a result of URL scraping. - TextColumn get attachments => text().map(ListConverter())(); - - /// The current state of the message. - TextColumn get state => text()(); - - /// The message type - TextColumn get type => text().withDefault(const Constant('regular'))(); - - /// The list of user mentioned in the message - TextColumn get mentionedUsers => text().map(ListConverter())(); - - /// A map describing the count of number of every reaction - TextColumn get reactionCounts => text().nullable().map(MapConverter())(); - - /// A map describing the count of score of every reaction - TextColumn get reactionScores => text().nullable().map(MapConverter())(); - - /// The ID of the parent message, if the message is a thread reply. - TextColumn get parentId => text().nullable()(); - - /// The ID of the quoted message, if the message is a quoted reply. - TextColumn get quotedMessageId => text().nullable()(); - - /// The ID of the poll, if the message is a poll. - TextColumn get pollId => text().nullable()(); - - /// Number of replies for this message. - IntColumn get replyCount => integer().nullable()(); - - /// Check if this message needs to show in the channel. - BoolColumn get showInChannel => boolean().nullable()(); - - /// If true the message is shadowed - BoolColumn get shadowed => boolean().withDefault(const Constant(false))(); - - /// A used command name. - TextColumn get command => text().nullable()(); - - /// The DateTime on which the message was created. - /// - /// Returns the latest between [localCreatedAt] and [remoteCreatedAt]. - /// If both are null, returns [currentDateAndTime]. - Expression get createdAt { - return coalesce( - [localCreatedAt, remoteCreatedAt, currentDateAndTime], - ); - } - - /// The DateTime on which the message was created on the client. - DateTimeColumn get localCreatedAt => dateTime().nullable()(); - - /// The DateTime on which the message was created on the server. - DateTimeColumn get remoteCreatedAt => dateTime().nullable()(); - - /// The DateTime on which the message was updated last time. - /// - /// Returns the latest between [localUpdatedAt] and [remoteUpdatedAt]. - /// If both are null, returns [createdAt]. - Expression get updatedAt { - return coalesce( - [localUpdatedAt, remoteUpdatedAt, createdAt], - ); - } - - /// The DateTime on which the message was updated on the client. - DateTimeColumn get localUpdatedAt => dateTime().nullable()(); - - /// The DateTime on which the message was updated on the server. - DateTimeColumn get remoteUpdatedAt => dateTime().nullable()(); - - /// The DateTime on which the message was deleted. - /// - /// Returns the latest between [localDeletedAt] and [remoteDeletedAt]. - Expression get deletedAt { - return coalesce( - [localDeletedAt, remoteDeletedAt], - ); - } - - /// The DateTime on which the message was deleted on the client. - DateTimeColumn get localDeletedAt => dateTime().nullable()(); - - /// The DateTime on which the message was deleted on the server. - DateTimeColumn get remoteDeletedAt => dateTime().nullable()(); - - /// The DateTime at which the message text was edited - DateTimeColumn get messageTextUpdatedAt => dateTime().nullable()(); - - /// Id of the User who sent the message - TextColumn get userId => text().nullable()(); - - /// Whether the message is pinned or not - BoolColumn get pinned => boolean().withDefault(const Constant(false))(); - - /// The DateTime at which the message was pinned - DateTimeColumn get pinnedAt => dateTime().nullable()(); - - /// The DateTime on which the message pin expires - DateTimeColumn get pinExpires => dateTime().nullable()(); - - /// Id of the User who pinned the message - TextColumn get pinnedByUserId => text().nullable()(); - - /// The channel cid of which this message is part of - TextColumn get channelCid => - text().references(Channels, #cid, onDelete: KeyAction.cascade)(); - - /// A Map of [messageText] translations. - TextColumn get i18n => - text().nullable().map(NullableMapConverter())(); - - /// Message custom extraData - TextColumn get extraData => text().nullable().map(MapConverter())(); - - @override - Set get primaryKey => {id}; -} diff --git a/packages/stream_chat_persistence/lib/src/entity/pinned_message_reactions.dart b/packages/stream_chat_persistence/lib/src/entity/pinned_message_reactions.dart deleted file mode 100644 index e4c9b06e58..0000000000 --- a/packages/stream_chat_persistence/lib/src/entity/pinned_message_reactions.dart +++ /dev/null @@ -1,13 +0,0 @@ -// coverage:ignore-file -import 'package:drift/drift.dart'; -import 'package:stream_chat_persistence/src/entity/pinned_messages.dart'; -import 'package:stream_chat_persistence/src/entity/reactions.dart'; - -/// Represents a [PinnedMessageReactions] table in [MoorChatDatabase]. -@DataClassName('PinnedMessageReactionEntity') -class PinnedMessageReactions extends Reactions { - /// The messageId to which the reaction belongs - @override - TextColumn get messageId => - text().references(PinnedMessages, #id, onDelete: KeyAction.cascade)(); -} diff --git a/packages/stream_chat_persistence/lib/src/entity/pinned_messages.dart b/packages/stream_chat_persistence/lib/src/entity/pinned_messages.dart deleted file mode 100644 index 386c07259e..0000000000 --- a/packages/stream_chat_persistence/lib/src/entity/pinned_messages.dart +++ /dev/null @@ -1,7 +0,0 @@ -// coverage:ignore-file -import 'package:drift/drift.dart'; -import 'package:stream_chat_persistence/src/entity/messages.dart'; - -/// Represents a [PinnedMessages] table in [MoorChatDatabase]. -@DataClassName('PinnedMessageEntity') -class PinnedMessages extends Messages {} diff --git a/packages/stream_chat_persistence/lib/src/entity/poll_votes.dart b/packages/stream_chat_persistence/lib/src/entity/poll_votes.dart deleted file mode 100644 index 5087b5a0df..0000000000 --- a/packages/stream_chat_persistence/lib/src/entity/poll_votes.dart +++ /dev/null @@ -1,38 +0,0 @@ -// coverage:ignore-file -import 'package:drift/drift.dart'; -import 'package:stream_chat_persistence/src/entity/entity.dart'; - -/// Represents a [PollVotes] table in [MoorChatDatabase]. -@DataClassName('PollVoteEntity') -class PollVotes extends Table { - /// The unique identifier of the poll vote. - TextColumn get id => text().nullable()(); - - /// The unique identifier of the poll the vote belongs to. - TextColumn get pollId => - text().nullable().references(Polls, #id, onDelete: KeyAction.cascade)(); - - /// The unique identifier of the option selected in the poll. - /// - /// Nullable if the user provided an answer. - TextColumn get optionId => text().nullable()(); - - /// The text of the answer provided in the poll. - /// - /// Nullable if the user selected an option. - TextColumn get answerText => text().nullable()(); - - /// The date when the poll vote was created. - DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); - - /// The date when the poll vote was last updated. - DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); - - /// The unique identifier of the user who voted. - /// - /// Nullable if the poll is anonymous. - TextColumn get userId => text().nullable()(); - - @override - Set get primaryKey => {id, pollId}; -} diff --git a/packages/stream_chat_persistence/lib/src/entity/polls.dart b/packages/stream_chat_persistence/lib/src/entity/polls.dart deleted file mode 100644 index f377301dba..0000000000 --- a/packages/stream_chat_persistence/lib/src/entity/polls.dart +++ /dev/null @@ -1,75 +0,0 @@ -// coverage:ignore-file -import 'package:drift/drift.dart'; -import 'package:stream_chat_persistence/src/converter/list_converter.dart'; -import 'package:stream_chat_persistence/src/converter/map_converter.dart'; -import 'package:stream_chat_persistence/src/converter/voting_visibility_converter.dart'; - -/// Represents a [Polls] table in [MoorChatDatabase]. -@DataClassName('PollEntity') -class Polls extends Table { - /// The unique identifier of the poll. - TextColumn get id => text()(); - - /// The name of the poll. - TextColumn get name => text()(); - - /// The description of the poll. - TextColumn get description => text().nullable()(); - - /// The list of options available for the poll. - TextColumn get options => text().map(ListConverter())(); - - /// Represents the visibility of the voting process. - /// - /// Defaults to 'public'. - TextColumn get votingVisibility => text() - .map(const VotingVisibilityConverter()) - .withDefault(const Constant('public'))(); - - /// If true, only unique votes are allowed. - /// - /// Defaults to false. - BoolColumn get enforceUniqueVote => - boolean().withDefault(const Constant(false))(); - - /// The maximum number of votes allowed per user. - IntColumn get maxVotesAllowed => integer().nullable()(); - - /// If true, users can suggest their own options. - /// - /// Defaults to false. - BoolColumn get allowUserSuggestedOptions => - boolean().withDefault(const Constant(false))(); - - /// If true, users can provide their own answers/comments. - /// - /// Defaults to false. - BoolColumn get allowAnswers => boolean().withDefault(const Constant(false))(); - - /// Indicates if the poll is closed. - BoolColumn get isClosed => boolean().withDefault(const Constant(false))(); - - /// The total number of answers received by the poll. - IntColumn get answersCount => integer().withDefault(const Constant(0))(); - - /// Map of vote counts by option. - TextColumn get voteCountsByOption => text().map(MapConverter())(); - - /// The total number of votes received by the poll. - IntColumn get voteCount => integer().withDefault(const Constant(0))(); - - /// The id of the user who created the poll. - TextColumn get createdById => text().nullable()(); - - /// The date when the poll was created. - DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); - - /// The date when the poll was last updated. - DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); - - /// Map of custom poll extraData - TextColumn get extraData => text().nullable().map(MapConverter())(); - - @override - Set get primaryKey => {id}; -} diff --git a/packages/stream_chat_persistence/lib/src/entity/reactions.dart b/packages/stream_chat_persistence/lib/src/entity/reactions.dart deleted file mode 100644 index 39bf42589f..0000000000 --- a/packages/stream_chat_persistence/lib/src/entity/reactions.dart +++ /dev/null @@ -1,34 +0,0 @@ -// coverage:ignore-file -import 'package:drift/drift.dart'; -import 'package:stream_chat_persistence/src/converter/map_converter.dart'; -import 'package:stream_chat_persistence/src/entity/messages.dart'; - -/// Represents a [Reactions] table in [MoorChatDatabase]. -@DataClassName('ReactionEntity') -class Reactions extends Table { - /// The id of the user that sent the reaction - TextColumn get userId => text()(); - - /// The messageId to which the reaction belongs - TextColumn get messageId => - text().references(Messages, #id, onDelete: KeyAction.cascade)(); - - /// The type of the reaction - TextColumn get type => text()(); - - /// The DateTime on which the reaction is created - DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); - - /// The score of the reaction (ie. number of reactions sent) - IntColumn get score => integer().withDefault(const Constant(0))(); - - /// Reaction custom extraData - TextColumn get extraData => text().nullable().map(MapConverter())(); - - @override - Set get primaryKey => { - messageId, - type, - userId, - }; -} diff --git a/packages/stream_chat_persistence/lib/src/entity/reads.dart b/packages/stream_chat_persistence/lib/src/entity/reads.dart deleted file mode 100644 index b8a8ac22be..0000000000 --- a/packages/stream_chat_persistence/lib/src/entity/reads.dart +++ /dev/null @@ -1,29 +0,0 @@ -// coverage:ignore-file -import 'package:drift/drift.dart'; -import 'package:stream_chat_persistence/src/entity/channels.dart'; - -/// Represents a [Reads] table in [MoorChatDatabase]. -@DataClassName('ReadEntity') -class Reads extends Table { - /// Date of the read event - DateTimeColumn get lastRead => dateTime()(); - - /// Id of the User who sent the event - TextColumn get userId => text()(); - - /// The channel cid of which this read belongs - TextColumn get channelCid => - text().references(Channels, #cid, onDelete: KeyAction.cascade)(); - - /// Number of unread messages - IntColumn get unreadMessages => integer().withDefault(const Constant(0))(); - - /// Id of the last read message - TextColumn get lastReadMessageId => text().nullable()(); - - @override - Set get primaryKey => { - userId, - channelCid, - }; -} diff --git a/packages/stream_chat_persistence/lib/src/entity/users.dart b/packages/stream_chat_persistence/lib/src/entity/users.dart deleted file mode 100644 index 4245c02e63..0000000000 --- a/packages/stream_chat_persistence/lib/src/entity/users.dart +++ /dev/null @@ -1,37 +0,0 @@ -// coverage:ignore-file -import 'package:drift/drift.dart'; -import 'package:stream_chat_persistence/src/converter/map_converter.dart'; - -/// Represents a [Users] table in [MoorChatDatabase]. -@DataClassName('UserEntity') -class Users extends Table { - /// User id - TextColumn get id => text()(); - - /// User role - TextColumn get role => text().nullable()(); - - /// The language this user prefers. - TextColumn get language => text().nullable()(); - - /// Date of user creation - DateTimeColumn get createdAt => dateTime().nullable()(); - - /// Date of last user update - DateTimeColumn get updatedAt => dateTime().nullable()(); - - /// Date of last user connection - DateTimeColumn get lastActive => dateTime().nullable()(); - - /// True if user is online - BoolColumn get online => boolean().withDefault(const Constant(false))(); - - /// True if user is banned from the chat - BoolColumn get banned => boolean().withDefault(const Constant(false))(); - - /// Map of custom user extraData - TextColumn get extraData => text().map(MapConverter())(); - - @override - Set get primaryKey => {id}; -} diff --git a/packages/stream_chat_persistence/lib/src/mapper/channel_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/channel_mapper.dart deleted file mode 100644 index 18865d822e..0000000000 --- a/packages/stream_chat_persistence/lib/src/mapper/channel_mapper.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -/// Useful mapping functions for [ChannelEntity] -extension ChannelEntityX on ChannelEntity { - /// Maps a [ChannelEntity] into [ChannelModel] - ChannelModel toChannelModel({User? createdBy}) { - final config = ChannelConfig.fromJson(this.config); - return ChannelModel( - id: id, - ownCapabilities: ownCapabilities, - config: config, - type: type, - frozen: frozen, - createdAt: createdAt, - updatedAt: updatedAt, - memberCount: memberCount, - cid: cid, - lastMessageAt: lastMessageAt, - deletedAt: deletedAt, - extraData: extraData ?? {}, - createdBy: createdBy, - ); - } - - /// Maps a [ChannelEntity] into [ChannelState] - ChannelState toChannelState({ - User? createdBy, - List members = const [], - List reads = const [], - List messages = const [], - List pinnedMessages = const [], - }) => - ChannelState( - members: members, - read: reads, - messages: messages, - pinnedMessages: pinnedMessages, - channel: toChannelModel(createdBy: createdBy), - ); -} - -/// Useful mapping functions for [ChannelModel] -extension ChannelModelX on ChannelModel { - /// Maps a [ChannelModel] into [ChannelEntity] - ChannelEntity toEntity() => ChannelEntity( - id: id, - type: type, - cid: cid, - ownCapabilities: ownCapabilities, - config: config.toJson(), - frozen: frozen, - lastMessageAt: lastMessageAt, - createdAt: createdAt, - updatedAt: updatedAt, - deletedAt: deletedAt, - memberCount: memberCount, - createdById: createdBy?.id, - extraData: extraData, - ); -} diff --git a/packages/stream_chat_persistence/lib/src/mapper/event_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/event_mapper.dart deleted file mode 100644 index 0c3adb7b07..0000000000 --- a/packages/stream_chat_persistence/lib/src/mapper/event_mapper.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -/// Useful mapping functions for [ConnectionEventEntity] -extension ConnectionEventX on ConnectionEventEntity { - /// Maps a [ConnectionEventEntity] into [Event] - Event toEvent() => Event( - type: type, - createdAt: lastEventAt, - me: ownUser != null ? OwnUser.fromJson(ownUser!) : null, - totalUnreadCount: totalUnreadCount, - unreadChannels: unreadChannels, - ); -} diff --git a/packages/stream_chat_persistence/lib/src/mapper/mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/mapper.dart deleted file mode 100644 index 32de5859d3..0000000000 --- a/packages/stream_chat_persistence/lib/src/mapper/mapper.dart +++ /dev/null @@ -1,9 +0,0 @@ -export 'channel_mapper.dart'; -export 'event_mapper.dart'; -export 'member_mapper.dart'; -export 'message_mapper.dart'; -export 'pinned_message_mapper.dart'; -export 'pinned_message_reaction_mapper.dart'; -export 'reaction_mapper.dart'; -export 'read_mapper.dart'; -export 'user_mapper.dart'; diff --git a/packages/stream_chat_persistence/lib/src/mapper/member_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/member_mapper.dart deleted file mode 100644 index e1b3bac2ad..0000000000 --- a/packages/stream_chat_persistence/lib/src/mapper/member_mapper.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -/// Useful mapping functions for [MemberEntity] -extension MemberEntityX on MemberEntity { - /// Maps a [MemberEntity] into [Member] - Member toMember({User? user}) => Member( - user: user, - userId: userId, - banned: banned, - shadowBanned: shadowBanned, - updatedAt: updatedAt, - createdAt: createdAt, - channelRole: channelRole, - inviteAcceptedAt: inviteAcceptedAt, - invited: invited, - inviteRejectedAt: inviteRejectedAt, - isModerator: isModerator, - ); -} - -/// Useful mapping functions for [Member] -extension MemberX on Member { - /// Maps a [Member] into [MemberEntity] - MemberEntity toEntity({required String cid}) => MemberEntity( - userId: user!.id, - banned: banned, - shadowBanned: shadowBanned, - channelCid: cid, - createdAt: createdAt, - isModerator: isModerator, - inviteRejectedAt: inviteRejectedAt, - invited: invited, - inviteAcceptedAt: inviteAcceptedAt, - channelRole: channelRole, - updatedAt: updatedAt, - ); -} diff --git a/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart deleted file mode 100644 index 33e3071412..0000000000 --- a/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'dart:convert'; - -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -/// Useful mapping functions for [MessageEntity] -extension MessageEntityX on MessageEntity { - /// Maps a [MessageEntity] into [Message] - Message toMessage({ - User? user, - User? pinnedBy, - List? latestReactions, - List? ownReactions, - Message? quotedMessage, - Poll? poll, - }) => - Message( - shadowed: shadowed, - latestReactions: latestReactions, - ownReactions: ownReactions, - attachments: attachments.map((it) { - final json = jsonDecode(it); - return Attachment.fromData(json); - }).toList(), - extraData: extraData ?? {}, - createdAt: remoteCreatedAt, - localCreatedAt: localCreatedAt, - updatedAt: remoteUpdatedAt, - localUpdatedAt: localUpdatedAt, - deletedAt: remoteDeletedAt, - localDeletedAt: localDeletedAt, - messageTextUpdatedAt: messageTextUpdatedAt, - id: id, - type: type, - state: MessageState.fromJson(jsonDecode(state)), - command: command, - parentId: parentId, - quotedMessageId: quotedMessageId, - quotedMessage: quotedMessage, - pollId: pollId, - poll: poll, - reactionCounts: reactionCounts, - reactionScores: reactionScores, - replyCount: replyCount, - showInChannel: showInChannel, - text: messageText, - user: user, - pinned: pinned, - pinnedAt: pinnedAt, - pinExpires: pinExpires, - pinnedBy: pinnedBy, - mentionedUsers: - mentionedUsers.map((e) => User.fromJson(jsonDecode(e))).toList(), - i18n: i18n, - ); -} - -/// Useful mapping functions for [Message] -extension MessageX on Message { - /// Maps a [Message] into [MessageEntity] - MessageEntity toEntity({required String cid}) => MessageEntity( - id: id, - attachments: attachments.map((it) => jsonEncode(it.toData())).toList(), - channelCid: cid, - type: type, - parentId: parentId, - quotedMessageId: quotedMessageId, - pollId: pollId, - command: command, - remoteCreatedAt: remoteCreatedAt, - localCreatedAt: localCreatedAt, - shadowed: shadowed, - showInChannel: showInChannel, - replyCount: replyCount, - reactionScores: reactionScores, - reactionCounts: reactionCounts, - mentionedUsers: mentionedUsers.map(jsonEncode).toList(), - state: jsonEncode(state), - remoteUpdatedAt: remoteUpdatedAt, - localUpdatedAt: localUpdatedAt, - extraData: extraData, - userId: user?.id, - remoteDeletedAt: remoteDeletedAt, - localDeletedAt: localDeletedAt, - messageTextUpdatedAt: messageTextUpdatedAt, - messageText: text, - pinned: pinned, - pinnedAt: pinnedAt, - pinExpires: pinExpires, - pinnedByUserId: pinnedBy?.id, - i18n: i18n, - ); -} diff --git a/packages/stream_chat_persistence/lib/src/mapper/pinned_message_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/pinned_message_mapper.dart deleted file mode 100644 index 1f0b8d4fb3..0000000000 --- a/packages/stream_chat_persistence/lib/src/mapper/pinned_message_mapper.dart +++ /dev/null @@ -1,94 +0,0 @@ -import 'dart:convert'; - -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -/// Useful mapping functions for [PinnedMessageEntity] -extension PinnedMessageEntityX on PinnedMessageEntity { - /// Maps a [PinnedMessageEntity] into [Message] - Message toMessage({ - User? user, - User? pinnedBy, - List? latestReactions, - List? ownReactions, - Message? quotedMessage, - Poll? poll, - }) => - Message( - shadowed: shadowed, - latestReactions: latestReactions, - ownReactions: ownReactions, - attachments: attachments.map((it) { - final json = jsonDecode(it); - return Attachment.fromData(json); - }).toList(), - extraData: extraData ?? {}, - createdAt: remoteCreatedAt, - localCreatedAt: localCreatedAt, - updatedAt: remoteUpdatedAt, - localUpdatedAt: localUpdatedAt, - deletedAt: remoteDeletedAt, - localDeletedAt: localDeletedAt, - messageTextUpdatedAt: messageTextUpdatedAt, - id: id, - type: type, - state: MessageState.fromJson(jsonDecode(state)), - command: command, - parentId: parentId, - quotedMessageId: quotedMessageId, - quotedMessage: quotedMessage, - pollId: pollId, - poll: poll, - reactionCounts: reactionCounts, - reactionScores: reactionScores, - replyCount: replyCount, - showInChannel: showInChannel, - text: messageText, - user: user, - pinned: pinned, - pinnedAt: pinnedAt, - pinExpires: pinExpires, - pinnedBy: pinnedBy, - mentionedUsers: - mentionedUsers.map((e) => User.fromJson(jsonDecode(e))).toList(), - i18n: i18n, - ); -} - -/// Useful mapping functions for [Message] -extension PMessageX on Message { - /// Maps a [Message] into [PinnedMessageEntity] - PinnedMessageEntity toPinnedEntity({required String cid}) => - PinnedMessageEntity( - id: id, - attachments: attachments.map((it) => jsonEncode(it.toData())).toList(), - channelCid: cid, - type: type, - parentId: parentId, - quotedMessageId: quotedMessageId, - pollId: pollId, - command: command, - remoteCreatedAt: remoteCreatedAt, - localCreatedAt: localCreatedAt, - shadowed: shadowed, - showInChannel: showInChannel, - replyCount: replyCount, - reactionScores: reactionScores, - reactionCounts: reactionCounts, - mentionedUsers: mentionedUsers.map(jsonEncode).toList(), - state: jsonEncode(state), - remoteUpdatedAt: remoteUpdatedAt, - localUpdatedAt: localUpdatedAt, - extraData: extraData, - userId: user?.id, - remoteDeletedAt: remoteDeletedAt, - localDeletedAt: localDeletedAt, - messageTextUpdatedAt: messageTextUpdatedAt, - messageText: text, - pinned: pinned, - pinnedAt: pinnedAt, - pinExpires: pinExpires, - pinnedByUserId: pinnedBy?.id, - i18n: i18n, - ); -} diff --git a/packages/stream_chat_persistence/lib/src/mapper/pinned_message_reaction_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/pinned_message_reaction_mapper.dart deleted file mode 100644 index ecab7cb40e..0000000000 --- a/packages/stream_chat_persistence/lib/src/mapper/pinned_message_reaction_mapper.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -/// Useful mapping functions for [PinnedMessageReactionEntity] -extension PinnedMessageReactionEntityX on PinnedMessageReactionEntity { - /// Maps a [PinnedMessageReactionEntity] into [Reaction] - Reaction toReaction({User? user}) => Reaction( - extraData: extraData ?? {}, - type: type, - createdAt: createdAt, - userId: userId, - user: user, - messageId: messageId, - score: score, - ); -} - -/// Useful mapping functions for [Reaction] -extension PReactionX on Reaction { - /// Maps a [Reaction] into [ReactionEntity] - PinnedMessageReactionEntity toPinnedEntity() => PinnedMessageReactionEntity( - extraData: extraData, - type: type, - createdAt: createdAt, - userId: userId!, - messageId: messageId!, - score: score, - ); -} diff --git a/packages/stream_chat_persistence/lib/src/mapper/poll_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/poll_mapper.dart deleted file mode 100644 index 451b5e853c..0000000000 --- a/packages/stream_chat_persistence/lib/src/mapper/poll_mapper.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'dart:convert'; - -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -/// Useful mapping functions for [PollEntity] -extension PollEntityX on PollEntity { - /// Maps a [PollEntity] into [Poll] - Poll toPoll({ - User? createdBy, - List latestAnswers = const [], - List ownVotesAndAnswers = const [], - Map> latestVotesByOption = const {}, - }) { - return Poll( - id: id, - name: name, - description: description, - options: options.map((it) { - final json = jsonDecode(it); - return PollOption.fromJson(json); - }).toList(), - votingVisibility: votingVisibility, - enforceUniqueVote: enforceUniqueVote, - maxVotesAllowed: maxVotesAllowed, - allowAnswers: allowAnswers, - latestAnswers: latestAnswers, - answersCount: answersCount, - allowUserSuggestedOptions: allowUserSuggestedOptions, - isClosed: isClosed, - createdAt: createdAt, - updatedAt: updatedAt, - voteCountsByOption: voteCountsByOption, - voteCount: voteCount, - latestVotesByOption: latestVotesByOption, - createdById: createdById, - createdBy: createdBy, - ownVotesAndAnswers: ownVotesAndAnswers, - extraData: extraData ?? {}, - ); - } -} - -/// Useful mapping functions for [Poll] -extension PollX on Poll { - /// Maps a [Poll] into [PollEntity] - PollEntity toEntity() => PollEntity( - id: id, - name: name, - description: description, - options: options.map(jsonEncode).toList(), - votingVisibility: votingVisibility, - enforceUniqueVote: enforceUniqueVote, - maxVotesAllowed: maxVotesAllowed, - allowAnswers: allowAnswers, - answersCount: answersCount, - allowUserSuggestedOptions: allowUserSuggestedOptions, - isClosed: isClosed, - createdAt: createdAt, - updatedAt: updatedAt, - voteCountsByOption: voteCountsByOption, - voteCount: voteCount, - createdById: createdById, - extraData: extraData, - ); -} diff --git a/packages/stream_chat_persistence/lib/src/mapper/poll_vote_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/poll_vote_mapper.dart deleted file mode 100644 index 25fa23ab35..0000000000 --- a/packages/stream_chat_persistence/lib/src/mapper/poll_vote_mapper.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -/// Useful mapping functions for [PollVoteEntity] -extension PollVoteEntityX on PollVoteEntity { - /// Maps a [PollVoteEntity] into [PollVote] - PollVote toPollVote({ - User? user, - }) => - PollVote( - id: id, - pollId: pollId, - optionId: optionId, - answerText: answerText, - createdAt: createdAt, - updatedAt: updatedAt, - userId: userId, - user: user, - ); -} - -/// Useful mapping functions for [PollVote] -extension PollVoteX on PollVote { - /// Maps a [PollVote] into [PollVoteEntity] - PollVoteEntity toEntity() => PollVoteEntity( - id: id, - pollId: pollId, - optionId: optionId, - answerText: answerText, - createdAt: createdAt, - updatedAt: updatedAt, - userId: userId, - ); -} diff --git a/packages/stream_chat_persistence/lib/src/mapper/reaction_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/reaction_mapper.dart deleted file mode 100644 index a524fafe1c..0000000000 --- a/packages/stream_chat_persistence/lib/src/mapper/reaction_mapper.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -/// Useful mapping functions for [ReactionEntity] -extension ReactionEntityX on ReactionEntity { - /// Maps a [ReactionEntity] into [Reaction] - Reaction toReaction({User? user}) => Reaction( - extraData: extraData ?? {}, - type: type, - createdAt: createdAt, - userId: userId, - user: user, - messageId: messageId, - score: score, - ); -} - -/// Useful mapping functions for [Reaction] -extension ReactionX on Reaction { - /// Maps a [Reaction] into [ReactionEntity] - ReactionEntity toEntity() => ReactionEntity( - extraData: extraData, - type: type, - createdAt: createdAt, - userId: userId!, - messageId: messageId!, - score: score, - ); -} diff --git a/packages/stream_chat_persistence/lib/src/mapper/read_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/read_mapper.dart deleted file mode 100644 index 9e75a0e074..0000000000 --- a/packages/stream_chat_persistence/lib/src/mapper/read_mapper.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -/// Useful mapping functions for [ReadEntity] -extension ReadEntityX on ReadEntity { - /// Maps a [ReadEntity] into [Read] - Read toRead({required User user}) => Read( - user: user, - lastRead: lastRead, - unreadMessages: unreadMessages, - lastReadMessageId: lastReadMessageId, - ); -} - -/// Useful mapping functions for [Read] -extension ReadX on Read { - /// Maps a [Read] into [ReadEntity] - ReadEntity toEntity({required String cid}) => ReadEntity( - lastRead: lastRead, - userId: user.id, - channelCid: cid, - unreadMessages: unreadMessages, - lastReadMessageId: lastReadMessageId, - ); -} diff --git a/packages/stream_chat_persistence/lib/src/mapper/user_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/user_mapper.dart deleted file mode 100644 index f6409c3913..0000000000 --- a/packages/stream_chat_persistence/lib/src/mapper/user_mapper.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -/// Useful mapping functions for [UserEntity] -extension UserEntityX on UserEntity { - /// Maps a [UserEntity] into [User] - User toUser() => User( - id: id, - updatedAt: updatedAt, - language: language, - role: role, - online: online, - lastActive: lastActive, - extraData: extraData, - banned: banned, - createdAt: createdAt, - ); -} - -/// Useful mapping functions for [User] -extension UserX on User { - /// Maps a [User] into [UserEntity] - UserEntity toEntity() => UserEntity( - id: id, - role: role, - language: language, - createdAt: createdAt, - updatedAt: updatedAt, - lastActive: lastActive, - online: online, - banned: banned, - extraData: extraData, - ); -} diff --git a/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart b/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart deleted file mode 100644 index 089b1544c6..0000000000 --- a/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart +++ /dev/null @@ -1,480 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -/// Various connection modes on which [StreamChatPersistenceClient] can work -enum ConnectionMode { - /// Connects the [StreamChatPersistenceClient] on a regular/default isolate - regular, - - /// Connects the [StreamChatPersistenceClient] on a background isolate - background, -} - -/// Signature for a function which provides instance of [DriftChatDatabase] -typedef DatabaseProvider = DriftChatDatabase Function(String, ConnectionMode); - -final _levelEmojiMapper = { - Level.INFO: 'ā„¹ļø', - Level.WARNING: 'āš ļø', - Level.SEVERE: '🚨', -}; - -/// A [DriftChatDatabase] based implementation of the [ChatPersistenceClient] -class StreamChatPersistenceClient extends ChatPersistenceClient { - /// Creates a new instance of the stream chat persistence client - StreamChatPersistenceClient({ - /// Connection mode on which the client will work - ConnectionMode connectionMode = ConnectionMode.regular, - Level logLevel = Level.WARNING, - - /// Whether to use an experimental storage implementation on the web - /// that uses IndexedDB if the current browser supports it. - /// Otherwise, falls back to the local storage based implementation. - bool webUseExperimentalIndexedDb = false, - LogHandlerFunction? logHandlerFunction, - }) : _connectionMode = connectionMode, - _webUseIndexedDbIfSupported = webUseExperimentalIndexedDb, - _logger = Logger.detached('šŸ’½')..level = logLevel { - _logger.onRecord.listen(logHandlerFunction ?? _defaultLogHandler); - } - - /// [DriftChatDatabase] instance used by this client. - @visibleForTesting - DriftChatDatabase? db; - - final Logger _logger; - final ConnectionMode _connectionMode; - final bool _webUseIndexedDbIfSupported; - - void _defaultLogHandler(LogRecord record) { - print( - '(${record.time}) ' - '${_levelEmojiMapper[record.level] ?? record.level.name} ' - '${record.loggerName} ${record.message}', - ); - if (record.stackTrace != null) print(record.stackTrace); - } - - bool get _debugIsConnected { - assert(() { - if (!isConnected) { - throw StateError(''' - $runtimeType hasn't been connected yet or used after `disconnect` - was called. Consider calling `connect` to create a connection. - '''); - } - return true; - }(), ''); - return true; - } - - Future _defaultDatabaseProvider( - String userId, - ConnectionMode mode, - ) => - SharedDB.constructDatabase( - userId, - connectionMode: mode, - webUseIndexedDbIfSupported: _webUseIndexedDbIfSupported, - ); - - @override - bool get isConnected => db != null; - - @override - String? get userId => db?.userId; - - @override - Future connect( - String userId, { - DatabaseProvider? databaseProvider, // Used only for testing - }) async { - if (db != null) { - throw Exception( - 'An instance of StreamChatDatabase is already connected.\n' - 'disconnect the previous instance before connecting again.', - ); - } - _logger.info('connect'); - db = databaseProvider?.call(userId, _connectionMode) ?? - await _defaultDatabaseProvider(userId, _connectionMode); - } - - @override - Future getConnectionInfo() { - assert(_debugIsConnected, ''); - _logger.info('getConnectionInfo'); - return db!.connectionEventDao.connectionEvent; - } - - @override - Future updateConnectionInfo(Event event) { - assert(_debugIsConnected, ''); - _logger.info('updateConnectionInfo'); - return db!.connectionEventDao.updateConnectionEvent(event); - } - - @override - Future updateLastSyncAt(DateTime lastSyncAt) { - assert(_debugIsConnected, ''); - _logger.info('updateLastSyncAt'); - return db!.connectionEventDao.updateLastSyncAt(lastSyncAt); - } - - @override - Future getLastSyncAt() { - assert(_debugIsConnected, ''); - _logger.info('getLastSyncAt'); - return db!.connectionEventDao.lastSyncAt; - } - - @override - Future deleteChannels(List cids) { - assert(_debugIsConnected, ''); - _logger.info('deleteChannels'); - return db!.channelDao.deleteChannelByCids(cids); - } - - @override - Future> getChannelCids() { - assert(_debugIsConnected, ''); - _logger.info('getChannelCids'); - return db!.channelDao.cids; - } - - @override - Future deleteMessageByIds(List messageIds) { - assert(_debugIsConnected, ''); - _logger.info('deleteMessageByIds'); - return db!.messageDao.deleteMessageByIds(messageIds); - } - - @override - Future deletePinnedMessageByIds(List messageIds) { - assert(_debugIsConnected, ''); - _logger.info('deletePinnedMessageByIds'); - return db!.pinnedMessageDao.deleteMessageByIds(messageIds); - } - - @override - Future deleteMessageByCids(List cids) { - assert(_debugIsConnected, ''); - _logger.info('deleteMessageByCids'); - return db!.messageDao.deleteMessageByCids(cids); - } - - @override - Future deletePinnedMessageByCids(List cids) { - assert(_debugIsConnected, ''); - _logger.info('deletePinnedMessageByCids'); - return db!.pinnedMessageDao.deleteMessageByCids(cids); - } - - @override - Future> getMembersByCid(String cid) { - assert(_debugIsConnected, ''); - _logger.info('getMembersByCid'); - return db!.memberDao.getMembersByCid(cid); - } - - @override - Future getChannelByCid(String cid) { - assert(_debugIsConnected, ''); - _logger.info('getChannelByCid'); - return db!.channelDao.getChannelByCid(cid); - } - - @override - Future> getMessagesByCid( - String cid, { - PaginationParams? messagePagination, - }) { - assert(_debugIsConnected, ''); - _logger.info('getMessagesByCid'); - return db!.messageDao.getMessagesByCid( - cid, - messagePagination: messagePagination, - ); - } - - @override - Future> getPinnedMessagesByCid( - String cid, { - PaginationParams? messagePagination, - }) { - assert(_debugIsConnected, ''); - _logger.info('getPinnedMessagesByCid'); - return db!.pinnedMessageDao.getMessagesByCid( - cid, - messagePagination: messagePagination, - ); - } - - @override - Future> getReadsByCid(String cid) async { - assert(_debugIsConnected, ''); - _logger.info('getReadsByCid'); - return db!.readDao.getReadsByCid(cid); - } - - @override - Future>> getChannelThreads(String cid) async { - assert(_debugIsConnected, ''); - _logger.info('getChannelThreads'); - final messages = await db!.messageDao.getThreadMessages(cid); - final messageByParentIdDictionary = >{}; - for (final message in messages) { - final parentId = message.parentId!; - messageByParentIdDictionary[parentId] = [ - ...messageByParentIdDictionary[parentId] ?? [], - message, - ]; - } - - return messageByParentIdDictionary; - } - - @override - Future> getReplies( - String parentId, { - PaginationParams? options, - }) { - assert(_debugIsConnected, ''); - _logger.info('getReplies'); - return db!.messageDao.getThreadMessagesByParentId( - parentId, - options: options, - ); - } - - @override - Future> getChannelStates({ - Filter? filter, - List>? channelStateSort, - PaginationParams? paginationParams, - }) async { - assert(_debugIsConnected, ''); - assert(() { - if (channelStateSort?.any((it) => it.comparator == null) ?? false) { - throw ArgumentError( - 'SortOption requires a comparator in order to sort', - ); - } - return true; - }(), ''); - - _logger.info('getChannelStates'); - - final channels = await db!.channelQueryDao.getChannels(filter: filter); - - final channelStates = await Future.wait( - channels.map((e) => getChannelStateByCid(e.cid)), - ); - - // Sort the channel states - var comparator = _defaultChannelStateComparator; - if (channelStateSort != null && channelStateSort.isNotEmpty) { - comparator = _combineComparators( - channelStateSort.map((it) => it.comparator).withNullifyer, - ); - } - channelStates.sort(comparator); - - final offset = paginationParams?.offset; - if (offset != null && offset > 0 && channelStates.isNotEmpty) { - channelStates.removeRange(0, offset); - } - - if (paginationParams?.limit != null) { - return channelStates.take(paginationParams!.limit).toList(); - } - - return channelStates; - } - - @override - Future updateChannelQueries( - Filter? filter, - List cids, { - bool clearQueryCache = false, - }) { - assert(_debugIsConnected, ''); - _logger.info('updateChannelQueries'); - return db!.channelQueryDao.updateChannelQueries( - filter, - cids, - clearQueryCache: clearQueryCache, - ); - } - - @override - Future updateChannels(List channels) { - assert(_debugIsConnected, ''); - _logger.info('updateChannels'); - return db!.channelDao.updateChannels(channels); - } - - @override - Future updatePolls(List polls) { - assert(_debugIsConnected, ''); - _logger.info('updatePolls'); - return db!.pollDao.updatePolls(polls); - } - - @override - Future deletePollsByIds(List pollIds) { - assert(_debugIsConnected, ''); - _logger.info('deletePollsByIds'); - return db!.pollDao.deletePollsByIds(pollIds); - } - - @override - Future bulkUpdateMembers(Map?> members) { - assert(_debugIsConnected, ''); - _logger.info('bulkUpdateMembers'); - return db!.memberDao.bulkUpdateMembers(members); - } - - @override - Future bulkUpdateMessages(Map?> messages) { - assert(_debugIsConnected, ''); - _logger.info('bulkUpdateMessages'); - return db!.messageDao.bulkUpdateMessages(messages); - } - - @override - Future bulkUpdatePinnedMessages(Map?> messages) { - assert(_debugIsConnected, ''); - _logger.info('bulkUpdatePinnedMessages'); - return db!.pinnedMessageDao.bulkUpdateMessages(messages); - } - - @override - Future updatePollVotes(List pollVotes) { - assert(_debugIsConnected, ''); - _logger.info('updatePollVotes'); - return db!.pollVoteDao.updatePollVotes(pollVotes); - } - - @override - Future updatePinnedMessageReactions(List reactions) { - assert(_debugIsConnected, ''); - _logger.info('updatePinnedMessageReactions'); - return db!.pinnedMessageReactionDao.updateReactions(reactions); - } - - @override - Future updateReactions(List reactions) { - assert(_debugIsConnected, ''); - _logger.info('updateReactions'); - return db!.reactionDao.updateReactions(reactions); - } - - @override - Future bulkUpdateReads(Map?> reads) { - assert(_debugIsConnected, ''); - _logger.info('bulkUpdateReads'); - return db!.readDao.bulkUpdateReads(reads); - } - - @override - Future updateUsers(List users) { - assert(_debugIsConnected, ''); - _logger.info('updateUsers'); - return db!.userDao.updateUsers(users); - } - - @override - Future deletePinnedMessageReactionsByMessageId( - List messageIds, - ) { - assert(_debugIsConnected, ''); - _logger.info('deletePinnedMessageReactionsByMessageId'); - return db!.pinnedMessageReactionDao.deleteReactionsByMessageIds(messageIds); - } - - @override - Future deleteReactionsByMessageId(List messageIds) { - assert(_debugIsConnected, ''); - _logger.info('deleteReactionsByMessageId'); - return db!.reactionDao.deleteReactionsByMessageIds(messageIds); - } - - @override - Future deletePollVotesByPollIds(List pollIds) { - assert(_debugIsConnected, ''); - _logger.info('deletePollVotesByPollIds'); - return db!.pollVoteDao.deletePollVotesByPollIds(pollIds); - } - - @override - Future deleteMembersByCids(List cids) { - assert(_debugIsConnected, ''); - _logger.info('deleteMembersByCids'); - return db!.memberDao.deleteMemberByCids(cids); - } - - @override - Future updateChannelThreads( - String cid, - Map> threads, - ) { - assert(_debugIsConnected, ''); - _logger.info('updateChannelThreads'); - return db!.transaction(() => super.updateChannelThreads(cid, threads)); - } - - @override - Future updateChannelStates(List channelStates) { - assert(_debugIsConnected, ''); - _logger.info('updateChannelStates'); - return db!.transaction(() => super.updateChannelStates(channelStates)); - } - - @override - Future disconnect({bool flush = false}) async { - _logger.info('disconnect'); - if (isConnected) { - _logger.info('Disconnecting'); - if (flush) { - _logger.info('Flushing'); - await db!.flush(); - } - await db!.disconnect(); - db = null; - } - } -} - -// Creates a new combined [Comparator] which sorts items -// by the given [comparators]. -Comparator _combineComparators(Iterable> comparators) { - return (T a, T b) { - for (final comparator in comparators) { - try { - final result = comparator(a, b); - if (result != 0) return result; - } catch (e) { - // If the comparator throws an exception, we ignore it and - // continue with the next comparator. - continue; - } - } - return 0; - }; -} - -// The default [Comparator] used to sort [ChannelState]s. -int _defaultChannelStateComparator(ChannelState a, ChannelState b) { - final dateA = a.channel?.lastMessageAt ?? a.channel?.createdAt; - final dateB = b.channel?.lastMessageAt ?? b.channel?.createdAt; - - if (dateA == null && dateB == null) return 0; - if (dateA == null) return 1; - if (dateB == null) { - return -1; - } else { - return dateB.compareTo(dateA); - } -} diff --git a/packages/stream_chat_persistence/lib/stream_chat_persistence.dart b/packages/stream_chat_persistence/lib/stream_chat_persistence.dart deleted file mode 100644 index 5f9aff5be9..0000000000 --- a/packages/stream_chat_persistence/lib/stream_chat_persistence.dart +++ /dev/null @@ -1,3 +0,0 @@ -library stream_chat_persistence; - -export 'src/stream_chat_persistence_client.dart'; diff --git a/packages/stream_chat_persistence/pubspec.yaml b/packages/stream_chat_persistence/pubspec.yaml deleted file mode 100644 index 96d66986ed..0000000000 --- a/packages/stream_chat_persistence/pubspec.yaml +++ /dev/null @@ -1,40 +0,0 @@ -name: stream_chat_persistence -homepage: https://github.com/GetStream/stream-chat-flutter -description: Official Stream Chat Persistence library. Build your own chat experience using Dart and Flutter. -version: 9.4.0 -repository: https://github.com/GetStream/stream-chat-flutter -issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues - -# Note: The environment configuration and dependency versions are managed by Melos. -# -# Do not edit them manually. -# -# Steps to update dependencies: -# 1. Modify the version in the melos.yaml file. -# 2. Run `melos bootstrap` to apply changes. -# -# Steps to add a new dependency: -# 1. Add the dependency to this list. -# 2. Add it to the melos.yaml file for future updates. - -environment: - sdk: ^3.6.2 - flutter: ">=3.27.4" - -dependencies: - drift: ^2.22.1 - flutter: - sdk: flutter - logging: ^1.2.0 - meta: ^1.9.1 - path: ^1.8.3 - path_provider: ^2.1.3 - sqlite3_flutter_libs: ^0.5.26 - stream_chat: ^9.4.0 - -dev_dependencies: - build_runner: ^2.4.9 - drift_dev: ^2.22.1 - flutter_test: - sdk: flutter - mocktail: ^1.0.0 \ No newline at end of file diff --git a/packages/stream_chat_persistence/test/mock_chat_database.dart b/packages/stream_chat_persistence/test/mock_chat_database.dart deleted file mode 100644 index 1bd48f5f9d..0000000000 --- a/packages/stream_chat_persistence/test/mock_chat_database.dart +++ /dev/null @@ -1,88 +0,0 @@ -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat_persistence/src/dao/dao.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -class MockChatDatabase extends Mock implements DriftChatDatabase { - @override - UserDao get userDao => _userDao ??= MockUserDao(); - UserDao? _userDao; - - @override - ChannelDao get channelDao => _channelDao ??= MockChannelDao(); - ChannelDao? _channelDao; - - @override - MessageDao get messageDao => _messageDao ??= MockMessageDao(); - MessageDao? _messageDao; - - @override - PinnedMessageDao get pinnedMessageDao => - _pinnedMessageDao ??= MockPinnedMessageDao(); - PinnedMessageDao? _pinnedMessageDao; - - @override - MemberDao get memberDao => _memberDao ??= MockMemberDao(); - MemberDao? _memberDao; - - @override - ReactionDao get reactionDao => _reactionDao ??= MockReactionDao(); - ReactionDao? _reactionDao; - - @override - PinnedMessageReactionDao get pinnedMessageReactionDao => - _pinnedMessageReactionDao ??= MockPinnedMessageReactionDao(); - PinnedMessageReactionDao? _pinnedMessageReactionDao; - - @override - ReadDao get readDao => _readDao ??= MockReadDao(); - ReadDao? _readDao; - - @override - ChannelQueryDao get channelQueryDao => - _channelQueryDao ??= MockChannelQueryDao(); - ChannelQueryDao? _channelQueryDao; - - @override - ConnectionEventDao get connectionEventDao => - _connectionEventDao ??= MockConnectionEventDao(); - ConnectionEventDao? _connectionEventDao; - - @override - PollDao get pollDao => _pollDao ??= MockPollDao(); - PollDao? _pollDao; - - @override - PollVoteDao get pollVoteDao => _pollVoteDao ??= MockPollVoteDao(); - PollVoteDao? _pollVoteDao; - - @override - Future flush() => Future.value(); - - @override - Future disconnect() => Future.value(); -} - -class MockUserDao extends Mock implements UserDao {} - -class MockChannelDao extends Mock implements ChannelDao {} - -class MockMessageDao extends Mock implements MessageDao {} - -class MockPinnedMessageDao extends Mock implements PinnedMessageDao {} - -class MockMemberDao extends Mock implements MemberDao {} - -class MockReactionDao extends Mock implements ReactionDao {} - -class MockPinnedMessageReactionDao extends Mock - implements PinnedMessageReactionDao {} - -class MockReadDao extends Mock implements ReadDao {} - -class MockChannelQueryDao extends Mock implements ChannelQueryDao {} - -class MockConnectionEventDao extends Mock implements ConnectionEventDao {} - -class MockPollDao extends Mock implements PollDao {} - -class MockPollVoteDao extends Mock implements PollVoteDao {} diff --git a/packages/stream_chat_persistence/test/src/converter/list_coverter_test.dart b/packages/stream_chat_persistence/test/src/converter/list_coverter_test.dart deleted file mode 100644 index 05c7910239..0000000000 --- a/packages/stream_chat_persistence/test/src/converter/list_coverter_test.dart +++ /dev/null @@ -1,94 +0,0 @@ -import 'dart:convert'; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_persistence/src/converter/list_converter.dart'; - -void main() { - group('ListConverter', () { - final listConverter = ListConverter(); - - group('fromSql', () { - test('should throw type error if the provided json is not a list', () { - final json = {'test_key': 'testData'}; - expect( - () => listConverter.fromSql(jsonEncode(json)), - throwsA(isA()), - ); - }); - - test( - 'should throw type error if the provided json is not a list of String', - () { - final json = [22, 33, 44]; - expect( - () => listConverter.fromSql(jsonEncode(json)), - throwsA(isA()), - ); - }, - ); - - test('should return list of String if json data list is provided', () { - final data = ['data1', 'data2', 'data3']; - final res = listConverter.fromSql(jsonEncode(data)); - expect(res.length, data.length); - }); - }); - - group('toSql', () { - test('should return json string if data list is provided', () { - final data = ['data1', 'data2', 'data3']; - final res = listConverter.toSql(data); - expect(res, jsonEncode(data)); - }); - }); - }); - - group('NullableListConverter', () { - final listConverter = NullableListConverter(); - - group('fromSql', () { - test('should return null if nothing is provided', () { - final res = listConverter.fromSql(null); - expect(res, isNull); - }); - - test('should throw type error if the provided json is not a list', () { - final json = {'test_key': 'testData'}; - expect( - () => listConverter.fromSql(jsonEncode(json)), - throwsA(isA()), - ); - }); - - test( - 'should throw type error if the provided json is not a list of String', - () { - final json = [22, 33, 44]; - expect( - () => listConverter.fromSql(jsonEncode(json)), - throwsA(isA()), - ); - }, - ); - - test('should return list of String if json data list is provided', () { - final data = ['data1', 'data2', 'data3']; - final res = listConverter.fromSql(jsonEncode(data)); - expect(res!.length, data.length); - }); - }); - - group('toSql', () { - test('should return null if nothing is provided', () { - final res = listConverter.toSql(null); - expect(res, isNull); - }); - - test('should return json string if data list is provided', () { - final data = ['data1', 'data2', 'data3']; - final res = listConverter.toSql(data); - expect(res, jsonEncode(data)); - }); - }); - }); -} diff --git a/packages/stream_chat_persistence/test/src/converter/map_converter_test.dart b/packages/stream_chat_persistence/test/src/converter/map_converter_test.dart deleted file mode 100644 index 78faa9369b..0000000000 --- a/packages/stream_chat_persistence/test/src/converter/map_converter_test.dart +++ /dev/null @@ -1,112 +0,0 @@ -import 'dart:convert'; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_persistence/src/converter/map_converter.dart'; - -void main() { - group('MapConverter', () { - final mapConverter = MapConverter(); - - group('fromSql', () { - test('should throw type error if the provided json is not a map', () { - const json = ['testData1', 'testData2', 'testData3']; - expect( - () => mapConverter.fromSql(jsonEncode(json)), - throwsA(isA()), - ); - }); - - test( - 'should throw type error if the provided json is not a ' - 'map of String, String', - () { - const json = {'test_key': 22, 'test_key2': 33, 'test_key3': 44}; - expect( - () => mapConverter.fromSql(jsonEncode(json)), - throwsA(isA()), - ); - }, - ); - - test('should return map of String, String if json data is provided', () { - const data = { - 'test_key': 'testValue', - 'test_key2': 'testValue2', - 'test_key3': 'testValue3', - }; - final res = mapConverter.fromSql(jsonEncode(data)); - expect(res, data); - }); - }); - - group('toSql', () { - test('should return json string if data map is provided', () { - const data = { - 'test_key': 'testValue', - 'test_key2': 'testValue2', - 'test_key3': 'testValue3', - }; - final res = mapConverter.toSql(data); - expect(res, jsonEncode(data)); - }); - }); - }); - - group('NullableMapConverter', () { - final mapConverter = NullableMapConverter(); - - group('fromSql', () { - test('should return null if nothing is provided', () { - final res = mapConverter.fromSql(null); - expect(res, isNull); - }); - - test('should throw type error if the provided json is not a map', () { - const json = ['testData1', 'testData2', 'testData3']; - expect( - () => mapConverter.fromSql(jsonEncode(json)), - throwsA(isA()), - ); - }); - - test( - 'should throw type error if the provided json is not a ' - 'map of String, String', - () { - const json = {'test_key': 22, 'test_key2': 33, 'test_key3': 44}; - expect( - () => mapConverter.fromSql(jsonEncode(json)), - throwsA(isA()), - ); - }, - ); - - test('should return map of String, String if json data is provided', () { - const data = { - 'test_key': 'testValue', - 'test_key2': 'testValue2', - 'test_key3': 'testValue3', - }; - final res = mapConverter.fromSql(jsonEncode(data)); - expect(res, data); - }); - }); - - group('toSql', () { - test('should return null if nothing is provided', () { - final res = mapConverter.toSql(null); - expect(res, isNull); - }); - - test('should return json string if data map is provided', () { - const data = { - 'test_key': 'testValue', - 'test_key2': 'testValue2', - 'test_key3': 'testValue3', - }; - final res = mapConverter.toSql(data); - expect(res, jsonEncode(data)); - }); - }); - }); -} diff --git a/packages/stream_chat_persistence/test/src/converter/voting_visibility_converter_test.dart b/packages/stream_chat_persistence/test/src/converter/voting_visibility_converter_test.dart deleted file mode 100644 index f99a44e707..0000000000 --- a/packages/stream_chat_persistence/test/src/converter/voting_visibility_converter_test.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/converter/voting_visibility_converter.dart'; - -void main() { - group('VotingVisibilityConverter', () { - const converter = VotingVisibilityConverter(); - - test('toSql converts VotingVisibility to String', () { - expect(converter.toSql(VotingVisibility.anonymous), 'anonymous'); - expect(converter.toSql(VotingVisibility.public), 'public'); - }); - - test('fromSql converts String to VotingVisibility', () { - expect(converter.fromSql('anonymous'), VotingVisibility.anonymous); - expect(converter.fromSql('public'), VotingVisibility.public); - }); - - test('fromSql throws ArgumentError for invalid String', () { - expect( - () => converter.fromSql('invalid_value'), - throwsA(isA()), - ); - }); - }); -} diff --git a/packages/stream_chat_persistence/test/src/dao/channel_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/channel_dao_test.dart deleted file mode 100644 index da974fa31c..0000000000 --- a/packages/stream_chat_persistence/test/src/dao/channel_dao_test.dart +++ /dev/null @@ -1,201 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/dao/channel_dao.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -import '../../stream_chat_persistence_client_test.dart'; - -void main() { - late ChannelDao channelDao; - late DriftChatDatabase database; - - setUp(() { - database = testDatabaseProvider('testUserId'); - channelDao = database.channelDao; - }); - - test('getChannelByCid', () async { - const id = 'testId'; - const cid = 'testCid'; - const type = 'testType'; - - // Should be null initially - final channel = await channelDao.getChannelByCid(cid); - expect(channel, isNull); - - // Saving a dummy channel - final dummyChannel = ChannelModel( - id: id, - type: type, - cid: cid, - config: ChannelConfig(), - ); - await channelDao.updateChannels([dummyChannel]); - - // Should match the dummy channel - final updatedChannel = await channelDao.getChannelByCid(cid); - expect(updatedChannel, isNotNull); - expect(updatedChannel!.id, id); - expect(updatedChannel.cid, cid); - expect(updatedChannel.type, type); - }); - - test('deleteChannelByCids', () async { - const id = 'testId'; - const cid = 'testCid'; - const type = 'testType'; - - // Saving a dummy channel - final dummyChannel = ChannelModel( - id: id, - type: type, - cid: cid, - config: ChannelConfig(), - ); - await channelDao.updateChannels([dummyChannel]); - - // Should match the dummy channel - final updatedChannel = await channelDao.getChannelByCid(cid); - expect(updatedChannel, isNotNull); - expect(updatedChannel!.id, id); - expect(updatedChannel.cid, cid); - expect(updatedChannel.type, type); - - //Saving a dummy user - const userId = 'userId'; - final dummyUser = User(id: userId); - await database.userDao.updateUsers([dummyUser]); - - // Saving a dummy member - final dummyMember = Member(userId: userId, user: dummyUser); - await database.memberDao.updateMembers(cid, [dummyMember]); - - // Should match the dummy member - final updatedMembers = await database.memberDao.getMembersByCid(cid); - expect(updatedMembers.length, 1); - expect(updatedMembers.first.userId, userId); - - // Saving a dummy message - const messageId = 'messageId'; - final dummyMessage = Message(id: messageId, user: dummyUser); - await database.messageDao.updateMessages(cid, [dummyMessage]); - - // Should match the dummy message - final updatedMessages = await database.messageDao.getMessagesByCid(cid); - expect(updatedMessages.length, 1); - expect(updatedMessages.first.id, messageId); - - // Saving a dummy read - final dummyRead = Read( - lastRead: DateTime.now(), - user: dummyUser, - lastReadMessageId: messageId, - ); - await database.readDao.updateReads(cid, [dummyRead]); - - // Should match the dummy read - final updatedReads = await database.readDao.getReadsByCid(cid); - expect(updatedReads.length, 1); - expect(updatedReads.first.user, dummyUser); - - // Saving a dummy reaction - final dummyReaction = - Reaction(type: 'type', messageId: messageId, userId: userId); - await database.reactionDao.updateReactions([dummyReaction]); - - // Should match the dummy reaction - final updatedReactions = - await database.reactionDao.getReactionsByUserId(messageId, userId); - expect(updatedReactions.length, 1); - expect(updatedReactions.first.messageId, messageId); - - // Deleting the dummyChannel using cid - await channelDao.deleteChannelByCids([cid]); - - // Fetched channel Should be null - final channel = await channelDao.getChannelByCid(cid); - expect(channel, isNull); - - // Fetched members for passed cid should be empty - final members = await database.memberDao.getMembersByCid(cid); - expect(members, isEmpty); - - // Fetched messages for passed cid should be empty - final messages = await database.messageDao.getMessagesByCid(cid); - expect(messages, isEmpty); - - // Fetched reads for passed cid should be empty - final reads = await database.readDao.getReadsByCid(cid); - expect(reads, isEmpty); - - // Fetched readtions for passed message id and user id should be empty - final reactions = - await database.reactionDao.getReactionsByUserId(messageId, userId); - expect(reactions, isEmpty); - }); - - test('cids', () async { - // Should be empty initially - final cids = await channelDao.cids; - expect(cids, []); - - const id = 'testId'; - const cid = 'testCid'; - const type = 'testType'; - - // Saving a dummy channel - final dummyChannel = ChannelModel( - id: id, - type: type, - cid: cid, - config: ChannelConfig(), - ); - await channelDao.updateChannels([dummyChannel]); - - // Should return the cid of the dummy channel - final updatedCids = await channelDao.cids; - expect(updatedCids, [cid]); - }); - - test('updateChannels', () async { - const id = 'testId'; - const cid = 'testCid'; - const type = 'testType'; - - // Should be null initially - final channel = await channelDao.getChannelByCid(cid); - expect(channel, isNull); - - // Saving a dummy channel - final dummyChannel = ChannelModel( - id: id, - type: type, - cid: cid, - config: ChannelConfig(), - ); - await channelDao.updateChannels([dummyChannel]); - - // Should match the dummy channel - final updatedChannel = await channelDao.getChannelByCid(cid); - expect(updatedChannel, isNotNull); - expect(updatedChannel!.id, id); - expect(updatedChannel.cid, cid); - expect(updatedChannel.type, type); - - // Updating the previously saved channel - const newType = 'newTestType'; - final newChannel = dummyChannel.copyWith(type: newType); - await channelDao.updateChannels([newChannel]); - - // Should match the new channel - final newUpdatedChannel = await channelDao.getChannelByCid(cid); - expect(newUpdatedChannel, isNotNull); - expect(newUpdatedChannel!.id, id); - expect(newUpdatedChannel.cid, cid); - expect(newUpdatedChannel.type, newType); - }); - - tearDown(() async { - await database.disconnect(); - }); -} diff --git a/packages/stream_chat_persistence/test/src/dao/channel_query_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/channel_query_dao_test.dart deleted file mode 100644 index 91c7ad7555..0000000000 --- a/packages/stream_chat_persistence/test/src/dao/channel_query_dao_test.dart +++ /dev/null @@ -1,230 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/dao/channel_query_dao.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -import '../../stream_chat_persistence_client_test.dart'; -import '../utils/date_matcher.dart'; - -void main() { - late DriftChatDatabase database; - late ChannelQueryDao channelQueryDao; - - setUp(() { - database = testDatabaseProvider('testUserId'); - channelQueryDao = database.channelQueryDao; - }); - - test('updateChannelQueries', () async { - final filter = Filter.in_('members', const ['testUserId']); - - const cids = ['testCid1', 'testCid2', 'testCid3']; - - final cachedCids = await channelQueryDao.getCachedChannelCids(filter); - expect(cachedCids, isEmpty); - - // Updating channel queries - await channelQueryDao.updateChannelQueries(filter, cids); - - final updatedCids = await channelQueryDao.getCachedChannelCids(filter); - expect(updatedCids, cids); - }); - - test('clear queryCache before updateChannelQueries', () async { - final filter = Filter.in_('members', const ['testUserId']); - - const cids = ['testCid1', 'testCid2', 'testCid3']; - - final cachedCids = await channelQueryDao.getCachedChannelCids(filter); - expect(cachedCids, isEmpty); - - // Updating channel queries - await channelQueryDao.updateChannelQueries( - filter, - cids, - clearQueryCache: true, - ); - - final updatedCids = await channelQueryDao.getCachedChannelCids(filter); - expect(updatedCids, cids); - }); - - test('getCachedChannelCids', () async { - final filter = Filter.in_('members', const ['testUserId']); - - const cids = ['testCid1', 'testCid2', 'testCid3']; - - final cachedCids = await channelQueryDao.getCachedChannelCids(filter); - expect(cachedCids, isEmpty); - - // Updating channel queries - await channelQueryDao.updateChannelQueries(filter, cids); - - final updatedCids = await channelQueryDao.getCachedChannelCids(filter); - expect(updatedCids, cids); - }); - - Future> _insertTestDataForGetChannel( - Filter filter, { - int count = 3, - }) async { - final now = DateTime.now(); - final userDao = database.userDao; - final channelDao = database.channelDao; - - final cids = List.generate(count, (index) => 'testCid$index'); - final users = List.generate(count, (index) => User(id: 'testId$index')); - final channels = List.generate( - count, - (index) => ChannelModel( - id: 'testId$index', - type: 'testType$index', - cid: cids[index], - createdBy: users[index], - config: ChannelConfig(), - extraData: {'test_custom_field': index + 3}, - createdAt: now, - memberCount: index + 3, - lastMessageAt: now.add(Duration(hours: index)), - ), - ).reversed.toList(growable: false); - - await userDao.updateUsers(users); - await channelDao.updateChannels(channels); - await channelQueryDao.updateChannelQueries(filter, cids); - - return channels; - } - - group('getChannels', () { - tearDown(() async => database.flush()); - - final filter = Filter.in_('members', const ['testUserId']); - - test('should return empty list of channels', () async { - final channels = await channelQueryDao.getChannels(filter: filter); - expect(channels, isEmpty); - }); - - test('should return all the inserted channels', () async { - // Inserting test data for get channels - final insertedChannels = await _insertTestDataForGetChannel(filter); - - // Should match with the inserted channels - final updatedChannels = await channelQueryDao.getChannels(filter: filter); - expect(updatedChannels.length, insertedChannels.length); - for (var i = 0; i < updatedChannels.length; i++) { - final updatedChannel = updatedChannels[i]; - final insertedChannel = insertedChannels[i]; - - // Should match all the basic details - expect(updatedChannel.id, insertedChannel.id); - expect(updatedChannel.type, insertedChannel.type); - expect(updatedChannel.cid, insertedChannel.cid); - expect(updatedChannel.memberCount, insertedChannel.memberCount); - - // Should match createdAt date - expect( - updatedChannel.createdAt, - isSameDateAs(insertedChannel.createdAt), - ); - - // Should match lastMessageAt date - expect( - updatedChannel.lastMessageAt, - isSameDateAs(insertedChannel.lastMessageAt), - ); - } - }); - - test('should return sorted channels using member count', () async { - int sortComparator(ChannelModel a, ChannelModel b) => - b.memberCount.compareTo(a.memberCount); - - // Inserting test data for get channels - final insertedChannels = await _insertTestDataForGetChannel(filter); - insertedChannels.sort(sortComparator); - - // Should match with the inserted channels - final updatedChannels = await channelQueryDao.getChannels( - filter: filter, - sort: [ - SortOption( - 'member_count', - comparator: sortComparator, - ) - ], - ); - - expect(updatedChannels.length, insertedChannels.length); - for (var i = 0; i < updatedChannels.length; i++) { - final updatedChannel = updatedChannels[i]; - final insertedChannel = insertedChannels[i]; - - // Should match all the basic details - expect(updatedChannel.id, insertedChannel.id); - expect(updatedChannel.type, insertedChannel.type); - expect(updatedChannel.cid, insertedChannel.cid); - expect(updatedChannel.memberCount, insertedChannel.memberCount); - - // Should match createdAt date - expect( - updatedChannel.createdAt, - isSameDateAs(insertedChannel.createdAt), - ); - - // Should match lastMessageAt date - expect( - updatedChannel.lastMessageAt, - isSameDateAs(insertedChannel.lastMessageAt), - ); - } - }); - - test('should return sorted channels using custom field', () async { - int sortComparator(ChannelModel a, ChannelModel b) { - final aData = int.parse(a.extraData['test_custom_field'].toString()); - final bData = int.parse(b.extraData['test_custom_field'].toString()); - return bData.compareTo(aData); - } - - // Inserting test data for get channels - final insertedChannels = await _insertTestDataForGetChannel(filter); - insertedChannels.sort(sortComparator); - - // Should match with the inserted channels - final updatedChannels = await channelQueryDao.getChannels( - filter: filter, - sort: [SortOption('test_custom_field', comparator: sortComparator)], - ); - - expect(updatedChannels.length, insertedChannels.length); - for (var i = 0; i < updatedChannels.length; i++) { - final updatedChannel = updatedChannels[i]; - final insertedChannel = insertedChannels[i]; - - // Should match all the basic details - expect(updatedChannel.id, insertedChannel.id); - expect(updatedChannel.type, insertedChannel.type); - expect(updatedChannel.cid, insertedChannel.cid); - expect(updatedChannel.memberCount, insertedChannel.memberCount); - - // Should match createdAt date - expect( - updatedChannel.createdAt, - isSameDateAs(insertedChannel.createdAt), - ); - - // Should match lastMessageAt date - expect( - updatedChannel.lastMessageAt, - isSameDateAs(insertedChannel.lastMessageAt), - ); - } - }); - }); - - tearDown(() async { - await database.disconnect(); - }); -} diff --git a/packages/stream_chat_persistence/test/src/dao/connection_event_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/connection_event_dao_test.dart deleted file mode 100644 index ed517366c8..0000000000 --- a/packages/stream_chat_persistence/test/src/dao/connection_event_dao_test.dart +++ /dev/null @@ -1,118 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/dao/connection_event_dao.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -import '../../stream_chat_persistence_client_test.dart'; -import '../utils/date_matcher.dart'; - -void main() { - late ConnectionEventDao eventDao; - late DriftChatDatabase database; - - setUp(() { - database = testDatabaseProvider('testUserId'); - eventDao = database.connectionEventDao; - }); - - test('connectionEvent', () async { - // Should be null initially - final event = await eventDao.connectionEvent; - expect(event, isNull); - - // Adding a new event - final newEvent = Event( - createdAt: DateTime.now(), - totalUnreadCount: 33, - unreadChannels: 3, - me: OwnUser(id: 'testUserId'), - ); - await eventDao.updateConnectionEvent(newEvent); - - // Should match the added event - final updatedEvent = await eventDao.connectionEvent; - expect(updatedEvent, isNotNull); - expect(updatedEvent!.me!.id, newEvent.me!.id); - expect(updatedEvent.totalUnreadCount, newEvent.totalUnreadCount); - expect(updatedEvent.unreadChannels, newEvent.unreadChannels); - }); - - test('lastSyncAt', () async { - // Should be null initially - final lastSyncAt = await eventDao.lastSyncAt; - expect(lastSyncAt, isNull); - - // Adding an event for testing - final event = Event( - createdAt: DateTime.now(), - totalUnreadCount: 33, - unreadChannels: 3, - me: OwnUser(id: 'testUserId'), - ); - await eventDao.updateConnectionEvent(event); - - // Updating it's last sync - final now = DateTime.now(); - await eventDao.updateLastSyncAt(now); - - // Should match the updated last sync - final updatedLastSyncAt = await eventDao.lastSyncAt; - expect(updatedLastSyncAt, isSameDateAs(now)); - }); - - test('updateConnectionEvent', () async { - // Adding and event for testing - final event = Event( - createdAt: DateTime.now(), - totalUnreadCount: 33, - unreadChannels: 3, - me: OwnUser(id: 'testUserId'), - ); - await eventDao.updateConnectionEvent(event); - - // Should match the previously added event - final fetchedEvent = await eventDao.connectionEvent; - expect(fetchedEvent, isNotNull); - expect(fetchedEvent!.me!.id, event.me!.id); - expect(fetchedEvent.totalUnreadCount, event.totalUnreadCount); - expect(fetchedEvent.unreadChannels, event.unreadChannels); - - // Updating the added event - final newEvent = event.copyWith(unreadChannels: 4); - await eventDao.updateConnectionEvent(newEvent); - - // Should match the updated event - final fetchedNewEvent = await eventDao.connectionEvent; - expect(fetchedNewEvent, isNotNull); - expect(fetchedNewEvent!.me!.id, event.me!.id); - expect(fetchedNewEvent.totalUnreadCount, event.totalUnreadCount); - expect(fetchedNewEvent.unreadChannels, newEvent.unreadChannels); - }); - - test('updateLastSyncAt', () async { - // Should be null initially - final lastSyncAt = await eventDao.lastSyncAt; - expect(lastSyncAt, isNull); - - // Adding an event just for testing - final event = Event( - createdAt: DateTime.now(), - totalUnreadCount: 33, - unreadChannels: 3, - me: OwnUser(id: 'testUserId'), - ); - await eventDao.updateConnectionEvent(event); - - // Updating it's last sync - final now = DateTime.now(); - await eventDao.updateLastSyncAt(now); - - // Should match the last sync - final updatedLastSyncAt = await eventDao.lastSyncAt; - expect(updatedLastSyncAt, isSameDateAs(now)); - }); - - tearDown(() async { - await database.disconnect(); - }); -} diff --git a/packages/stream_chat_persistence/test/src/dao/member_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/member_dao_test.dart deleted file mode 100644 index d96fcd02ee..0000000000 --- a/packages/stream_chat_persistence/test/src/dao/member_dao_test.dart +++ /dev/null @@ -1,155 +0,0 @@ -import 'dart:math' as math; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/dao/dao.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -import '../../stream_chat_persistence_client_test.dart'; -import '../utils/date_matcher.dart'; - -void main() { - late MemberDao memberDao; - late DriftChatDatabase database; - - setUp(() { - database = testDatabaseProvider('testUserId'); - memberDao = database.memberDao; - }); - - Future> _prepareTestData(String cid) async { - final channels = [ChannelModel(cid: cid)]; - final users = List.generate(3, (index) => User(id: 'testUserId$index')); - final memberList = List.generate( - 3, - (index) => Member( - user: users[index], - banned: math.Random().nextBool(), - shadowBanned: math.Random().nextBool(), - createdAt: DateTime.now(), - isModerator: math.Random().nextBool(), - invited: math.Random().nextBool(), - inviteAcceptedAt: DateTime.now(), - channelRole: 'testRole', - updatedAt: DateTime.now(), - ), - ); - await database.userDao.updateUsers(users); - await database.channelDao.updateChannels(channels); - await memberDao.updateMembers(cid, memberList); - return memberList; - } - - test('getMembersByCid', () async { - const cid = 'test:Cid'; - - // Should be empty initially - final members = await memberDao.getMembersByCid(cid); - expect(members, isEmpty); - - // Preparing test data - final memberList = await _prepareTestData(cid); - - // Should match the previous test data - final fetchedMembers = await memberDao.getMembersByCid(cid); - expect(fetchedMembers.length, memberList.length); - for (var i = 0; i < fetchedMembers.length; i++) { - final member = memberList[i]; - final fetchedMember = fetchedMembers[i]; - expect(fetchedMember.user!.id, member.user!.id); - expect(fetchedMember.banned, member.banned); - expect(fetchedMember.shadowBanned, member.shadowBanned); - expect(fetchedMember.createdAt, isSameDateAs(member.createdAt)); - expect(fetchedMember.isModerator, member.isModerator); - expect(fetchedMember.invited, member.invited); - expect(fetchedMember.channelRole, member.channelRole); - expect(fetchedMember.updatedAt, isSameDateAs(member.updatedAt)); - expect( - fetchedMember.inviteAcceptedAt, - isSameDateAs(member.inviteAcceptedAt), - ); - } - }); - - test('updateMembers', () async { - const cid = 'test:Cid'; - - // Preparing test data - final memberList = await _prepareTestData(cid); - - // Should match the previous test data - final fetchedMembers = await memberDao.getMembersByCid(cid); - expect(fetchedMembers.length, memberList.length); - for (var i = 0; i < fetchedMembers.length; i++) { - final member = memberList[i]; - final fetchedMember = fetchedMembers[i]; - expect(fetchedMember.user!.id, member.user!.id); - expect(fetchedMember.banned, member.banned); - expect(fetchedMember.shadowBanned, member.shadowBanned); - expect(fetchedMember.createdAt, isSameDateAs(member.createdAt)); - expect(fetchedMember.isModerator, member.isModerator); - expect(fetchedMember.invited, member.invited); - expect(fetchedMember.channelRole, member.channelRole); - expect(fetchedMember.updatedAt, isSameDateAs(member.updatedAt)); - expect( - fetchedMember.inviteAcceptedAt, - isSameDateAs(member.inviteAcceptedAt), - ); - } - - // Modifying one of the member and also adding one new - final copyMember = fetchedMembers.first.copyWith(banned: true); - final newUser = User(id: 'testUserId3'); - final newMember = Member( - user: newUser, - banned: math.Random().nextBool(), - shadowBanned: math.Random().nextBool(), - createdAt: DateTime.now(), - isModerator: math.Random().nextBool(), - invited: math.Random().nextBool(), - inviteAcceptedAt: DateTime.now(), - channelRole: 'testRole', - updatedAt: DateTime.now(), - ); - await database.userDao.updateUsers([newUser]); - await memberDao.updateMembers(cid, [copyMember, newMember]); - - // Fetched member length should be one more than inserted members. - // copyMember `banned` modified field should be true. - // Fetched members should contain the newMember. - final newFetchedMembers = await memberDao.getMembersByCid(cid); - expect(newFetchedMembers.length, fetchedMembers.length + 1); - expect( - newFetchedMembers - .firstWhere((it) => it.user!.id == copyMember.user!.id) - .banned, - true, - ); - expect( - newFetchedMembers - .where((it) => it.user!.id == newMember.user!.id) - .isNotEmpty, - true, - ); - }); - - test('deleteMemberByCids', () async { - const cid = 'test:Cid'; - - // Preparing test data - final members = await _prepareTestData(cid); - final fetchedMembers = await memberDao.getMembersByCid(cid); - expect(members.length, fetchedMembers.length); - - // Deleting all the members - await memberDao.deleteMemberByCids([cid]); - - // Fetched member list should be empty - final newFetchedMembers = await memberDao.getMembersByCid(cid); - expect(newFetchedMembers, isEmpty); - }); - - tearDown(() async { - await database.disconnect(); - }); -} diff --git a/packages/stream_chat_persistence/test/src/dao/message_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/message_dao_test.dart deleted file mode 100644 index ca98d622ca..0000000000 --- a/packages/stream_chat_persistence/test/src/dao/message_dao_test.dart +++ /dev/null @@ -1,434 +0,0 @@ -import 'dart:math' as math; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/dao/dao.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -import '../../stream_chat_persistence_client_test.dart'; - -void main() { - late MessageDao messageDao; - late DriftChatDatabase database; - - setUp(() { - database = testDatabaseProvider('testUserId'); - messageDao = database.messageDao; - }); - - Future> _prepareTestData( - String cid, { - bool quoted = false, - bool threads = false, - bool mapAllThreadToFirstMessage = false, - int count = 3, - }) async { - final channels = [ChannelModel(cid: cid)]; - final users = List.generate(count, (index) => User(id: 'testUserId$index')); - final messages = List.generate( - count, - (index) => Message( - id: 'testMessageId$cid$index', - type: 'testType', - user: users[index], - createdAt: DateTime.now(), - shadowed: math.Random().nextBool(), - replyCount: index, - updatedAt: DateTime.now(), - extraData: const {'extra_test_field': 'extraTestData'}, - text: 'Hello #$index', - pinned: math.Random().nextBool(), - pinnedAt: DateTime.now(), - pinnedBy: User(id: 'testUserId$index'), - i18n: { - 'en_text': 'Hello #$index', - 'hi_text': 'ą¤Øą¤®ą¤øą„ą¤¤ą„‡ #$index', - 'language': 'en', - }, - ), - ); - final quotedMessages = List.generate( - count, - (index) => Message( - id: 'testQuotedMessageId$cid$index', - type: 'testType', - user: users[index], - createdAt: DateTime.now(), - shadowed: math.Random().nextBool(), - replyCount: index, - updatedAt: DateTime.now(), - extraData: const {'extra_test_field': 'extraTestData'}, - text: 'Hello #$index', - quotedMessageId: messages[index].id, - pinned: math.Random().nextBool(), - pinnedAt: DateTime.now(), - pinnedBy: User(id: 'testUserId$index'), - i18n: { - 'en_text': 'Hello #$index', - 'hi_text': 'ą¤Øą¤®ą¤øą„ą¤¤ą„‡ #$index', - 'language': 'en', - }, - ), - ); - final threadMessages = List.generate( - count, - (index) => Message( - id: 'testThreadMessageId$cid$index', - type: 'testType', - user: users[index], - parentId: - mapAllThreadToFirstMessage ? messages[0].id : messages[index].id, - createdAt: DateTime.now(), - shadowed: math.Random().nextBool(), - replyCount: index, - updatedAt: DateTime.now(), - extraData: const {'extra_test_field': 'extraTestData'}, - text: 'Hello #$index', - pinned: math.Random().nextBool(), - pinnedAt: DateTime.now(), - pinnedBy: User(id: 'testUserId$index'), - i18n: { - 'en_text': 'Hello #$index', - 'hi_text': 'ą¤Øą¤®ą¤øą„ą¤¤ą„‡ #$index', - 'language': 'en', - }, - ), - ); - final allMessages = [ - ...messages, - if (quoted) ...quotedMessages, - if (threads) ...threadMessages - ]; - final reaction = Reaction( - type: 'type', - messageId: allMessages.first.id, - user: users.first, - ); - await database.userDao.updateUsers(users); - await database.channelDao.updateChannels(channels); - await messageDao.updateMessages(cid, allMessages); - await database.reactionDao.updateReactions([reaction]); - return allMessages; - } - - test('deleteMessageByIds', () async { - const cid = 'test:Cid'; - - // Preparing test data - final insertedMessages = await _prepareTestData(cid); - - // Fetched message list should match the test message list length - final messages = await messageDao.getMessagesByCid(cid); - expect(messages.length, insertedMessages.length); - - final firstMessageId = messages.first.id; - - // Fetched reactions list should have one reaction for given message id - final reactions = await database.reactionDao.getReactions(firstMessageId); - expect(reactions.length, 1); - - // Deleting 2 messages from DB - await messageDao.deleteMessageByIds( - [firstMessageId, 'testMessageId${cid}1'], - ); - - // New fetched messages length should 2 less than the - // previous fetched messages - final newMessages = await messageDao.getMessagesByCid(cid); - expect(newMessages.length, messages.length - 2); - - // Reaction for the first message should be deleted too - final newReactions = - await database.reactionDao.getReactions(firstMessageId); - expect(newReactions, isEmpty); - }); - - group('deleteMessageByCids', () { - const cid1 = 'test:Cid1'; - const cid2 = 'test:Cid2'; - - test( - 'should delete all the messages and reactions of first channel', - () async { - // Preparing test data - final cid1InsertedMessages = await _prepareTestData(cid1); - final cid2InsertedMessages = await _prepareTestData(cid2); - - // Fetched message list should match the test message list length - final cid1Messages = await messageDao.getMessagesByCid(cid1); - final cid2Messages = await messageDao.getMessagesByCid(cid2); - expect(cid1Messages.length, cid1InsertedMessages.length); - expect(cid2Messages.length, cid2InsertedMessages.length); - - // Fetched reactions list should have one reaction for given message id - final cid1firstMessageId = cid1Messages.first.id; - final cid1Reactions = - await database.reactionDao.getReactions(cid1firstMessageId); - expect(cid1Reactions.length, 1); - - // Deleting all the messages of cid1 - await messageDao.deleteMessageByCids([cid1]); - - // Fetched messages length of only cid1 should be empty - final cid1FetchedMessages = await messageDao.getMessagesByCid(cid1); - final cid2FetchedMessages = await messageDao.getMessagesByCid(cid2); - expect(cid1FetchedMessages, isEmpty); - expect(cid2FetchedMessages, isNotEmpty); - - // Reaction for the first message should be deleted too - final cid1FetchedReactions = - await database.reactionDao.getReactions(cid1firstMessageId); - expect(cid1FetchedReactions, isEmpty); - }, - ); - - test( - 'should delete all the messages and reactions of both channel', - () async { - // Preparing test data - final cid1InsertedMessages = await _prepareTestData(cid1); - final cid2InsertedMessages = await _prepareTestData(cid2); - - // Fetched message list should match the test message list length - final cid1Messages = await messageDao.getMessagesByCid(cid1); - final cid2Messages = await messageDao.getMessagesByCid(cid2); - expect(cid1Messages.length, cid1InsertedMessages.length); - expect(cid2Messages.length, cid2InsertedMessages.length); - - // Fetched reactions list should have one reaction for given message id - final cid1FirstMessageId = cid1Messages.first.id; - final cid1Reactions = - await database.reactionDao.getReactions(cid1FirstMessageId); - expect(cid1Reactions.length, 1); - final cid2FirstMessageId = cid2Messages.first.id; - final cid2Reactions = - await database.reactionDao.getReactions(cid2FirstMessageId); - expect(cid2Reactions.length, 1); - - // Deleting all the messages of cid1 - await messageDao.deleteMessageByCids([cid1, cid2]); - - // Fetched messages length of both cid1 and cid2 should be empty - final cid1FetchedMessages = await messageDao.getMessagesByCid(cid1); - final cid2FetchedMessages = await messageDao.getMessagesByCid(cid2); - expect(cid1FetchedMessages, isEmpty); - expect(cid2FetchedMessages, isEmpty); - - // Reaction for the first message should be deleted too - final cid1FetchedReactions = - await database.reactionDao.getReactions(cid1FirstMessageId); - expect(cid1FetchedReactions, isEmpty); - final cid2FetchedReactions = - await database.reactionDao.getReactions(cid2FirstMessageId); - expect(cid2FetchedReactions, isEmpty); - }, - ); - }); - - test('getMessageById', () async { - const cid = 'test:Cid'; - const id = 'testMessageId${cid}0'; - - // Should be null initially - final message = await messageDao.getMessageById(id); - expect(message, isNull); - - // Adding test message with the cid and id - final insertedMessages = await _prepareTestData(cid, count: 1); - expect(insertedMessages.first.id, id); - - // Fetched message id should match the inserted message id - final fetchedMessage = await messageDao.getMessageById(id); - expect(fetchedMessage, isNotNull); - expect(fetchedMessage!.id, insertedMessages.first.id); - }); - - test('getThreadMessages', () async { - const cid = 'test:Cid'; - - // Messages should be empty initially - final messages = await messageDao.getThreadMessages(cid); - expect(messages, isEmpty); - - // Preparing test data - final insertedMessages = await _prepareTestData(cid, threads: true); - expect(insertedMessages, isNotEmpty); - - // Should fetch all the thread messages of cid - final threadMessages = await messageDao.getThreadMessages(cid); - expect(threadMessages, isNotEmpty); - for (final message in threadMessages) { - expect(message.parentId, isNotNull); - } - }); - - test('getThreadMessagesByParentId', () async { - const cid = 'test:Cid'; - const parentId = 'testMessageId${cid}0'; - - // Messages should be empty initially - final messages = await messageDao.getThreadMessagesByParentId(parentId); - expect(messages, isEmpty); - - // Preparing test data - final insertedMessages = await _prepareTestData(cid, threads: true); - expect(insertedMessages, isNotEmpty); - - // Should fetch all the thread messages of parentId - final threadMessages = - await messageDao.getThreadMessagesByParentId(parentId); - expect(threadMessages.length, 1); - expect(threadMessages.first.parentId, parentId); - }); - - test('getThreadMessagesByParentId along with pagination', () async { - const cid = 'test:Cid'; - const parentId = 'testMessageId${cid}0'; - const options = PaginationParams( - limit: 15, - lessThan: 'testThreadMessageId${cid}25', - greaterThanOrEqual: 'testThreadMessageId${cid}5', - ); - - // Messages should be empty initially - final messages = await messageDao.getThreadMessagesByParentId( - parentId, - options: options, - ); - expect(messages, isEmpty); - - // Preparing test data - final insertedMessages = await _prepareTestData( - cid, - threads: true, - mapAllThreadToFirstMessage: true, - count: 30, - ); - expect(insertedMessages, isNotEmpty); - - // Should fetch all the thread messages of parentId and apply the pagination - final threadMessages = await messageDao.getThreadMessagesByParentId( - parentId, - options: options, - ); - expect(threadMessages.length, 15); - expect(threadMessages.first.parentId, parentId); - }); - - test('getMessagesByCid', () async { - const cid = 'test:Cid'; - - // Should be empty initially - final messages = await messageDao.getMessagesByCid(cid); - expect(messages, isEmpty); - - // Preparing test data - final insertedMessages = await _prepareTestData(cid); - expect(insertedMessages, isNotEmpty); - - // Fetched message should match the inserted messages - final fetchedMessages = await messageDao.getMessagesByCid(cid); - expect(fetchedMessages.length, insertedMessages.length); - for (var i = 0; i < fetchedMessages.length; i++) { - final fetchedMessage = fetchedMessages[i]; - final insertedMessage = insertedMessages[i]; - expect(fetchedMessage.id, insertedMessage.id); - } - }); - - test('getMessagesByCid along with quotedMessage', () async { - const cid = 'test:Cid'; - - // Should be empty initially - final messages = await messageDao.getMessagesByCid(cid); - expect(messages, isEmpty); - - // Preparing test data - final insertedMessages = await _prepareTestData(cid, quoted: true); - expect(insertedMessages, isNotEmpty); - - // Fetched message should match the inserted messages - final fetchedMessages = await messageDao.getMessagesByCid(cid); - expect(fetchedMessages.length, insertedMessages.length); - final quoted = fetchedMessages.where((it) => it.quotedMessage != null); - expect(quoted.length, insertedMessages.length / 2); - }); - - test('getMessagesByCid along with pagination', () async { - const cid = 'test:Cid'; - const limit = 15; - const lessThan = 'testMessageId${cid}25'; - const greaterThanOrEqual = 'testMessageId${cid}5'; - const pagination = PaginationParams( - limit: limit, - lessThan: lessThan, - greaterThanOrEqual: greaterThanOrEqual, - ); - - // Should be empty initially - final messages = await messageDao.getMessagesByCid( - cid, - messagePagination: pagination, - ); - expect(messages, isEmpty); - - // Preparing test data - final insertedMessages = await _prepareTestData(cid, count: 30); - expect(insertedMessages, isNotEmpty); - - // Fetched message should match the inserted messages - final fetchedMessages = await messageDao.getMessagesByCid( - cid, - messagePagination: pagination, - ); - expect(fetchedMessages.length, limit); - expect(fetchedMessages.last.id, 'testMessageId${cid}24'); - expect(fetchedMessages.first.id != lessThan, true); - }); - - test('updateMessages', () async { - const cid = 'test:Cid'; - - // Preparing test data - final insertedMessages = await _prepareTestData(cid); - expect(insertedMessages, isNotEmpty); - - // Modifying one of the message and also adding one new - final copyMessage = insertedMessages.first.copyWith(showInChannel: false); - final newMessage = Message( - id: 'testMessageId${cid}4', - type: 'testType', - user: User(id: 'testUserId4'), - createdAt: DateTime.now(), - shadowed: math.Random().nextBool(), - showInChannel: math.Random().nextBool(), - replyCount: 4, - updatedAt: DateTime.now(), - extraData: const {'extra_test_field': 'extraTestData'}, - text: 'Dummy text #4', - pinned: math.Random().nextBool(), - pinnedAt: DateTime.now(), - pinnedBy: User(id: 'testUserId4'), - ); - - await messageDao.updateMessages(cid, [copyMessage, newMessage]); - - // Fetched messages length should be one more than inserted message. - // copyMessage `showInChannel` modified field should be false. - // Fetched messages should contain the newMessage. - final fetchedMessages = await messageDao.getMessagesByCid(cid); - expect(fetchedMessages.length, insertedMessages.length + 1); - expect( - fetchedMessages.firstWhere((it) => it.id == copyMessage.id).showInChannel, - false, - ); - expect( - fetchedMessages.map((it) => it.id).contains(newMessage.id), - true, - ); - }); - - tearDown(() async { - await database.disconnect(); - }); -} diff --git a/packages/stream_chat_persistence/test/src/dao/pinned_message_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/pinned_message_dao_test.dart deleted file mode 100644 index 4dcf5e0e3b..0000000000 --- a/packages/stream_chat_persistence/test/src/dao/pinned_message_dao_test.dart +++ /dev/null @@ -1,425 +0,0 @@ -import 'dart:math' as math; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/dao/dao.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -import '../../stream_chat_persistence_client_test.dart'; - -void main() { - late PinnedMessageDao pinnedMessageDao; - late DriftChatDatabase database; - - setUp(() { - database = testDatabaseProvider('testUserId'); - pinnedMessageDao = database.pinnedMessageDao; - }); - - Future> _prepareTestData( - String cid, { - bool quoted = false, - bool threads = false, - bool mapAllThreadToFirstMessage = false, - int count = 3, - }) async { - final channels = [ChannelModel(cid: cid)]; - final users = List.generate(count, (index) => User(id: 'testUserId$index')); - final messages = List.generate( - count, - (index) => Message( - id: 'testMessageId$cid$index', - type: 'testType', - user: users[index], - createdAt: DateTime.now(), - shadowed: math.Random().nextBool(), - replyCount: index, - updatedAt: DateTime.now(), - extraData: const {'extra_test_field': 'extraTestData'}, - text: 'Dummy text #$index', - pinned: math.Random().nextBool(), - pinnedAt: DateTime.now(), - pinnedBy: User(id: 'testUserId$index'), - ), - ); - final quotedMessages = List.generate( - count, - (index) => Message( - id: 'testQuotedMessageId$cid$index', - type: 'testType', - user: users[index], - createdAt: DateTime.now(), - shadowed: math.Random().nextBool(), - replyCount: index, - updatedAt: DateTime.now(), - extraData: const {'extra_test_field': 'extraTestData'}, - text: 'Dummy text #$index', - quotedMessageId: messages[index].id, - pinned: math.Random().nextBool(), - pinnedAt: DateTime.now(), - pinnedBy: User(id: 'testUserId$index'), - ), - ); - final threadMessages = List.generate( - count, - (index) => Message( - id: 'testThreadMessageId$cid$index', - type: 'testType', - user: users[index], - parentId: - mapAllThreadToFirstMessage ? messages[0].id : messages[index].id, - createdAt: DateTime.now(), - shadowed: math.Random().nextBool(), - replyCount: index, - updatedAt: DateTime.now(), - extraData: const {'extra_test_field': 'extraTestData'}, - text: 'Dummy text #$index', - pinned: math.Random().nextBool(), - pinnedAt: DateTime.now(), - pinnedBy: User(id: 'testUserId$index'), - ), - ); - final allMessages = [ - ...messages, - if (quoted) ...quotedMessages, - if (threads) ...threadMessages - ]; - final reaction = Reaction( - type: 'type', - messageId: allMessages.first.id, - user: users.first, - ); - await database.userDao.updateUsers(users); - await database.channelDao.updateChannels(channels); - await pinnedMessageDao.updateMessages(cid, allMessages); - await database.pinnedMessageReactionDao.updateReactions([reaction]); - return allMessages; - } - - test('deleteMessageByIds', () async { - const cid = 'test:Cid'; - - // Preparing test data - final insertedMessages = await _prepareTestData(cid); - - // Fetched message list should match the test message list length - final messages = await pinnedMessageDao.getMessagesByCid(cid); - expect(messages.length, insertedMessages.length); - - final firstMessageId = messages.first.id; - - // Fetched reactions list should have one reaction for given message id - final reactions = - await database.pinnedMessageReactionDao.getReactions(firstMessageId); - expect(reactions.length, 1); - - // Deleting 2 messages from DB - await pinnedMessageDao.deleteMessageByIds( - [firstMessageId, 'testMessageId${cid}1'], - ); - - // New fetched messages length should 2 less than the - // previous fetched messages - final newMessages = await pinnedMessageDao.getMessagesByCid(cid); - expect(newMessages.length, messages.length - 2); - - // Reaction for the first message should be deleted too - final newReactions = - await database.pinnedMessageReactionDao.getReactions(firstMessageId); - expect(newReactions, isEmpty); - }); - - group('deleteMessageByCids', () { - const cid1 = 'test:Cid1'; - const cid2 = 'test:Cid2'; - - test( - 'should delete all the messages and reactions of first channel', - () async { - // Preparing test data - final cid1InsertedMessages = await _prepareTestData(cid1); - final cid2InsertedMessages = await _prepareTestData(cid2); - - // Fetched message list should match the test message list length - final cid1Messages = await pinnedMessageDao.getMessagesByCid(cid1); - final cid2Messages = await pinnedMessageDao.getMessagesByCid(cid2); - expect(cid1Messages.length, cid1InsertedMessages.length); - expect(cid2Messages.length, cid2InsertedMessages.length); - - // Fetched reactions list should have one reaction for given message id - final cid1firstMessageId = cid1Messages.first.id; - final cid1Reactions = await database.pinnedMessageReactionDao - .getReactions(cid1firstMessageId); - expect(cid1Reactions.length, 1); - - // Deleting all the messages of cid1 - await pinnedMessageDao.deleteMessageByCids([cid1]); - - // Fetched messages length of only cid1 should be empty - final cid1FetchedMessages = - await pinnedMessageDao.getMessagesByCid(cid1); - final cid2FetchedMessages = - await pinnedMessageDao.getMessagesByCid(cid2); - expect(cid1FetchedMessages, isEmpty); - expect(cid2FetchedMessages, isNotEmpty); - - // Reaction for the first message should be deleted too - final cid1FetchedReactions = await database.pinnedMessageReactionDao - .getReactions(cid1firstMessageId); - expect(cid1FetchedReactions, isEmpty); - }, - ); - - test( - 'should delete all the messages and reactions of both channel', - () async { - // Preparing test data - final cid1InsertedMessages = await _prepareTestData(cid1); - final cid2InsertedMessages = await _prepareTestData(cid2); - - // Fetched message list should match the test message list length - final cid1Messages = await pinnedMessageDao.getMessagesByCid(cid1); - final cid2Messages = await pinnedMessageDao.getMessagesByCid(cid2); - expect(cid1Messages.length, cid1InsertedMessages.length); - expect(cid2Messages.length, cid2InsertedMessages.length); - - // Fetched reactions list should have one reaction for given message id - final cid1FirstMessageId = cid1Messages.first.id; - final cid1Reactions = await database.pinnedMessageReactionDao - .getReactions(cid1FirstMessageId); - expect(cid1Reactions.length, 1); - final cid2FirstMessageId = cid2Messages.first.id; - final cid2Reactions = await database.pinnedMessageReactionDao - .getReactions(cid2FirstMessageId); - expect(cid2Reactions.length, 1); - - // Deleting all the messages of cid1 - await pinnedMessageDao.deleteMessageByCids([cid1, cid2]); - - // Fetched messages length of both cid1 and cid2 should be empty - final cid1FetchedMessages = - await pinnedMessageDao.getMessagesByCid(cid1); - final cid2FetchedMessages = - await pinnedMessageDao.getMessagesByCid(cid2); - expect(cid1FetchedMessages, isEmpty); - expect(cid2FetchedMessages, isEmpty); - - // Reaction for the first message should be deleted too - final cid1FetchedReactions = await database.pinnedMessageReactionDao - .getReactions(cid1FirstMessageId); - expect(cid1FetchedReactions, isEmpty); - final cid2FetchedReactions = await database.pinnedMessageReactionDao - .getReactions(cid2FirstMessageId); - expect(cid2FetchedReactions, isEmpty); - }, - ); - }); - - test('getMessageById', () async { - const cid = 'test:Cid'; - const id = 'testMessageId${cid}0'; - - // Should be null initially - final message = await pinnedMessageDao.getMessageById(id); - expect(message, isNull); - - // Adding test message with the cid and id - final insertedMessages = await _prepareTestData(cid, count: 1); - expect(insertedMessages.first.id, id); - - // Fetched message id should match the inserted message id - final fetchedMessage = await pinnedMessageDao.getMessageById(id); - expect(fetchedMessage, isNotNull); - expect(fetchedMessage!.id, insertedMessages.first.id); - }); - - test('getThreadMessages', () async { - const cid = 'test:Cid'; - - // Messages should be empty initially - final messages = await pinnedMessageDao.getThreadMessages(cid); - expect(messages, isEmpty); - - // Preparing test data - final insertedMessages = await _prepareTestData(cid, threads: true); - expect(insertedMessages, isNotEmpty); - - // Should fetch all the thread messages of cid - final threadMessages = await pinnedMessageDao.getThreadMessages(cid); - expect(threadMessages, isNotEmpty); - for (final message in threadMessages) { - expect(message.parentId, isNotNull); - } - }); - - test('getThreadMessagesByParentId', () async { - const cid = 'test:Cid'; - const parentId = 'testMessageId${cid}0'; - - // Messages should be empty initially - final messages = - await pinnedMessageDao.getThreadMessagesByParentId(parentId); - expect(messages, isEmpty); - - // Preparing test data - final insertedMessages = await _prepareTestData(cid, threads: true); - expect(insertedMessages, isNotEmpty); - - // Should fetch all the thread messages of parentId - final threadMessages = - await pinnedMessageDao.getThreadMessagesByParentId(parentId); - expect(threadMessages.length, 1); - expect(threadMessages.first.parentId, parentId); - }); - - test('getThreadMessagesByParentId along with pagination', () async { - const cid = 'test:Cid'; - const parentId = 'testMessageId${cid}0'; - const options = PaginationParams( - limit: 15, - lessThan: 'testThreadMessageId${cid}25', - greaterThanOrEqual: 'testThreadMessageId${cid}5', - ); - - // Messages should be empty initially - final messages = await pinnedMessageDao.getThreadMessagesByParentId( - parentId, - options: options, - ); - expect(messages, isEmpty); - - // Preparing test data - final insertedMessages = await _prepareTestData( - cid, - threads: true, - mapAllThreadToFirstMessage: true, - count: 30, - ); - expect(insertedMessages, isNotEmpty); - - // Should fetch all the thread messages of parentId and apply the pagination - final threadMessages = await pinnedMessageDao.getThreadMessagesByParentId( - parentId, - options: options, - ); - expect(threadMessages.length, 15); - expect(threadMessages.first.parentId, parentId); - }); - - test('getMessagesByCid', () async { - const cid = 'test:Cid'; - - // Should be empty initially - final messages = await pinnedMessageDao.getMessagesByCid(cid); - expect(messages, isEmpty); - - // Preparing test data - final insertedMessages = await _prepareTestData(cid); - expect(insertedMessages, isNotEmpty); - - // Fetched message should match the inserted messages - final fetchedMessages = await pinnedMessageDao.getMessagesByCid(cid); - expect(fetchedMessages.length, insertedMessages.length); - for (var i = 0; i < fetchedMessages.length; i++) { - final fetchedMessage = fetchedMessages[i]; - final insertedMessage = insertedMessages[i]; - expect(fetchedMessage.id, insertedMessage.id); - } - }); - - test('getMessagesByCid along with quotedMessage', () async { - const cid = 'test:Cid'; - - // Should be empty initially - final messages = await pinnedMessageDao.getMessagesByCid(cid); - expect(messages, isEmpty); - - // Preparing test data - final insertedMessages = await _prepareTestData(cid, quoted: true); - expect(insertedMessages, isNotEmpty); - - // Fetched message should match the inserted messages - final fetchedMessages = await pinnedMessageDao.getMessagesByCid(cid); - expect(fetchedMessages.length, insertedMessages.length); - final quoted = fetchedMessages.where((it) => it.quotedMessage != null); - expect(quoted.length, insertedMessages.length / 2); - }); - - test('getMessagesByCid along with pagination', () async { - const cid = 'test:Cid'; - const limit = 15; - const lessThan = 'testMessageId${cid}25'; - const greaterThanOrEqual = 'testMessageId${cid}5'; - const pagination = PaginationParams( - limit: limit, - lessThan: lessThan, - greaterThanOrEqual: greaterThanOrEqual, - ); - - // Should be empty initially - final messages = await pinnedMessageDao.getMessagesByCid( - cid, - messagePagination: pagination, - ); - expect(messages, isEmpty); - - // Preparing test data - final insertedMessages = await _prepareTestData(cid, count: 30); - expect(insertedMessages, isNotEmpty); - - // Fetched message should match the inserted messages - final fetchedMessages = await pinnedMessageDao.getMessagesByCid( - cid, - messagePagination: pagination, - ); - expect(fetchedMessages.length, limit); - expect(fetchedMessages.first.id, greaterThanOrEqual); - expect(fetchedMessages.last.id != lessThan, true); - }); - - test('updateMessages', () async { - const cid = 'test:Cid'; - - // Preparing test data - final insertedMessages = await _prepareTestData(cid); - expect(insertedMessages, isNotEmpty); - - // Modifying one of the message and also adding one new - final copyMessage = insertedMessages.first.copyWith(showInChannel: false); - final newMessage = Message( - id: 'testMessageId${cid}4', - type: 'testType', - user: User(id: 'testUserId4'), - createdAt: DateTime.now(), - shadowed: math.Random().nextBool(), - showInChannel: math.Random().nextBool(), - replyCount: 4, - updatedAt: DateTime.now(), - extraData: const {'extra_test_field': 'extraTestData'}, - text: 'Dummy text #4', - pinned: math.Random().nextBool(), - pinnedAt: DateTime.now(), - pinnedBy: User(id: 'testUserId4'), - ); - - await pinnedMessageDao.updateMessages(cid, [copyMessage, newMessage]); - - // Fetched messages length should be one more than inserted message. - // copyMessage `showInChannel` modified field should be false. - // Fetched messages should contain the newMessage. - final fetchedMessages = await pinnedMessageDao.getMessagesByCid(cid); - expect(fetchedMessages.length, insertedMessages.length + 1); - expect( - fetchedMessages.firstWhere((it) => it.id == copyMessage.id).showInChannel, - false, - ); - expect( - fetchedMessages.map((it) => it.id).contains(newMessage.id), - true, - ); - }); - - tearDown(() async { - await database.disconnect(); - }); -} diff --git a/packages/stream_chat_persistence/test/src/dao/pinned_message_reaction_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/pinned_message_reaction_dao_test.dart deleted file mode 100644 index 44abc4a644..0000000000 --- a/packages/stream_chat_persistence/test/src/dao/pinned_message_reaction_dao_test.dart +++ /dev/null @@ -1,205 +0,0 @@ -import 'dart:math' as math; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/dao/pinned_message_reaction_dao.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -import '../../stream_chat_persistence_client_test.dart'; - -void main() { - late PinnedMessageReactionDao pinnedMessageReactionDao; - late DriftChatDatabase database; - - setUp(() { - database = testDatabaseProvider('testUserId'); - pinnedMessageReactionDao = database.pinnedMessageReactionDao; - }); - - Future> _prepareReactionData( - String messageId, { - String? userId, - int count = 3, - }) async { - const cid = 'test:Cid'; - final channels = [ChannelModel(cid: cid)]; - final users = List.generate(count, (index) => User(id: 'testUserId$index')); - final message = Message( - id: messageId, - type: 'testType', - user: users.first, - createdAt: DateTime.now(), - shadowed: math.Random().nextBool(), - showInChannel: math.Random().nextBool(), - replyCount: 3, - updatedAt: DateTime.now(), - extraData: const {'extra_test_field': 'extraTestData'}, - text: 'Dummy text', - pinned: math.Random().nextBool(), - pinnedAt: DateTime.now(), - pinnedBy: users.first, - ); - final reactions = List.generate( - count, - (index) => Reaction( - type: 'testType$index', - createdAt: DateTime.now(), - userId: userId ?? users[index].id, - messageId: message.id, - score: count + 3, - extraData: {'extra_test_field': 'extraTestData'}, - ), - ); - - await database.userDao.updateUsers(users); - await database.channelDao.updateChannels(channels); - await database.pinnedMessageDao.updateMessages(cid, [message]); - await pinnedMessageReactionDao.updateReactions(reactions); - - return reactions; - } - - test('getReactions', () async { - const messageId = 'testMessageId'; - - // Should be empty initially - final reactions = await pinnedMessageReactionDao.getReactions(messageId); - expect(reactions, isEmpty); - - // Adding sample reactions - final insertedReactions = await _prepareReactionData(messageId); - expect(insertedReactions, isNotEmpty); - - // Fetched reaction length should match inserted reactions length. - // Every reaction messageId should match the provided messageId. - final fetchedReactions = - await pinnedMessageReactionDao.getReactions(messageId); - expect(fetchedReactions.length, insertedReactions.length); - expect(fetchedReactions.every((it) => it.messageId == messageId), true); - }); - - test('getReactionsByUserId', () async { - const messageId = 'testMessageId'; - const userId = 'testUserId'; - - // Should be empty initially - final reactions = - await pinnedMessageReactionDao.getReactionsByUserId(messageId, userId); - expect(reactions, isEmpty); - - // Adding sample reactions - final insertedReactions = - await _prepareReactionData(messageId, userId: userId); - expect(insertedReactions, isNotEmpty); - - // Fetched reaction length should match inserted reactions length. - // Every reaction messageId should match the provided messageId. - // Every reaction userId should match the provided userId. - final fetchedReactions = - await pinnedMessageReactionDao.getReactionsByUserId(messageId, userId); - expect(fetchedReactions.length, insertedReactions.length); - expect(fetchedReactions.every((it) => it.messageId == messageId), true); - expect(fetchedReactions.every((it) => it.userId == userId), true); - }); - - test('updateReactions', () async { - const messageId = 'testMessageId'; - - // Preparing test data - final reactions = await _prepareReactionData(messageId); - - // Modifying one of the reaction and also adding one new - final copyReaction = reactions.first.copyWith(score: 33); - final newReaction = Reaction( - type: 'testType3', - createdAt: DateTime.now(), - userId: 'testUserId3', - messageId: messageId, - score: 30, - extraData: {'extra_test_field': 'extraTestData'}, - ); - - await pinnedMessageReactionDao.updateReactions([copyReaction, newReaction]); - - // Fetched reaction length should be one more than inserted reactions. - // copyReaction `score` modified field should be 33. - // Fetched reactions should contain the newReaction. - final fetchedReactions = - await pinnedMessageReactionDao.getReactions(messageId); - expect(fetchedReactions.length, reactions.length + 1); - expect( - fetchedReactions - .firstWhere((it) => - it.userId == copyReaction.userId && it.type == copyReaction.type) - .score, - 33, - ); - expect( - fetchedReactions - .where((it) => - it.userId == newReaction.userId && it.type == newReaction.type) - .isNotEmpty, - true, - ); - }); - - group('deleteReactionsByMessageIds', () { - const messageId1 = 'testMessageId1'; - const messageId2 = 'testMessageId2'; - test('should delete all the reactions of first message', () async { - // Preparing test data - final insertedReactions1 = await _prepareReactionData(messageId1); - final insertedReactions2 = await _prepareReactionData(messageId2); - - // Fetched reaction list length should match - // the inserted reactions list length - final reactions1 = - await pinnedMessageReactionDao.getReactions(messageId1); - final reactions2 = - await pinnedMessageReactionDao.getReactions(messageId2); - expect(reactions1.length, insertedReactions1.length); - expect(reactions2.length, insertedReactions2.length); - - // Deleting all the reactions of messageId1 - await pinnedMessageReactionDao.deleteReactionsByMessageIds([messageId1]); - - // Fetched reactions length of only messageId1 should be empty - final fetchedReactions1 = - await pinnedMessageReactionDao.getReactions(messageId1); - final fetchedReactions2 = - await pinnedMessageReactionDao.getReactions(messageId2); - expect(fetchedReactions1, isEmpty); - expect(fetchedReactions2, isNotEmpty); - }); - test('should delete all the messages of both message', () async { - // Preparing test data - final insertedReactions1 = await _prepareReactionData(messageId1); - final insertedReactions2 = await _prepareReactionData(messageId2); - - // Fetched reaction list length should match - // the inserted reactions list length - final reactions1 = - await pinnedMessageReactionDao.getReactions(messageId1); - final reactions2 = - await pinnedMessageReactionDao.getReactions(messageId2); - expect(reactions1.length, insertedReactions1.length); - expect(reactions2.length, insertedReactions2.length); - - // Deleting all the reactions of messageId1 and messageId2 - await pinnedMessageReactionDao - .deleteReactionsByMessageIds([messageId1, messageId2]); - - // Fetched reactions length of both messages should be empty - final fetchedReactions1 = - await pinnedMessageReactionDao.getReactions(messageId1); - final fetchedReactions2 = - await pinnedMessageReactionDao.getReactions(messageId2); - expect(fetchedReactions1, isEmpty); - expect(fetchedReactions2, isEmpty); - }); - }); - - tearDown(() async { - await database.disconnect(); - }); -} diff --git a/packages/stream_chat_persistence/test/src/dao/poll_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/poll_dao_test.dart deleted file mode 100644 index 3f0b551670..0000000000 --- a/packages/stream_chat_persistence/test/src/dao/poll_dao_test.dart +++ /dev/null @@ -1,170 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/dao/dao.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -import '../../stream_chat_persistence_client_test.dart'; - -void main() { - late PollDao pollDao; - late DriftChatDatabase database; - - setUp(() { - database = testDatabaseProvider('testUserId'); - pollDao = database.pollDao; - }); - - List _expandPollVotes(Poll poll) { - final latestAnswers = poll.latestAnswers; - final latestVotes = poll.latestVotesByOption.values; - final ownVotesAndAnswers = poll.ownVotesAndAnswers; - return [ - ...latestAnswers, - ...latestVotes.expand((it) => it), - ...ownVotesAndAnswers, - ]; - } - - Future> _preparePollData({int count = 3}) async { - final polls = []; - - for (var i = 0; i < count; i++) { - final pollId = 'poll-$i'; - - final createdBy = User(id: 'user-$i', name: 'User $i'); - - const options = [ - PollOption(id: 'option-1', text: 'Red'), - PollOption(id: 'option-2', text: 'Blue'), - PollOption(id: 'option-3', text: 'Green'), - ]; - - final latestVotesByOption = { - for (final option in options) - option.id!: [ - for (var i = 0; i < count; i++) - PollVote( - id: 'pollVote-$i', - pollId: pollId, - userId: 'user-$i', - user: User(id: 'user-$i', name: 'User $i'), - optionId: option.id, - createdAt: DateTime.now(), - ), - ], - }; - - final voteCountsByOption = latestVotesByOption.map( - (key, value) => MapEntry(key, value.length), - ); - - final poll = Poll( - id: pollId, - name: 'testQuestion', - createdBy: createdBy, - createdById: createdBy.id, - options: options, - latestVotesByOption: latestVotesByOption, - voteCount: voteCountsByOption.values.reduce((a, b) => a + b), - voteCountsByOption: voteCountsByOption, - createdAt: DateTime.now(), - updatedAt: DateTime.now(), - enforceUniqueVote: false, - extraData: const {'test_extra_data': 'extraData'}, - ); - - polls.add(poll); - } - - final users = polls.map((it) => it.createdBy!).toList(); - final pollVotes = polls.expand(_expandPollVotes).toList(); - - await database.userDao.updateUsers(users); - await pollDao.updatePolls(polls); - await database.pollVoteDao.updatePollVotes(pollVotes); - - return polls; - } - - test('getPolls', () async { - // Should be empty initially - final polls = await pollDao.getPolls(); - expect(polls, isEmpty); - - // Adding sample polls - final insertedPolls = await _preparePollData(); - expect(insertedPolls, isNotEmpty); - - // Fetched polls length should match inserted polls length. - final fetchedPollVotes = await pollDao.getPolls(); - expect(fetchedPollVotes.length, insertedPolls.length); - }); - - test('updatePolls', () async { - // Preparing test data - final insertedPolls = await _preparePollData(); - - // Adding a new poll - final newPoll = insertedPolls.first.copyWith(id: 'newPollId'); - - await pollDao.updatePolls([newPoll]); - - // Fetched users length should be one more than inserted users. - // Fetched users should contain the newUser. - final fetchedPolls = await pollDao.getPolls(); - expect(fetchedPolls.length, insertedPolls.length + 1); - expect(fetchedPolls.any((it) => it.id == newPoll.id), isTrue); - }); - - test('getPollById', () async { - // Preparing test data - final insertedPolls = await _preparePollData(); - - // Fetched poll should not be null - final pollToFetch = insertedPolls.first; - final fetchedPoll = await pollDao.getPollById(pollToFetch.id); - expect(fetchedPoll!.id, pollToFetch.id); - }); - - test('deletePollsByIds', () async { - // Preparing test data - final insertedPolls = await _preparePollData(); - - // Deleting the first poll - final pollToDelete = insertedPolls.first; - await pollDao.deletePollsByIds([pollToDelete.id]); - - // Fetched poll list should be one less than inserted polls. - final fetchedPolls = await pollDao.getPolls(); - expect(fetchedPolls.length, insertedPolls.length - 1); - expect(fetchedPolls.any((it) => it.id == pollToDelete.id), isFalse); - }); - - test('deleting a poll should also delete its votes', () async { - // Preparing test data - final insertedPolls = await _preparePollData(); - - // Verify that the poll has votes - final pollToDelete = insertedPolls.first; - final pollVotes = await database.pollVoteDao.getPollVotes(pollToDelete.id); - expect(pollVotes, isNotEmpty); - - // Delete the poll - await pollDao.deletePollsByIds([pollToDelete.id]); - - // Fetched poll list should be one less than inserted polls. - final fetchedPolls = await pollDao.getPolls(); - expect(fetchedPolls.length, insertedPolls.length - 1); - expect(fetchedPolls.any((it) => it.id == pollToDelete.id), isFalse); - - // Fetched poll votes should be empty - final fetchedPollVotes = await database.pollVoteDao.getPollVotes( - pollToDelete.id, - ); - expect(fetchedPollVotes, isEmpty); - }); - - tearDown(() async { - await database.disconnect(); - }); -} diff --git a/packages/stream_chat_persistence/test/src/dao/poll_vote_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/poll_vote_dao_test.dart deleted file mode 100644 index 093aac84bf..0000000000 --- a/packages/stream_chat_persistence/test/src/dao/poll_vote_dao_test.dart +++ /dev/null @@ -1,177 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/dao/poll_vote_dao.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -import '../../stream_chat_persistence_client_test.dart'; - -void main() { - late PollVoteDao pollVoteDao; - late DriftChatDatabase database; - - setUp(() { - database = testDatabaseProvider('testUserId'); - pollVoteDao = database.pollVoteDao; - }); - - Future> _preparePollVoteData( - String pollId, { - String? userId, - int count = 3, - }) async { - const options = [ - PollOption(id: 'option-1', text: 'Red'), - PollOption(id: 'option-2', text: 'Blue'), - PollOption(id: 'option-3', text: 'Green'), - ]; - - final latestVotesByOption = { - for (final option in options) - option.id!: [ - for (var i = 0; i < count; i++) - PollVote( - id: '${option.id}-pollVote-$i', - pollId: pollId, - userId: 'user-$i', - user: User(id: 'user-$i', name: 'User $i'), - optionId: option.id, - createdAt: DateTime.now(), - ), - ], - }; - - final voteCountsByOption = latestVotesByOption.map( - (key, value) => MapEntry(key, value.length), - ); - - final users = latestVotesByOption.values - .expand((it) => it.map((it) => it.user!)) - .toList(); - - final poll = Poll( - id: pollId, - name: 'testQuestion', - createdById: userId ?? users.first.id, - votingVisibility: VotingVisibility.anonymous, - allowUserSuggestedOptions: true, - options: options, - voteCount: voteCountsByOption.values.reduce((a, b) => a + b), - voteCountsByOption: voteCountsByOption, - createdAt: DateTime.now(), - updatedAt: DateTime.now(), - enforceUniqueVote: false, - allowAnswers: true, - extraData: const {'test_extra_data': 'extraData'}, - ); - - final votes = latestVotesByOption.values.expand((it) => it).toList(); - - await database.userDao.updateUsers(users); - await database.pollDao.updatePolls([poll]); - await pollVoteDao.updatePollVotes(votes); - - return votes; - } - - test('getPollVotes', () async { - const pollId = 'testPollId'; - - // Should be empty initially - final pollVotes = await pollVoteDao.getPollVotes(pollId); - expect(pollVotes, isEmpty); - - // Adding sample poll votes - final insertedPollVotes = await _preparePollVoteData(pollId); - expect(insertedPollVotes, isNotEmpty); - - // Fetched pollVote length should match inserted pollVote length. - // Every pollVote pollId should match the provided pollId. - final fetchedPollVotes = await pollVoteDao.getPollVotes(pollId); - expect(fetchedPollVotes.length, insertedPollVotes.length); - expect(fetchedPollVotes.every((it) => it.pollId == pollId), true); - }); - - test('updatePollVotes', () async { - const pollId = 'testPollId'; - - // Preparing test data - final pollVotes = await _preparePollVoteData(pollId); - - // Adding a new pollVote - final newPollVote = PollVote( - id: 'pollVote-4', - pollId: pollId, - userId: 'user-3', - user: User(id: 'user-3', name: 'User 3'), - optionId: 'option-4', - createdAt: DateTime.now(), - ); - - await pollVoteDao.updatePollVotes([newPollVote]); - - // Fetched pollVote length should be one more than inserted pollVotes. - // Fetched pollVote should contain the newPollVote. - final fetchedPollVotes = await pollVoteDao.getPollVotes(pollId); - expect(fetchedPollVotes.length, pollVotes.length + 1); - expect( - fetchedPollVotes.any((it) => - it.id == newPollVote.id && - it.pollId == newPollVote.pollId && - it.optionId == newPollVote.optionId && - it.answerText == newPollVote.answerText), - true, - ); - }); - - group('deletePollVotesByPollIds', () { - const pollId1 = 'testPollId1'; - const pollId2 = 'testPollId2'; - test('should delete all the pollVotes of first poll', () async { - // Preparing test data - final insertedPollVotes1 = await _preparePollVoteData(pollId1); - final insertedPollVotes2 = await _preparePollVoteData(pollId2); - - // Fetched pollVote list length should match - // the inserted pollVote list length - final pollVotes1 = await pollVoteDao.getPollVotes(pollId1); - final pollVotes2 = await pollVoteDao.getPollVotes(pollId2); - expect(pollVotes1.length, insertedPollVotes1.length); - expect(pollVotes2.length, insertedPollVotes2.length); - - // Deleting all the pollVotes of messageId1 - await pollVoteDao.deletePollVotesByPollIds([pollId1]); - - // Fetched pollVotes length of only pollId1 should be empty - final fetchedPollVotes1 = await pollVoteDao.getPollVotes(pollId1); - final fetchedPollVotes2 = await pollVoteDao.getPollVotes(pollId2); - expect(fetchedPollVotes1, isEmpty); - expect(fetchedPollVotes2, isNotEmpty); - }); - - test('should delete all the pollVotes of both polls', () async { - // Preparing test data - final insertedPollVotes1 = await _preparePollVoteData(pollId1); - final insertedPollVotes2 = await _preparePollVoteData(pollId2); - - // Fetched pollVote list length should match - // the inserted pollVote list length - final pollVotes1 = await pollVoteDao.getPollVotes(pollId1); - final pollVotes2 = await pollVoteDao.getPollVotes(pollId2); - expect(pollVotes1.length, insertedPollVotes1.length); - expect(pollVotes2.length, insertedPollVotes2.length); - - // Deleting all the pollVotes of messageId1 - await pollVoteDao.deletePollVotesByPollIds([pollId1, pollId2]); - - // Fetched pollVotes length of only pollId1 should be empty - final fetchedPollVotes1 = await pollVoteDao.getPollVotes(pollId1); - final fetchedPollVotes2 = await pollVoteDao.getPollVotes(pollId2); - expect(fetchedPollVotes1, isEmpty); - expect(fetchedPollVotes2, isEmpty); - }); - }); - - tearDown(() async { - await database.disconnect(); - }); -} diff --git a/packages/stream_chat_persistence/test/src/dao/reaction_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/reaction_dao_test.dart deleted file mode 100644 index 7fa3569e4e..0000000000 --- a/packages/stream_chat_persistence/test/src/dao/reaction_dao_test.dart +++ /dev/null @@ -1,193 +0,0 @@ -import 'dart:math' as math; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/dao/reaction_dao.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -import '../../stream_chat_persistence_client_test.dart'; - -void main() { - late ReactionDao reactionDao; - late DriftChatDatabase database; - - setUp(() { - database = testDatabaseProvider('testUserId'); - reactionDao = database.reactionDao; - }); - - Future> _prepareReactionData( - String messageId, { - String? userId, - int count = 3, - }) async { - const cid = 'test:Cid'; - final channels = [ChannelModel(cid: cid)]; - final users = List.generate(count, (index) => User(id: 'testUserId$index')); - final message = Message( - id: messageId, - type: 'testType', - user: users.first, - createdAt: DateTime.now(), - shadowed: math.Random().nextBool(), - showInChannel: math.Random().nextBool(), - replyCount: 3, - updatedAt: DateTime.now(), - extraData: const {'extra_test_field': 'extraTestData'}, - text: 'Dummy text', - pinned: math.Random().nextBool(), - pinnedAt: DateTime.now(), - pinnedBy: users.first, - ); - final reactions = List.generate( - count, - (index) => Reaction( - type: 'testType$index', - createdAt: DateTime.now(), - userId: userId ?? users[index].id, - messageId: message.id, - score: count + 3, - extraData: {'extra_test_field': 'extraTestData'}, - ), - ); - - await database.userDao.updateUsers(users); - await database.channelDao.updateChannels(channels); - await database.messageDao.updateMessages(cid, [message]); - await reactionDao.updateReactions(reactions); - - return reactions; - } - - test('getReactions', () async { - const messageId = 'testMessageId'; - - // Should be empty initially - final reactions = await reactionDao.getReactions(messageId); - expect(reactions, isEmpty); - - // Adding sample reactions - final insertedReactions = await _prepareReactionData(messageId); - expect(insertedReactions, isNotEmpty); - - // Fetched reaction length should match inserted reactions length. - // Every reaction messageId should match the provided messageId. - final fetchedReactions = await reactionDao.getReactions(messageId); - expect(fetchedReactions.length, insertedReactions.length); - expect(fetchedReactions.every((it) => it.messageId == messageId), true); - }); - - test('getReactionsByUserId', () async { - const messageId = 'testMessageId'; - const userId = 'testUserId'; - - // Should be empty initially - final reactions = await reactionDao.getReactionsByUserId(messageId, userId); - expect(reactions, isEmpty); - - // Adding sample reactions - final insertedReactions = - await _prepareReactionData(messageId, userId: userId); - expect(insertedReactions, isNotEmpty); - - // Fetched reaction length should match inserted reactions length. - // Every reaction messageId should match the provided messageId. - // Every reaction userId should match the provided userId. - final fetchedReactions = - await reactionDao.getReactionsByUserId(messageId, userId); - expect(fetchedReactions.length, insertedReactions.length); - expect(fetchedReactions.every((it) => it.messageId == messageId), true); - expect(fetchedReactions.every((it) => it.userId == userId), true); - }); - - test('updateReactions', () async { - const messageId = 'testMessageId'; - - // Preparing test data - final reactions = await _prepareReactionData(messageId); - - // Modifying one of the reaction and also adding one new - final copyReaction = reactions.first.copyWith(score: 33); - final newReaction = Reaction( - type: 'testType3', - createdAt: DateTime.now(), - userId: 'testUserId3', - messageId: messageId, - score: 30, - extraData: {'extra_test_field': 'extraTestData'}, - ); - - await reactionDao.updateReactions([copyReaction, newReaction]); - - // Fetched reaction length should be one more than inserted reactions. - // copyReaction `score` modified field should be 33. - // Fetched reactions should contain the newReaction. - final fetchedReactions = await reactionDao.getReactions(messageId); - expect(fetchedReactions.length, reactions.length + 1); - expect( - fetchedReactions - .firstWhere((it) => - it.userId == copyReaction.userId && it.type == copyReaction.type) - .score, - 33, - ); - expect( - fetchedReactions - .where((it) => - it.userId == newReaction.userId && it.type == newReaction.type) - .isNotEmpty, - true, - ); - }); - - group('deleteReactionsByMessageIds', () { - const messageId1 = 'testMessageId1'; - const messageId2 = 'testMessageId2'; - test('should delete all the reactions of first message', () async { - // Preparing test data - final insertedReactions1 = await _prepareReactionData(messageId1); - final insertedReactions2 = await _prepareReactionData(messageId2); - - // Fetched reaction list length should match - // the inserted reactions list length - final reactions1 = await reactionDao.getReactions(messageId1); - final reactions2 = await reactionDao.getReactions(messageId2); - expect(reactions1.length, insertedReactions1.length); - expect(reactions2.length, insertedReactions2.length); - - // Deleting all the reactions of messageId1 - await reactionDao.deleteReactionsByMessageIds([messageId1]); - - // Fetched reactions length of only messageId1 should be empty - final fetchedReactions1 = await reactionDao.getReactions(messageId1); - final fetchedReactions2 = await reactionDao.getReactions(messageId2); - expect(fetchedReactions1, isEmpty); - expect(fetchedReactions2, isNotEmpty); - }); - test('should delete all the reactions of both message', () async { - // Preparing test data - final insertedReactions1 = await _prepareReactionData(messageId1); - final insertedReactions2 = await _prepareReactionData(messageId2); - - // Fetched reaction list length should match - // the inserted reactions list length - final reactions1 = await reactionDao.getReactions(messageId1); - final reactions2 = await reactionDao.getReactions(messageId2); - expect(reactions1.length, insertedReactions1.length); - expect(reactions2.length, insertedReactions2.length); - - // Deleting all the reactions of messageId1 and messageId2 - await reactionDao.deleteReactionsByMessageIds([messageId1, messageId2]); - - // Fetched reactions length of both messages should be empty - final fetchedReactions1 = await reactionDao.getReactions(messageId1); - final fetchedReactions2 = await reactionDao.getReactions(messageId2); - expect(fetchedReactions1, isEmpty); - expect(fetchedReactions2, isEmpty); - }); - }); - - tearDown(() async { - await database.disconnect(); - }); -} diff --git a/packages/stream_chat_persistence/test/src/dao/read_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/read_dao_test.dart deleted file mode 100644 index aae58e83f6..0000000000 --- a/packages/stream_chat_persistence/test/src/dao/read_dao_test.dart +++ /dev/null @@ -1,102 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/dao/dao.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -import '../../stream_chat_persistence_client_test.dart'; -import '../utils/date_matcher.dart'; - -void main() { - late ReadDao readDao; - late DriftChatDatabase database; - - setUp(() { - database = testDatabaseProvider('testUserId'); - readDao = database.readDao; - }); - - Future> _prepareReadData(String cid, {int count = 3}) async { - final channels = [ChannelModel(cid: cid)]; - final users = List.generate(count, (index) => User(id: 'testUserId$index')); - final reads = List.generate( - count, - (index) => Read( - lastRead: DateTime.now(), - user: users[index], - unreadMessages: index + 10, - lastReadMessageId: 'lastMessageId$index', - ), - ); - - await database.userDao.updateUsers(users); - await database.channelDao.updateChannels(channels); - await readDao.updateReads(cid, reads); - return reads; - } - - test('getReadsByCid', () async { - const cid = 'test:Cid'; - - // Should be empty initially - final reads = await readDao.getReadsByCid(cid); - expect(reads, isEmpty); - - // Preparing test data - final insertedReads = await _prepareReadData(cid); - expect(insertedReads, isNotEmpty); - - // Fetched reads should be equal to inserted reads - final fetchedReads = await readDao.getReadsByCid(cid); - expect(fetchedReads.length, insertedReads.length); - for (var i = 0; i < fetchedReads.length; i++) { - final fetchedRead = fetchedReads[i]; - final insertedRead = insertedReads[i]; - expect(fetchedRead.user.id, insertedRead.user.id); - expect(fetchedRead.lastRead, isSameDateAs(insertedRead.lastRead)); - expect(fetchedRead.unreadMessages, insertedRead.unreadMessages); - } - }); - - test('updateReads', () async { - const cid = 'test:Cid'; - - // Preparing test data - final insertedReads = await _prepareReadData(cid); - - // Modifying one of the read and also adding one new - final copyRead = insertedReads.first.copyWith(unreadMessages: 33); - final newUser = User(id: 'testUserId3'); - final newRead = Read( - lastRead: DateTime.now(), - user: newUser, - unreadMessages: 30, - lastReadMessageId: 'lastMessageId3', - ); - await database.userDao.updateUsers([newUser]); - await readDao.updateReads(cid, [copyRead, newRead]); - - // Fetched reads length should be one more than inserted reads. - // copyRead `unreadMessages` modified field should be 33. - // Fetched reads should contain the newRead. - final fetchedReads = await readDao.getReadsByCid(cid); - expect(fetchedReads.length, insertedReads.length + 1); - expect( - fetchedReads - .firstWhere((it) => it.user.id == copyRead.user.id) - .unreadMessages, - 33, - ); - expect( - fetchedReads - .where((it) => - it.user.id == newRead.user.id && - it.unreadMessages == newRead.unreadMessages) - .isNotEmpty, - true, - ); - }); - - tearDown(() async { - await database.disconnect(); - }); -} diff --git a/packages/stream_chat_persistence/test/src/dao/user_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/user_dao_test.dart deleted file mode 100644 index f5a40dbc69..0000000000 --- a/packages/stream_chat_persistence/test/src/dao/user_dao_test.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'dart:math' as math; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/dao/dao.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -import '../../stream_chat_persistence_client_test.dart'; - -void main() { - late UserDao userDao; - late DriftChatDatabase database; - - setUp(() { - database = testDatabaseProvider('testUserId'); - userDao = database.userDao; - }); - - Future> _prepareUserData({int count = 3}) async { - final users = List.generate( - count, - (index) => User( - id: 'testUserId$index', - role: 'testRole', - language: 'hi', - createdAt: DateTime.now(), - updatedAt: DateTime.now(), - lastActive: DateTime.now(), - online: math.Random().nextBool(), - banned: math.Random().nextBool(), - ), - ); - await userDao.updateUsers(users); - return users; - } - - test('updateUsers', () async { - // Preparing test data - final insertedUsers = await _prepareUserData(); - - // Modifying one of the user and also adding one new - final copyUser = insertedUsers.first.copyWith(online: false); - final newUser = User( - id: 'testUserId3', - role: 'testRole', - createdAt: DateTime.now(), - updatedAt: DateTime.now(), - online: math.Random().nextBool(), - banned: math.Random().nextBool(), - ); - await userDao.updateUsers([copyUser, newUser]); - - // Fetched users length should be one more than inserted users. - // copyUser `online` modified field should be `false`. - // Fetched users should contain the newUser. - final fetchedUsers = await userDao.getUsers(); - expect(fetchedUsers.length, insertedUsers.length + 1); - expect(fetchedUsers.firstWhere((it) => it.id == copyUser.id).online, false); - expect(fetchedUsers.contains(newUser), true); - }); - - test('getUsers', () async { - // Should be empty initially - final users = await userDao.getUsers(); - expect(users, isEmpty); - - // Preparing test data - final insertedUsers = await _prepareUserData(); - expect(insertedUsers, isNotEmpty); - - // Fetched user list should match inserted user list length - final fetchedUsers = await userDao.getUsers(); - expect(fetchedUsers.length, insertedUsers.length); - }); - - tearDown(() async { - await database.disconnect(); - }); -} diff --git a/packages/stream_chat_persistence/test/src/db/drift_chat_database_test.dart b/packages/stream_chat_persistence/test/src/db/drift_chat_database_test.dart deleted file mode 100644 index 996c152fb1..0000000000 --- a/packages/stream_chat_persistence/test/src/db/drift_chat_database_test.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:drift/drift.dart' hide isNotNull; -import 'package:drift/isolate.dart'; -import 'package:drift/native.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; - -DatabaseConnection _backgroundConnection() => - DatabaseConnection(NativeDatabase.memory()); - -void main() { - test( - 'default constructor should create a new instance of MoorChatDatabase', - () async { - const userId = 'testUserId'; - final executor = NativeDatabase.memory(); - final database = DriftChatDatabase(userId, executor); - expect(database, isNotNull); - expect(database.userId, userId); - - addTearDown(() async { - await database.disconnect(); - }); - }, - ); - - test( - 'connect constructor should create a new instance of MoorChatDatabase', - () async { - const userId = 'testUserId'; - final isolate = await DriftIsolate.spawn(_backgroundConnection); - final connection = DatabaseConnection.delayed(isolate.connect()); - - final database = DriftChatDatabase(userId, connection); - expect(database, isNotNull); - expect(database.userId, userId); - - addTearDown(() async { - await database.disconnect(); - await isolate.shutdownAll(); - }); - }, - ); -} diff --git a/packages/stream_chat_persistence/test/src/mapper/channel_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/channel_mapper_test.dart deleted file mode 100644 index 88c617bd67..0000000000 --- a/packages/stream_chat_persistence/test/src/mapper/channel_mapper_test.dart +++ /dev/null @@ -1,124 +0,0 @@ -import 'dart:math' as math; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/mapper/channel_mapper.dart'; - -import '../utils/date_matcher.dart'; - -void main() { - group('ChannelEntity', () { - final user = User(id: 'testUserId'); - final entity = ChannelEntity( - id: 'testId', - type: 'testType', - cid: 'testCid', - ownCapabilities: ['testCapability'], - config: {'max_message_length': 33}, - frozen: math.Random().nextBool(), - lastMessageAt: DateTime.now(), - createdAt: DateTime.now(), - updatedAt: DateTime.now(), - deletedAt: DateTime.now(), - memberCount: 33, - createdById: user.id, - extraData: {'test_extra_data': 'testData'}, - ); - - test('toChannelModel should map entity into ChannelModel', () { - final channelModel = entity.toChannelModel(createdBy: user); - expect(channelModel, isA()); - expect(channelModel.id, entity.id); - expect(channelModel.ownCapabilities, entity.ownCapabilities); - expect(channelModel.config.toJson()['max_message_length'], 33); - expect(channelModel.frozen, entity.frozen); - expect(channelModel.createdAt, isSameDateAs(entity.createdAt)); - expect(channelModel.updatedAt, isSameDateAs(entity.updatedAt)); - expect(channelModel.memberCount, entity.memberCount); - expect(channelModel.cid, entity.cid); - expect(channelModel.lastMessageAt, isSameDateAs(entity.lastMessageAt)); - expect(channelModel.deletedAt, isSameDateAs(entity.deletedAt)); - expect(channelModel.extraData, entity.extraData); - expect(channelModel.createdBy!.id, entity.createdById); - }); - - test('toChannelState should map entity into ChannelState ', () { - final members = List.generate(3, (index) => Member()); - final reads = List.generate( - 3, - (index) => Read( - user: User(id: 'testUserId$index'), - lastRead: DateTime.now(), - lastReadMessageId: 'lastMessageId$index', - ), - ); - final messages = List.generate(3, (index) => Message()); - - final channelState = entity.toChannelState( - createdBy: user, - members: members, - reads: reads, - messages: messages, - pinnedMessages: messages, - ); - - expect(channelState, isA()); - expect(channelState.members?.length, members.length); - expect(channelState.read?.length, reads.length); - expect(channelState.messages?.length, messages.length); - expect(channelState.pinnedMessages?.length, messages.length); - - final channelModel = channelState.channel!; - expect(channelModel.id, entity.id); - expect(channelModel.ownCapabilities, entity.ownCapabilities); - expect(channelModel.config.toJson()['max_message_length'], 33); - expect(channelModel.frozen, entity.frozen); - expect(channelModel.createdAt, isSameDateAs(entity.createdAt)); - expect(channelModel.updatedAt, isSameDateAs(entity.updatedAt)); - expect(channelModel.memberCount, entity.memberCount); - expect(channelModel.cid, entity.cid); - expect(channelModel.lastMessageAt, isSameDateAs(entity.lastMessageAt)); - expect(channelModel.deletedAt, isSameDateAs(entity.deletedAt)); - expect(channelModel.extraData, entity.extraData); - expect(channelModel.createdBy!.id, entity.createdById); - }); - }); - - test('toEntity should map model into ChannelEntity', () { - final createdBy = User(id: 'testUserId'); - final model = ChannelModel( - id: 'testId', - type: 'testType', - cid: 'testCid', - ownCapabilities: ['testCapability'], - config: ChannelConfig(maxMessageLength: 33), - frozen: math.Random().nextBool(), - lastMessageAt: DateTime.now(), - createdAt: DateTime.now(), - updatedAt: DateTime.now(), - deletedAt: DateTime.now(), - memberCount: 33, - createdBy: createdBy, - extraData: {'test_extra_data': 'testData'}, - ); - - final channelEntity = model.toEntity(); - expect(channelEntity, isA()); - expect(channelEntity.id, model.id); - expect(channelEntity.ownCapabilities, model.ownCapabilities); - expect( - channelEntity.config['max_message_length'], - model.config.maxMessageLength, - ); - expect(channelEntity.frozen, model.frozen); - expect(channelEntity.createdAt, isSameDateAs(model.createdAt)); - expect(channelEntity.updatedAt, isSameDateAs(model.updatedAt)); - expect(channelEntity.memberCount, model.memberCount); - expect(channelEntity.cid, model.cid); - expect(channelEntity.lastMessageAt, isSameDateAs(model.lastMessageAt)); - expect(channelEntity.deletedAt, isSameDateAs(model.deletedAt)); - expect(channelEntity.extraData, model.extraData); - expect(channelEntity.createdById, model.createdBy!.id); - }); -} diff --git a/packages/stream_chat_persistence/test/src/mapper/event_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/event_mapper_test.dart deleted file mode 100644 index 6acc4e05ea..0000000000 --- a/packages/stream_chat_persistence/test/src/mapper/event_mapper_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/mapper/event_mapper.dart'; - -import '../utils/date_matcher.dart'; - -void main() { - test('toEvent should map entity into Event', () { - const type = 'dummy.type'; - final now = DateTime.now(); - final ownUser = OwnUser(id: 'testUserId'); - final entity = ConnectionEventEntity( - id: 3, - type: type, - ownUser: ownUser.toJson(), - totalUnreadCount: 33, - unreadChannels: 33, - lastSyncAt: now, - lastEventAt: now, - ); - final event = entity.toEvent(); - expect(event, isA()); - expect(event.type, type); - expect(event.createdAt.toUtc(), isSameDateAs(now.toUtc())); - expect(event.me!.id, ownUser.id); - expect(event.totalUnreadCount, entity.totalUnreadCount); - expect(event.unreadChannels, entity.unreadChannels); - }); -} diff --git a/packages/stream_chat_persistence/test/src/mapper/member_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/member_mapper_test.dart deleted file mode 100644 index 001872aa1b..0000000000 --- a/packages/stream_chat_persistence/test/src/mapper/member_mapper_test.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'dart:math' as math; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/mapper/member_mapper.dart'; - -import '../utils/date_matcher.dart'; - -void main() { - test('toMember should map entity into Member', () { - final user = User(id: 'testUserId'); - final entity = MemberEntity( - userId: user.id, - channelCid: 'testCid', - createdAt: DateTime.now(), - updatedAt: DateTime.now(), - channelRole: 'testRole', - inviteAcceptedAt: DateTime.now(), - inviteRejectedAt: DateTime.now(), - invited: math.Random().nextBool(), - banned: math.Random().nextBool(), - shadowBanned: math.Random().nextBool(), - isModerator: math.Random().nextBool(), - ); - final member = entity.toMember(user: user); - expect(member, isA()); - expect(member.user!.id, entity.userId); - expect(member.createdAt, isSameDateAs(entity.createdAt)); - expect(member.updatedAt, isSameDateAs(entity.updatedAt)); - expect(member.channelRole, entity.channelRole); - expect(member.inviteAcceptedAt, isSameDateAs(entity.inviteAcceptedAt)); - expect(member.inviteRejectedAt, isSameDateAs(entity.inviteRejectedAt)); - expect(member.invited, entity.invited); - expect(member.banned, entity.banned); - expect(member.shadowBanned, entity.shadowBanned); - expect(member.isModerator, entity.isModerator); - }); - - test('toEntity show map member into MemberEntity', () { - const cid = 'testCid'; - final user = User(id: 'testUserId'); - final member = Member( - user: user, - createdAt: DateTime.now(), - updatedAt: DateTime.now(), - channelRole: 'testRole', - inviteAcceptedAt: DateTime.now(), - inviteRejectedAt: DateTime.now(), - invited: math.Random().nextBool(), - banned: math.Random().nextBool(), - shadowBanned: math.Random().nextBool(), - isModerator: math.Random().nextBool(), - ); - final entity = member.toEntity(cid: cid); - expect(entity, isA()); - expect(entity.channelCid, cid); - expect(entity.userId, member.user!.id); - expect(entity.createdAt, isSameDateAs(member.createdAt)); - expect(entity.updatedAt, isSameDateAs(member.updatedAt)); - expect(entity.channelRole, member.channelRole); - expect(entity.inviteAcceptedAt, isSameDateAs(member.inviteAcceptedAt)); - expect(entity.inviteRejectedAt, isSameDateAs(member.inviteRejectedAt)); - expect(entity.invited, member.invited); - expect(entity.banned, member.banned); - expect(entity.shadowBanned, member.shadowBanned); - expect(entity.isModerator, member.isModerator); - }); -} diff --git a/packages/stream_chat_persistence/test/src/mapper/message_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/message_mapper_test.dart deleted file mode 100644 index 76dc763fc6..0000000000 --- a/packages/stream_chat_persistence/test/src/mapper/message_mapper_test.dart +++ /dev/null @@ -1,255 +0,0 @@ -import 'dart:convert'; -import 'dart:math' as math; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/mapper/message_mapper.dart'; - -import '../utils/date_matcher.dart'; - -void main() { - test('toMessage should map the entity into Message', () { - final user = User(id: 'testUserId'); - final quotedMessage = Message(id: 'testQuotedMessageId'); - final reactions = List.generate( - 3, - (index) => Reaction( - messageId: 'testMessageId', - createdAt: DateTime.now(), - type: 'testType$index', - user: user, - score: math.Random().nextInt(50), - ), - ); - final attachments = List.generate( - 3, - (index) => Attachment( - id: 'testAttachmentId', - type: 'testAttachmentType', - assetUrl: 'testAssetUrl', - ), - ); - final poll = Poll( - id: 'testPollId', - name: 'testQuestion', - options: const [ - PollOption( - id: 'testOptionId', - text: 'testOptionText', - ), - ], - ); - final entity = MessageEntity( - id: 'testMessageId', - attachments: attachments.map((it) => jsonEncode(it.toData())).toList(), - channelCid: 'testCid', - type: 'testType', - parentId: 'testParentId', - quotedMessageId: quotedMessage.id, - pollId: poll.id, - command: 'testCommand', - localCreatedAt: DateTime.now(), - remoteCreatedAt: DateTime.now().add(const Duration(seconds: 1)), - shadowed: math.Random().nextBool(), - showInChannel: math.Random().nextBool(), - replyCount: 33, - reactionScores: {for (final r in reactions) r.type: r.score}, - reactionCounts: reactions.fold( - {}, - (prev, curr) => - prev?..update(curr.type, (value) => value + 1, ifAbsent: () => 1), - ), - mentionedUsers: [ - jsonEncode(User(id: 'testuser')), - ], - state: jsonEncode(MessageState.sent), - localUpdatedAt: DateTime.now(), - remoteUpdatedAt: DateTime.now().add(const Duration(seconds: 1)), - messageTextUpdatedAt: DateTime.now().add(const Duration(minutes: 5)), - extraData: {'extra_test_data': 'extraData'}, - userId: user.id, - localDeletedAt: DateTime.now(), - remoteDeletedAt: DateTime.now().add(const Duration(seconds: 1)), - messageText: 'Hello', - pinned: true, - pinExpires: DateTime.now().toUtc(), - pinnedAt: DateTime.now(), - pinnedByUserId: user.id, - i18n: const { - 'en_text': 'Hello', - 'hi_text': 'ą¤Øą¤®ą¤øą„ą¤¤ą„‡', - 'language': 'en', - }, - ); - final message = entity.toMessage( - user: user, - pinnedBy: user, - latestReactions: reactions, - ownReactions: reactions, - quotedMessage: quotedMessage, - poll: poll, - ); - - expect(message, isA()); - expect(message.id, entity.id); - expect(message.type, entity.type); - expect(message.parentId, entity.parentId); - expect(message.quotedMessageId, entity.quotedMessageId); - expect(message.pollId, entity.pollId); - expect(message.command, entity.command); - expect(message.localCreatedAt, isSameDateAs(entity.localCreatedAt)); - expect(message.remoteCreatedAt, isSameDateAs(entity.remoteCreatedAt)); - expect(message.shadowed, entity.shadowed); - expect(message.showInChannel, entity.showInChannel); - for (var i = 0; i < message.mentionedUsers.length; i++) { - final entityMentionedUser = - User.fromJson(jsonDecode(entity.mentionedUsers[i])); - expect(message.mentionedUsers[i].id, entityMentionedUser.id); - } - expect(message.replyCount, entity.replyCount); - expect(message.reactionScores, entity.reactionScores); - expect(message.reactionCounts, entity.reactionCounts); - expect(message.state, MessageState.fromJson(jsonDecode(entity.state))); - expect(message.localUpdatedAt, isSameDateAs(entity.localUpdatedAt)); - expect(message.remoteUpdatedAt, isSameDateAs(entity.remoteUpdatedAt)); - expect( - message.messageTextUpdatedAt, - isSameDateAs(entity.messageTextUpdatedAt), - ); - expect(message.extraData, entity.extraData); - expect(message.user!.id, entity.userId); - expect(message.localDeletedAt, isSameDateAs(entity.localDeletedAt)); - expect(message.remoteDeletedAt, isSameDateAs(entity.remoteDeletedAt)); - expect(message.text, entity.messageText); - expect(message.pinned, entity.pinned); - expect(message.pinExpires, isSameDateAs(entity.pinExpires)); - expect(message.pinnedAt, isSameDateAs(entity.pinnedAt)); - expect(message.pinnedBy!.id, entity.pinnedByUserId); - expect(message.reactionCounts, entity.reactionCounts); - expect(message.reactionScores, entity.reactionScores); - expect(message.i18n, entity.i18n); - for (var i = 0; i < message.attachments.length; i++) { - final messageAttachment = message.attachments[i]; - final entityAttachmentData = jsonDecode(entity.attachments[i]); - final entityAttachment = Attachment.fromData(entityAttachmentData); - expect(messageAttachment.id, entityAttachment.id); - expect(messageAttachment.type, entityAttachment.type); - expect(messageAttachment.assetUrl, entityAttachment.assetUrl); - } - }); - - test('toEntity should map message into MessageEntity', () { - const cid = 'testCid'; - final user = User(id: 'testUserId'); - final quotedMessage = Message(id: 'testQuotedMessageId'); - final reactions = List.generate( - 3, - (index) => Reaction( - messageId: 'testMessageId', - createdAt: DateTime.now(), - type: 'testType$index', - user: user, - score: math.Random().nextInt(50), - ), - ); - final attachments = List.generate( - 3, - (index) => Attachment( - id: 'testAttachmentId', - type: 'testAttachmentType', - assetUrl: 'testAssetUrl', - ), - ); - final poll = Poll( - id: 'testPollId', - name: 'testQuestion', - options: const [ - PollOption( - id: 'testOptionId', - text: 'testOptionText', - ), - ], - ); - final message = Message( - id: 'testMessageId', - attachments: attachments, - type: 'testType', - parentId: 'testParentId', - quotedMessageId: quotedMessage.id, - pollId: poll.id, - command: 'testCommand', - localCreatedAt: DateTime.now(), - createdAt: DateTime.now().add(const Duration(seconds: 1)), - shadowed: math.Random().nextBool(), - showInChannel: math.Random().nextBool(), - replyCount: 33, - mentionedUsers: [ - User(id: 'testuser'), - ], - reactionScores: {for (final r in reactions) r.type: r.score}, - reactionCounts: reactions.fold( - {}, - (prev, curr) => - prev?..update(curr.type, (value) => value + 1, ifAbsent: () => 1), - ), - localUpdatedAt: DateTime.now(), - updatedAt: DateTime.now().add(const Duration(seconds: 1)), - messageTextUpdatedAt: DateTime.now().add(const Duration(minutes: 5)), - extraData: const {'extra_test_data': 'extraData'}, - user: user, - localDeletedAt: DateTime.now(), - deletedAt: DateTime.now().add(const Duration(seconds: 1)), - text: 'Hello', - pinned: true, - pinExpires: DateTime.now(), - pinnedAt: DateTime.now(), - pinnedBy: user, - i18n: const { - 'en_text': 'Hello', - 'hi_text': 'ą¤Øą¤®ą¤øą„ą¤¤ą„‡', - 'language': 'en', - }, - ); - final entity = message.toEntity(cid: cid); - expect(entity, isA()); - expect(entity.id, message.id); - expect(entity.type, message.type); - expect(entity.parentId, message.parentId); - expect(entity.quotedMessageId, message.quotedMessageId); - expect(entity.pollId, message.pollId); - expect(entity.command, message.command); - expect(entity.localCreatedAt, isSameDateAs(message.localCreatedAt)); - expect(entity.remoteCreatedAt, isSameDateAs(message.remoteCreatedAt)); - expect(entity.shadowed, message.shadowed); - expect(entity.showInChannel, message.showInChannel); - expect(entity.replyCount, message.replyCount); - expect( - entity.mentionedUsers, message.mentionedUsers.map(jsonEncode).toList()); - expect(entity.reactionScores, message.reactionScores); - expect(entity.reactionCounts, message.reactionCounts); - expect(entity.state, jsonEncode(message.state)); - expect(entity.localUpdatedAt, isSameDateAs(message.localUpdatedAt)); - expect(entity.remoteUpdatedAt, isSameDateAs(message.remoteUpdatedAt)); - expect( - entity.messageTextUpdatedAt, - isSameDateAs(message.messageTextUpdatedAt), - ); - expect(entity.extraData, message.extraData); - expect(entity.userId, message.user!.id); - expect(entity.localDeletedAt, isSameDateAs(message.localDeletedAt)); - expect(entity.remoteDeletedAt, isSameDateAs(message.remoteDeletedAt)); - expect(entity.messageText, message.text); - expect(entity.pinned, message.pinned); - expect(entity.pinExpires, isSameDateAs(message.pinExpires)); - expect(entity.pinnedAt, isSameDateAs(message.pinnedAt)); - expect(entity.pinnedByUserId, message.pinnedBy!.id); - expect(entity.reactionCounts, message.reactionCounts); - expect(entity.reactionScores, message.reactionScores); - expect( - entity.attachments, - message.attachments.map((it) => jsonEncode(it.toData())).toList(), - ); - expect(entity.i18n, message.i18n); - }); -} diff --git a/packages/stream_chat_persistence/test/src/mapper/pinned_message_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/pinned_message_mapper_test.dart deleted file mode 100644 index 9d54faea59..0000000000 --- a/packages/stream_chat_persistence/test/src/mapper/pinned_message_mapper_test.dart +++ /dev/null @@ -1,255 +0,0 @@ -import 'dart:convert'; -import 'dart:math' as math; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/mapper/pinned_message_mapper.dart'; - -import '../utils/date_matcher.dart'; - -void main() { - test('toMessage should map the entity into Message', () { - final user = User(id: 'testUserId'); - final quotedMessage = Message(id: 'testQuotedMessageId'); - final reactions = List.generate( - 3, - (index) => Reaction( - messageId: 'testMessageId', - createdAt: DateTime.now(), - type: 'testType$index', - user: user, - score: math.Random().nextInt(50), - ), - ); - final attachments = List.generate( - 3, - (index) => Attachment( - id: 'testAttachmentId', - type: 'testAttachmentType', - assetUrl: 'testAssetUrl', - ), - ); - final poll = Poll( - id: 'testPollId', - name: 'testQuestion', - options: const [ - PollOption( - id: 'testOptionId', - text: 'testOptionText', - ), - ], - ); - final entity = PinnedMessageEntity( - id: 'testMessageId', - attachments: attachments.map((it) => jsonEncode(it.toData())).toList(), - channelCid: 'testCid', - type: 'testType', - parentId: 'testParentId', - quotedMessageId: quotedMessage.id, - pollId: poll.id, - command: 'testCommand', - localCreatedAt: DateTime.now(), - remoteCreatedAt: DateTime.now().add(const Duration(seconds: 1)), - shadowed: math.Random().nextBool(), - showInChannel: math.Random().nextBool(), - replyCount: 33, - reactionScores: {for (final r in reactions) r.type: r.score}, - reactionCounts: reactions.fold( - {}, - (prev, curr) => - prev?..update(curr.type, (value) => value + 1, ifAbsent: () => 1), - ), - mentionedUsers: [ - jsonEncode(User(id: 'testuser')), - ], - state: jsonEncode(MessageState.sent), - localUpdatedAt: DateTime.now(), - remoteUpdatedAt: DateTime.now().add(const Duration(seconds: 1)), - messageTextUpdatedAt: DateTime.now().add(const Duration(minutes: 5)), - extraData: {'extra_test_data': 'extraData'}, - userId: user.id, - localDeletedAt: DateTime.now(), - remoteDeletedAt: DateTime.now().add(const Duration(seconds: 1)), - messageText: 'Hello', - pinned: true, - pinExpires: DateTime.now().toUtc(), - pinnedAt: DateTime.now(), - pinnedByUserId: user.id, - i18n: const { - 'en_text': 'Hello', - 'hi_text': 'ą¤Øą¤®ą¤øą„ą¤¤ą„‡', - 'language': 'en', - }, - ); - final message = entity.toMessage( - user: user, - pinnedBy: user, - latestReactions: reactions, - ownReactions: reactions, - quotedMessage: quotedMessage, - poll: poll, - ); - - expect(message, isA()); - expect(message.id, entity.id); - expect(message.type, entity.type); - expect(message.parentId, entity.parentId); - expect(message.quotedMessageId, entity.quotedMessageId); - expect(message.pollId, entity.pollId); - expect(message.command, entity.command); - expect(message.localCreatedAt, isSameDateAs(entity.localCreatedAt)); - expect(message.remoteCreatedAt, isSameDateAs(entity.remoteCreatedAt)); - expect(message.shadowed, entity.shadowed); - expect(message.showInChannel, entity.showInChannel); - for (var i = 0; i < message.mentionedUsers.length; i++) { - final entityMentionedUser = - User.fromJson(jsonDecode(entity.mentionedUsers[i])); - expect(message.mentionedUsers[i].id, entityMentionedUser.id); - } - expect(message.replyCount, entity.replyCount); - expect(message.reactionScores, entity.reactionScores); - expect(message.reactionCounts, entity.reactionCounts); - expect(message.state, MessageState.fromJson(jsonDecode(entity.state))); - expect(message.localUpdatedAt, isSameDateAs(entity.localUpdatedAt)); - expect(message.remoteUpdatedAt, isSameDateAs(entity.remoteUpdatedAt)); - expect( - message.messageTextUpdatedAt, - isSameDateAs(entity.messageTextUpdatedAt), - ); - expect(message.extraData, entity.extraData); - expect(message.user!.id, entity.userId); - expect(message.localDeletedAt, isSameDateAs(entity.localDeletedAt)); - expect(message.remoteDeletedAt, isSameDateAs(entity.remoteDeletedAt)); - expect(message.text, entity.messageText); - expect(message.pinned, entity.pinned); - expect(message.pinExpires, isSameDateAs(entity.pinExpires)); - expect(message.pinnedAt, isSameDateAs(entity.pinnedAt)); - expect(message.pinnedBy!.id, entity.pinnedByUserId); - expect(message.reactionCounts, entity.reactionCounts); - expect(message.reactionScores, entity.reactionScores); - expect(message.i18n, entity.i18n); - for (var i = 0; i < message.attachments.length; i++) { - final messageAttachment = message.attachments[i]; - final entityAttachmentData = jsonDecode(entity.attachments[i]); - final entityAttachment = Attachment.fromData(entityAttachmentData); - expect(messageAttachment.id, entityAttachment.id); - expect(messageAttachment.type, entityAttachment.type); - expect(messageAttachment.assetUrl, entityAttachment.assetUrl); - } - }); - - test('toEntity should map message into MessageEntity', () { - const cid = 'testCid'; - final user = User(id: 'testUserId'); - final quotedMessage = Message(id: 'testQuotedMessageId'); - final reactions = List.generate( - 3, - (index) => Reaction( - messageId: 'testMessageId', - createdAt: DateTime.now(), - type: 'testType$index', - user: user, - score: math.Random().nextInt(50), - ), - ); - final attachments = List.generate( - 3, - (index) => Attachment( - id: 'testAttachmentId', - type: 'testAttachmentType', - assetUrl: 'testAssetUrl', - ), - ); - final poll = Poll( - id: 'testPollId', - name: 'testQuestion', - options: const [ - PollOption( - id: 'testOptionId', - text: 'testOptionText', - ), - ], - ); - final message = Message( - id: 'testMessageId', - attachments: attachments, - type: 'testType', - parentId: 'testParentId', - quotedMessageId: quotedMessage.id, - pollId: poll.id, - command: 'testCommand', - localCreatedAt: DateTime.now(), - createdAt: DateTime.now().add(const Duration(seconds: 1)), - shadowed: math.Random().nextBool(), - showInChannel: math.Random().nextBool(), - replyCount: 33, - mentionedUsers: [ - User(id: 'testuser'), - ], - reactionScores: {for (final r in reactions) r.type: r.score}, - reactionCounts: reactions.fold( - {}, - (prev, curr) => - prev?..update(curr.type, (value) => value + 1, ifAbsent: () => 1), - ), - localUpdatedAt: DateTime.now(), - updatedAt: DateTime.now().add(const Duration(seconds: 1)), - messageTextUpdatedAt: DateTime.now().add(const Duration(minutes: 5)), - extraData: const {'extra_test_data': 'extraData'}, - user: user, - localDeletedAt: DateTime.now(), - deletedAt: DateTime.now().add(const Duration(seconds: 1)), - text: 'Hello', - pinned: true, - pinExpires: DateTime.now(), - pinnedAt: DateTime.now(), - pinnedBy: user, - i18n: const { - 'en_text': 'Hello', - 'hi_text': 'ą¤Øą¤®ą¤øą„ą¤¤ą„‡', - 'language': 'en', - }, - ); - final entity = message.toPinnedEntity(cid: cid); - expect(entity, isA()); - expect(entity.id, message.id); - expect(entity.type, message.type); - expect(entity.parentId, message.parentId); - expect(entity.quotedMessageId, message.quotedMessageId); - expect(entity.pollId, message.pollId); - expect(entity.command, message.command); - expect(entity.localCreatedAt, isSameDateAs(message.localCreatedAt)); - expect(entity.remoteCreatedAt, isSameDateAs(message.remoteCreatedAt)); - expect(entity.shadowed, message.shadowed); - expect(entity.showInChannel, message.showInChannel); - expect(entity.replyCount, message.replyCount); - expect( - entity.mentionedUsers, message.mentionedUsers.map(jsonEncode).toList()); - expect(entity.reactionScores, message.reactionScores); - expect(entity.reactionCounts, message.reactionCounts); - expect(entity.state, jsonEncode(message.state)); - expect(entity.localUpdatedAt, isSameDateAs(message.localUpdatedAt)); - expect(entity.remoteUpdatedAt, isSameDateAs(message.remoteUpdatedAt)); - expect( - message.messageTextUpdatedAt, - isSameDateAs(entity.messageTextUpdatedAt), - ); - expect(entity.extraData, message.extraData); - expect(entity.userId, message.user!.id); - expect(entity.localDeletedAt, isSameDateAs(message.localDeletedAt)); - expect(entity.remoteDeletedAt, isSameDateAs(message.remoteDeletedAt)); - expect(entity.messageText, message.text); - expect(entity.pinned, message.pinned); - expect(entity.pinExpires, isSameDateAs(message.pinExpires)); - expect(entity.pinnedAt, isSameDateAs(message.pinnedAt)); - expect(entity.pinnedByUserId, message.pinnedBy!.id); - expect(entity.reactionCounts, message.reactionCounts); - expect(entity.reactionScores, message.reactionScores); - expect( - entity.attachments, - message.attachments.map((it) => jsonEncode(it.toData())).toList(), - ); - expect(entity.i18n, message.i18n); - }); -} diff --git a/packages/stream_chat_persistence/test/src/mapper/pinned_message_reaction_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/pinned_message_reaction_mapper_test.dart deleted file mode 100644 index 2753f4f4ce..0000000000 --- a/packages/stream_chat_persistence/test/src/mapper/pinned_message_reaction_mapper_test.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/mapper/pinned_message_reaction_mapper.dart'; - -import '../utils/date_matcher.dart'; - -void main() { - test('toReaction should map the entity into Reaction', () { - final user = User(id: 'testUserId'); - final message = Message(id: 'testMessageId'); - final entity = PinnedMessageReactionEntity( - userId: user.id, - messageId: message.id, - type: 'haha', - score: 33, - createdAt: DateTime.now(), - extraData: {'extra_test_data': 'extraData'}, - ); - - final reaction = entity.toReaction(user: user); - expect(reaction, isA()); - expect(reaction.userId, entity.userId); - expect(reaction.messageId, entity.messageId); - expect(reaction.type, entity.type); - expect(reaction.score, entity.score); - expect(reaction.createdAt, isSameDateAs(entity.createdAt)); - expect(reaction.extraData, entity.extraData); - }); - - test('toEntity should map reaction into PinnedMessageReactionEntity', () { - final user = User(id: 'testUserId'); - final message = Message(id: 'testMessageId'); - final reaction = Reaction( - userId: user.id, - messageId: message.id, - type: 'haha', - score: 33, - createdAt: DateTime.now(), - extraData: {'extra_test_data': 'extraData'}, - ); - - final entity = reaction.toPinnedEntity(); - expect(entity, isA()); - expect(entity.userId, reaction.userId); - expect(entity.messageId, reaction.messageId); - expect(entity.type, reaction.type); - expect(entity.score, reaction.score); - expect(entity.createdAt, isSameDateAs(reaction.createdAt)); - expect(entity.extraData, reaction.extraData); - }); -} diff --git a/packages/stream_chat_persistence/test/src/mapper/poll_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/poll_mapper_test.dart deleted file mode 100644 index a4f4ef64d6..0000000000 --- a/packages/stream_chat_persistence/test/src/mapper/poll_mapper_test.dart +++ /dev/null @@ -1,193 +0,0 @@ -import 'dart:convert'; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/mapper/poll_mapper.dart'; - -import '../utils/date_matcher.dart'; - -void main() { - test('toPoll should map entity into Poll', () { - final currentUser = User(id: 'curr-user', name: 'Current User'); - final latestVotesByOption = { - 'option-1': [ - for (var i = 0; i < 5; i++) - PollVote( - userId: 'user-$i', - user: User(id: 'user-$i', name: 'User $i'), - optionId: 'option-1', - createdAt: DateTime.now(), - ), - ], - 'option-2': [ - for (var i = 0; i < 2; i++) - PollVote( - userId: 'user-$i', - user: User(id: 'user-$i', name: 'User $i'), - optionId: 'option-2', - createdAt: DateTime.now(), - ), - ], - 'option-3': [ - PollVote( - user: currentUser, - userId: currentUser.id, - optionId: 'option-3', - createdAt: DateTime.now(), - ), - ], - }; - final voteCountsByOption = latestVotesByOption.map( - (key, value) => MapEntry(key, value.length), - ); - final latestAnswers = [ - PollVote( - user: currentUser, - userId: currentUser.id, - answerText: 'I also like yellow', - createdAt: DateTime.now(), - ), - ]; - final entity = PollEntity( - id: 'poll-1', - name: 'testQuestion', - createdById: currentUser.id, - votingVisibility: VotingVisibility.public, - allowUserSuggestedOptions: true, - options: const [ - PollOption(id: 'option-1', text: 'Red'), - PollOption(id: 'option-2', text: 'Blue'), - PollOption(id: 'option-3', text: 'Green'), - ].map(jsonEncode).toList(), - voteCount: voteCountsByOption.values.reduce((a, b) => a + b), - voteCountsByOption: voteCountsByOption, - createdAt: DateTime.now(), - updatedAt: DateTime.now(), - enforceUniqueVote: false, - isClosed: false, - allowAnswers: true, - answersCount: latestAnswers.length, - extraData: {'test_extra_data': 'extraData'}, - ); - final poll = entity.toPoll( - createdBy: currentUser, - latestAnswers: latestAnswers, - latestVotesByOption: latestVotesByOption, - ownVotesAndAnswers: [ - ...latestAnswers, - ...latestVotesByOption.values.expand((it) => it), - ].where((it) => it.userId == currentUser.id).toList(), - ); - - expect(poll, isA()); - expect(poll.id, entity.id); - expect(poll.name, entity.name); - expect(poll.createdById, entity.createdById); - expect(poll.votingVisibility, entity.votingVisibility); - expect(poll.enforceUniqueVote, entity.enforceUniqueVote); - expect(poll.maxVotesAllowed, entity.maxVotesAllowed); - expect(poll.allowUserSuggestedOptions, entity.allowUserSuggestedOptions); - for (var i = 0; i < poll.options.length; i++) { - final pollOption = poll.options[i]; - final entityOptionJson = jsonDecode(entity.options[i]); - final entityOption = PollOption.fromJson(entityOptionJson); - expect(pollOption.id, entityOption.id); - expect(pollOption.text, entityOption.text); - expect(pollOption.extraData, entityOption.extraData); - } - expect(poll.allowAnswers, entity.allowAnswers); - expect(poll.answersCount, entity.answersCount); - expect(poll.isClosed, entity.isClosed); - expect(poll.createdAt, isSameDateAs(entity.createdAt)); - expect(poll.updatedAt, isSameDateAs(entity.updatedAt)); - expect(poll.voteCountsByOption, entity.voteCountsByOption); - expect(poll.voteCount, entity.voteCount); - expect(poll.createdById, entity.createdById); - expect(poll.extraData, entity.extraData); - }); - - test('toEntity should map poll into PollEntity', () { - final currentUser = User(id: 'curr-user', name: 'Current User'); - final latestVotesByOption = { - 'option-1': [ - for (var i = 0; i < 5; i++) - PollVote( - userId: 'user-$i', - user: User(id: 'user-$i', name: 'User $i'), - optionId: 'option-1', - createdAt: DateTime.now(), - ), - ], - 'option-2': [ - for (var i = 0; i < 2; i++) - PollVote( - userId: 'user-$i', - user: User(id: 'user-$i', name: 'User $i'), - optionId: 'option-2', - createdAt: DateTime.now(), - ), - ], - 'option-3': [ - PollVote( - user: currentUser, - userId: currentUser.id, - optionId: 'option-3', - createdAt: DateTime.now(), - ), - ], - }; - final voteCountsByOption = latestVotesByOption.map( - (key, value) => MapEntry(key, value.length), - ); - final latestAnswers = [ - PollVote( - user: currentUser, - userId: currentUser.id, - answerText: 'I also like yellow', - createdAt: DateTime.now(), - ), - ]; - final poll = Poll( - id: 'poll-1', - name: 'testQuestion', - createdById: currentUser.id, - allowUserSuggestedOptions: true, - options: const [ - PollOption(id: 'option-1', text: 'Red'), - PollOption(id: 'option-2', text: 'Blue'), - PollOption(id: 'option-3', text: 'Green'), - ], - voteCount: voteCountsByOption.values.reduce((a, b) => a + b), - voteCountsByOption: voteCountsByOption, - createdAt: DateTime.now(), - updatedAt: DateTime.now(), - enforceUniqueVote: false, - allowAnswers: true, - answersCount: latestAnswers.length, - extraData: const {'test_extra_data': 'extraData'}, - ); - final entity = poll.toEntity(); - expect(entity, isA()); - expect(entity.id, poll.id); - expect(entity.name, poll.name); - expect(entity.createdById, poll.createdById); - expect(entity.votingVisibility, poll.votingVisibility); - expect(entity.enforceUniqueVote, poll.enforceUniqueVote); - expect(entity.maxVotesAllowed, poll.maxVotesAllowed); - expect(entity.allowUserSuggestedOptions, poll.allowUserSuggestedOptions); - expect( - entity.options, - poll.options.map((it) => jsonEncode(it.toJson())).toList(), - ); - expect(entity.allowAnswers, poll.allowAnswers); - expect(entity.answersCount, poll.answersCount); - expect(entity.isClosed, poll.isClosed); - expect(entity.createdAt, isSameDateAs(poll.createdAt)); - expect(entity.updatedAt, isSameDateAs(poll.updatedAt)); - expect(entity.voteCountsByOption, poll.voteCountsByOption); - expect(entity.voteCount, poll.voteCount); - expect(entity.createdById, poll.createdById); - expect(entity.extraData, poll.extraData); - }); -} diff --git a/packages/stream_chat_persistence/test/src/mapper/poll_vote_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/poll_vote_mapper_test.dart deleted file mode 100644 index d6af413e3f..0000000000 --- a/packages/stream_chat_persistence/test/src/mapper/poll_vote_mapper_test.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/mapper/poll_vote_mapper.dart'; - -import '../utils/date_matcher.dart'; - -void main() { - test('toPollVote should map entity into PollVote', () { - final currentUser = User(id: 'curr-user', name: 'Current User'); - final entity = PollVoteEntity( - id: 'poll-vote-1', - pollId: 'poll-1', - optionId: 'option-1', - createdAt: DateTime.now(), - updatedAt: DateTime.now(), - userId: currentUser.id, - ); - final pollVote = entity.toPollVote(user: currentUser); - - expect(pollVote, isA()); - expect(pollVote.id, entity.id); - expect(pollVote.pollId, entity.pollId); - expect(pollVote.optionId, entity.optionId); - expect(pollVote.answerText, entity.answerText); - expect(pollVote.createdAt, isSameDateAs(entity.createdAt)); - expect(pollVote.updatedAt, isSameDateAs(entity.updatedAt)); - expect(pollVote.userId, entity.userId); - }); - - test('toEntity should map pollVote into PollVote', () { - final currentUser = User(id: 'curr-user', name: 'Current User'); - final pollVote = PollVote( - id: 'poll-vote-1', - pollId: 'poll-1', - optionId: 'option-1', - createdAt: DateTime.now(), - updatedAt: DateTime.now(), - userId: currentUser.id, - user: currentUser, - ); - final entity = pollVote.toEntity(); - expect(entity, isA()); - expect(entity.id, pollVote.id); - expect(entity.pollId, pollVote.pollId); - expect(entity.optionId, pollVote.optionId); - expect(entity.answerText, pollVote.answerText); - expect(entity.createdAt, isSameDateAs(pollVote.createdAt)); - expect(entity.updatedAt, isSameDateAs(pollVote.updatedAt)); - expect(entity.userId, pollVote.userId); - }); -} diff --git a/packages/stream_chat_persistence/test/src/mapper/reaction_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/reaction_mapper_test.dart deleted file mode 100644 index 844eb3b86b..0000000000 --- a/packages/stream_chat_persistence/test/src/mapper/reaction_mapper_test.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/mapper/reaction_mapper.dart'; - -import '../utils/date_matcher.dart'; - -void main() { - test('toReaction should map the entity into Reaction', () { - final user = User(id: 'testUserId'); - final message = Message(id: 'testMessageId'); - final entity = ReactionEntity( - userId: user.id, - messageId: message.id, - type: 'haha', - score: 33, - createdAt: DateTime.now(), - extraData: {'extra_test_data': 'extraData'}, - ); - - final reaction = entity.toReaction(user: user); - expect(reaction, isA()); - expect(reaction.userId, entity.userId); - expect(reaction.messageId, entity.messageId); - expect(reaction.type, entity.type); - expect(reaction.score, entity.score); - expect(reaction.createdAt, isSameDateAs(entity.createdAt)); - expect(reaction.extraData, entity.extraData); - }); - - test('toEntity should map reaction into ReactionEntity', () { - final user = User(id: 'testUserId'); - final message = Message(id: 'testMessageId'); - final reaction = Reaction( - userId: user.id, - messageId: message.id, - type: 'haha', - score: 33, - createdAt: DateTime.now(), - extraData: {'extra_test_data': 'extraData'}, - ); - - final entity = reaction.toEntity(); - expect(entity, isA()); - expect(entity.userId, reaction.userId); - expect(entity.messageId, reaction.messageId); - expect(entity.type, reaction.type); - expect(entity.score, reaction.score); - expect(entity.createdAt, isSameDateAs(reaction.createdAt)); - expect(entity.extraData, reaction.extraData); - }); -} diff --git a/packages/stream_chat_persistence/test/src/mapper/read_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/read_mapper_test.dart deleted file mode 100644 index fef6328491..0000000000 --- a/packages/stream_chat_persistence/test/src/mapper/read_mapper_test.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/mapper/read_mapper.dart'; - -import '../utils/date_matcher.dart'; - -void main() { - test('toRead should map entity into Read', () { - const cid = 'testCid'; - const lastMessageId = 'lastMessageId'; - final user = User(id: 'testUserId'); - final entity = ReadEntity( - lastRead: DateTime.now(), - userId: user.id, - channelCid: cid, - unreadMessages: 33, - lastReadMessageId: lastMessageId, - ); - - final read = entity.toRead(user: user); - expect(read, isA()); - expect(read.lastRead, isSameDateAs(entity.lastRead)); - expect(read.user.id, entity.userId); - expect(read.unreadMessages, entity.unreadMessages); - expect(read.lastReadMessageId, lastMessageId); - }); - - test('toEntity should map read into ReadEntity', () { - const cid = 'testCid'; - const lastMessageId = 'lastMessageId'; - final user = User(id: 'testUserId'); - final read = Read( - lastRead: DateTime.now(), - user: user, - unreadMessages: 33, - lastReadMessageId: lastMessageId, - ); - - final entity = read.toEntity(cid: cid); - expect(entity, isA()); - expect(entity.lastRead, isSameDateAs(read.lastRead)); - expect(entity.userId, read.user.id); - expect(entity.unreadMessages, read.unreadMessages); - expect(entity.lastReadMessageId, lastMessageId); - }); -} diff --git a/packages/stream_chat_persistence/test/src/mapper/user_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/user_mapper_test.dart deleted file mode 100644 index b41e9d5978..0000000000 --- a/packages/stream_chat_persistence/test/src/mapper/user_mapper_test.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'dart:math' as math; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/mapper/user_mapper.dart'; - -import '../utils/date_matcher.dart'; - -void main() { - test('toUser should map entity into User', () { - final entity = UserEntity( - id: 'testUserId', - role: 'testType', - language: 'hi', - createdAt: DateTime.now(), - updatedAt: DateTime.now(), - lastActive: DateTime.now(), - online: math.Random().nextBool(), - banned: math.Random().nextBool(), - extraData: {'test_extra_data': 'extraData'}, - ); - final user = entity.toUser(); - expect(user, isA()); - expect(user.id, entity.id); - expect(user.role, entity.role); - expect(user.language, entity.language); - expect(user.createdAt, isSameDateAs(entity.createdAt)); - expect(user.updatedAt, isSameDateAs(entity.updatedAt)); - expect(user.lastActive, isSameDateAs(entity.lastActive)); - expect(user.online, entity.online); - expect(user.banned, entity.banned); - expect(user.extraData, entity.extraData); - }); - - test('toEntity should map user into UserEntity', () { - final user = User( - id: 'testUserId', - role: 'testType', - language: 'hi', - createdAt: DateTime.now(), - updatedAt: DateTime.now(), - lastActive: DateTime.now(), - online: math.Random().nextBool(), - banned: math.Random().nextBool(), - extraData: const {'test_extra_data': 'extraData'}, - ); - final entity = user.toEntity(); - expect(entity, isA()); - expect(entity.id, user.id); - expect(entity.role, user.role); - expect(entity.language, user.language); - expect(entity.createdAt, isSameDateAs(user.createdAt)); - expect(entity.updatedAt, isSameDateAs(user.updatedAt)); - expect(entity.lastActive, isSameDateAs(user.lastActive)); - expect(entity.online, user.online); - expect(entity.banned, user.banned); - expect(entity.extraData, user.extraData); - }); -} diff --git a/packages/stream_chat_persistence/test/src/utils/date_matcher.dart b/packages/stream_chat_persistence/test/src/utils/date_matcher.dart deleted file mode 100644 index d19d25f2a6..0000000000 --- a/packages/stream_chat_persistence/test/src/utils/date_matcher.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; - -Matcher isSameDateAs(DateTime? targetDate) => - _IsSameDateAs(targetDate: targetDate); - -class _IsSameDateAs extends Matcher { - const _IsSameDateAs({required this.targetDate}); - - final DateTime? targetDate; - - @override - bool matches(covariant DateTime date, Map matchState) { - return date.year == targetDate?.year && - date.month == targetDate?.month && - date.day == targetDate?.day && - date.hour == targetDate?.hour && - date.minute == targetDate?.minute && - date.second == targetDate?.second; - } - - @override - Description describe(Description description) => - description.add('is same date as $targetDate'); -} diff --git a/packages/stream_chat_persistence/test/stream_chat_persistence_client_test.dart b/packages/stream_chat_persistence/test/stream_chat_persistence_client_test.dart deleted file mode 100644 index efb5608c77..0000000000 --- a/packages/stream_chat_persistence/test/stream_chat_persistence_client_test.dart +++ /dev/null @@ -1,636 +0,0 @@ -import 'package:drift/native.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_persistence/src/db/drift_chat_database.dart'; -import 'package:stream_chat_persistence/src/stream_chat_persistence_client.dart'; - -import 'mock_chat_database.dart'; -import 'src/utils/date_matcher.dart'; - -DriftChatDatabase testDatabaseProvider(String userId, [ConnectionMode? mode]) => - DriftChatDatabase(userId, NativeDatabase.memory()); - -void main() { - group('connect', () { - const userId = 'testUserId'; - test('successfully connects with the Database', () async { - final client = StreamChatPersistenceClient(logLevel: Level.ALL); - expect(client.isConnected, false); - await client.connect(userId, databaseProvider: testDatabaseProvider); - expect(client.isConnected, true); - expect(client.db, isA()); - expect(client.userId, userId); - - addTearDown(() async { - await client.disconnect(); - }); - }); - - test('throws if already connected', () async { - final client = StreamChatPersistenceClient(logLevel: Level.ALL); - expect(client.isConnected, false); - await client.connect(userId, databaseProvider: testDatabaseProvider); - expect(client.isConnected, true); - expect(client.db, isA()); - expect(client.userId, userId); - expect( - () => client.connect(userId, databaseProvider: testDatabaseProvider), - throwsException, - ); - - addTearDown(() async { - await client.disconnect(); - }); - }); - }); - - test('disconnect', () async { - const userId = 'testUserId'; - final client = StreamChatPersistenceClient(logLevel: Level.ALL); - await client.connect(userId, databaseProvider: testDatabaseProvider); - expect(client.isConnected, true); - await client.disconnect(flush: true); - expect(client.isConnected, false); - }); - - test('client function throws stateError if db is not yet connected', () { - final client = StreamChatPersistenceClient(logLevel: Level.ALL); - expect( - // Running a function that requires db connection. - () => client.getReplies('testParentId'), - throwsA(isA()), - ); - }); - - group('client functions', () { - const userId = 'testUserId'; - final mockDatabase = MockChatDatabase(); - DriftChatDatabase _mockDatabaseProvider(_, __) => mockDatabase; - late StreamChatPersistenceClient client; - - setUp(() async { - client = StreamChatPersistenceClient(logLevel: Level.ALL); - await client.connect(userId, databaseProvider: _mockDatabaseProvider); - }); - - tearDown(() async { - await client.disconnect(); - }); - - test('getReplies', () async { - const parentId = 'testParentId'; - final replies = List.generate(3, (index) => Message(id: 'testId$index')); - - when(() => mockDatabase.messageDao.getThreadMessagesByParentId(parentId)) - .thenAnswer((_) async => replies); - - final fetchedReplies = await client.getReplies(parentId); - expect(fetchedReplies.length, replies.length); - verify(() => - mockDatabase.messageDao.getThreadMessagesByParentId(parentId)) - .called(1); - }); - - test('getConnectionInfo', () async { - final event = Event(); - when(() => mockDatabase.connectionEventDao.connectionEvent) - .thenAnswer((_) async => event); - - final fetchedEvent = await client.getConnectionInfo(); - expect(fetchedEvent, isNotNull); - expect(fetchedEvent!.type, event.type); - verify(() => mockDatabase.connectionEventDao.connectionEvent).called(1); - }); - - test('getLastSyncAt', () async { - final lastSync = DateTime.now(); - when(() => mockDatabase.connectionEventDao.lastSyncAt) - .thenAnswer((_) async => lastSync); - - final fetchedLastSync = await client.getLastSyncAt(); - expect(fetchedLastSync, isSameDateAs(lastSync)); - verify(() => mockDatabase.connectionEventDao.lastSyncAt).called(1); - }); - - test('updateConnectionInfo', () async { - final event = Event(); - when(() => mockDatabase.connectionEventDao.updateConnectionEvent(event)) - .thenAnswer((_) async => 1); - - await client.updateConnectionInfo(event); - verify(() => mockDatabase.connectionEventDao.updateConnectionEvent(event)) - .called(1); - }); - - test('updateLastSyncAt', () async { - final lastSync = DateTime.now(); - when(() => mockDatabase.connectionEventDao.updateLastSyncAt(lastSync)) - .thenAnswer((_) async => 1); - - await client.updateLastSyncAt(lastSync); - verify(() => mockDatabase.connectionEventDao.updateLastSyncAt(lastSync)) - .called(1); - }); - - test('getChannelCids', () async { - final channelCids = List.generate(3, (index) => 'testCid$index'); - when(() => mockDatabase.channelDao.cids) - .thenAnswer((_) async => channelCids); - - final fetchedChannelCids = await client.getChannelCids(); - expect(fetchedChannelCids.length, channelCids.length); - verify(() => mockDatabase.channelDao.cids).called(1); - }); - - test('getChannelByCid', () async { - const cid = 'testType:testId'; - final channelModel = ChannelModel(cid: cid); - when(() => mockDatabase.channelDao.getChannelByCid(cid)) - .thenAnswer((_) async => channelModel); - - final fetchedChannelModel = await client.getChannelByCid(cid); - expect(fetchedChannelModel, isNotNull); - expect(fetchedChannelModel!.cid, channelModel.cid); - verify(() => mockDatabase.channelDao.getChannelByCid(cid)).called(1); - }); - - test('getMembersByCid', () async { - const cid = 'testCid'; - final members = List.generate(3, (index) => Member()); - when(() => mockDatabase.memberDao.getMembersByCid(cid)) - .thenAnswer((_) async => members); - - final fetchedMembers = await client.getMembersByCid(cid); - expect(fetchedMembers.length, members.length); - verify(() => mockDatabase.memberDao.getMembersByCid(cid)).called(1); - }); - - test('getReadsByCid', () async { - const cid = 'testCid'; - final reads = List.generate( - 3, - (index) => Read( - user: User(id: 'testUserId$index'), - lastRead: DateTime.now(), - lastReadMessageId: 'lastMessageId$index', - ), - ); - when(() => mockDatabase.readDao.getReadsByCid(cid)) - .thenAnswer((_) async => reads); - - final fetchedReads = await client.getReadsByCid(cid); - expect(fetchedReads.length, reads.length); - verify(() => mockDatabase.readDao.getReadsByCid(cid)).called(1); - }); - - test('getMessagesByCid', () async { - const cid = 'testCid'; - final messages = List.generate(3, (index) => Message()); - when(() => mockDatabase.messageDao.getMessagesByCid(cid)) - .thenAnswer((_) async => messages); - - final fetchedMessages = await client.getMessagesByCid(cid); - expect(fetchedMessages.length, messages.length); - verify(() => mockDatabase.messageDao.getMessagesByCid(cid)).called(1); - }); - - test('getPinnedMessagesByCid', () async { - const cid = 'testCid'; - final messages = List.generate(3, (index) => Message()); - when(() => mockDatabase.pinnedMessageDao.getMessagesByCid(cid)) - .thenAnswer((_) async => messages); - - final fetchedMessages = await client.getPinnedMessagesByCid(cid); - expect(fetchedMessages.length, messages.length); - verify(() => mockDatabase.pinnedMessageDao.getMessagesByCid(cid)) - .called(1); - }); - - test('getChannelStateByCid', () async { - const cid = 'testType:testId'; - final messages = List.generate(3, (index) => Message()); - final members = List.generate(3, (index) => Member()); - final reads = List.generate( - 3, - (index) => Read( - user: User(id: 'testUserId$index'), - lastRead: DateTime.now(), - lastReadMessageId: 'lastMessageId$index', - ), - ); - final channel = ChannelModel(cid: cid); - - when(() => mockDatabase.memberDao.getMembersByCid(cid)) - .thenAnswer((_) async => members); - when(() => mockDatabase.readDao.getReadsByCid(cid)) - .thenAnswer((_) async => reads); - when(() => mockDatabase.channelDao.getChannelByCid(cid)) - .thenAnswer((_) async => channel); - when(() => mockDatabase.messageDao.getMessagesByCid(cid)) - .thenAnswer((_) async => messages); - when(() => mockDatabase.pinnedMessageDao.getMessagesByCid(cid)) - .thenAnswer((_) async => messages); - - final fetchedChannelState = await client.getChannelStateByCid(cid); - expect(fetchedChannelState.messages?.length, messages.length); - expect(fetchedChannelState.pinnedMessages?.length, messages.length); - expect(fetchedChannelState.members?.length, members.length); - expect(fetchedChannelState.read?.length, reads.length); - expect(fetchedChannelState.channel!.cid, channel.cid); - - verify(() => mockDatabase.memberDao.getMembersByCid(cid)).called(1); - verify(() => mockDatabase.readDao.getReadsByCid(cid)).called(1); - verify(() => mockDatabase.channelDao.getChannelByCid(cid)).called(1); - verify(() => mockDatabase.messageDao.getMessagesByCid(cid)).called(1); - verify(() => mockDatabase.pinnedMessageDao.getMessagesByCid(cid)) - .called(1); - }); - - group('getChannelState', () { - test('should throw if sort is provided without comparator', () async { - final sort = [ - const SortOption( - 'testField', - direction: SortOption.ASC, - ), - ]; - - expect( - () => client.getChannelStates(channelStateSort: sort), - throwsA(isA()), - ); - }); - - test('should work fine', () async { - const cid = 'testType:testId'; - final channels = List.generate(3, (index) => ChannelModel(cid: cid)); - final messages = List.generate(3, (index) => Message()); - final members = List.generate(3, (index) => Member()); - final reads = List.generate( - 3, - (index) => Read( - user: User(id: 'testUserId$index'), - lastRead: DateTime.now(), - lastReadMessageId: 'lastMessageId$index', - ), - ); - final channel = ChannelModel(cid: cid); - final channelStates = channels - .map( - (channel) => ChannelState( - channel: channel, - messages: messages, - pinnedMessages: messages, - members: members, - read: reads, - ), - ) - .toList(growable: false); - - when(() => mockDatabase.channelQueryDao.getChannels()) - .thenAnswer((_) async => channels); - when(() => mockDatabase.memberDao.getMembersByCid(cid)) - .thenAnswer((_) async => members); - when(() => mockDatabase.readDao.getReadsByCid(cid)) - .thenAnswer((_) async => reads); - when(() => mockDatabase.channelDao.getChannelByCid(cid)) - .thenAnswer((_) async => channel); - when(() => mockDatabase.messageDao.getMessagesByCid(cid)) - .thenAnswer((_) async => messages); - when(() => mockDatabase.pinnedMessageDao.getMessagesByCid(cid)) - .thenAnswer((_) async => messages); - - final fetchedChannelStates = await client.getChannelStates(); - expect(fetchedChannelStates.length, channelStates.length); - - for (var i = 0; i < fetchedChannelStates.length; i++) { - final original = channelStates[i]; - final fetched = fetchedChannelStates[i]; - expect(fetched.members?.length, original.members?.length); - expect(fetched.messages?.length, original.messages?.length); - expect( - fetched.pinnedMessages?.length, original.pinnedMessages?.length); - expect(fetched.read?.length, original.read?.length); - expect(fetched.channel!.cid, original.channel!.cid); - } - - verify(() => mockDatabase.channelQueryDao.getChannels()).called(1); - verify(() => mockDatabase.memberDao.getMembersByCid(cid)).called(3); - verify(() => mockDatabase.readDao.getReadsByCid(cid)).called(3); - verify(() => mockDatabase.channelDao.getChannelByCid(cid)).called(3); - verify(() => mockDatabase.messageDao.getMessagesByCid(cid)).called(3); - verify(() => mockDatabase.pinnedMessageDao.getMessagesByCid(cid)) - .called(3); - }); - }); - - test('updateChannelQueries', () async { - final filter = Filter.in_('members', const ['testUserId']); - const cids = []; - when(() => - mockDatabase.channelQueryDao.updateChannelQueries(filter, cids)) - .thenAnswer((_) => Future.value()); - - await client.updateChannelQueries(filter, cids); - verify(() => - mockDatabase.channelQueryDao.updateChannelQueries(filter, cids)) - .called(1); - }); - - test('deleteMessageById', () async { - const messageId = 'testMessageId'; - when(() => mockDatabase.messageDao.deleteMessageByIds([messageId])) - .thenAnswer((_) async => 1); - - await client.deleteMessageById(messageId); - verify(() => mockDatabase.messageDao.deleteMessageByIds([messageId])) - .called(1); - }); - - test('deletePinnedMessageById', () async { - const messageId = 'testMessageId'; - when(() => mockDatabase.pinnedMessageDao.deleteMessageByIds([messageId])) - .thenAnswer((_) async => 1); - - await client.deletePinnedMessageById(messageId); - verify(() => - mockDatabase.pinnedMessageDao.deleteMessageByIds([messageId])) - .called(1); - }); - - test('deleteMessageByIds', () async { - const messageIds = []; - when(() => mockDatabase.messageDao.deleteMessageByIds(messageIds)) - .thenAnswer((_) async => 1); - - await client.deleteMessageByIds(messageIds); - verify(() => mockDatabase.messageDao.deleteMessageByIds(messageIds)) - .called(1); - }); - - test('deletePinnedMessageByIds', () async { - const messageIds = []; - when(() => mockDatabase.pinnedMessageDao.deleteMessageByIds(messageIds)) - .thenAnswer((_) async => 1); - - await client.deletePinnedMessageByIds(messageIds); - verify(() => mockDatabase.pinnedMessageDao.deleteMessageByIds(messageIds)) - .called(1); - }); - - test('deleteMessageByCid', () async { - const cid = 'testCid'; - when(() => mockDatabase.messageDao.deleteMessageByCids([cid])) - .thenAnswer((_) async => 1); - - await client.deleteMessageByCid(cid); - verify(() => mockDatabase.messageDao.deleteMessageByCids([cid])) - .called(1); - }); - - test('deletePinnedMessageByCid', () async { - const cid = 'testCid'; - when(() => mockDatabase.pinnedMessageDao.deleteMessageByCids([cid])) - .thenAnswer((_) async => 1); - - await client.deletePinnedMessageByCid(cid); - verify(() => mockDatabase.pinnedMessageDao.deleteMessageByCids([cid])) - .called(1); - }); - - test('deleteMessageByCids', () async { - const cids = []; - when(() => mockDatabase.messageDao.deleteMessageByCids(cids)) - .thenAnswer((_) async => 1); - - await client.deleteMessageByCids(cids); - verify(() => mockDatabase.messageDao.deleteMessageByCids(cids)).called(1); - }); - - test('deletePinnedMessageByCids', () async { - const cids = []; - when(() => mockDatabase.pinnedMessageDao.deleteMessageByCids(cids)) - .thenAnswer((_) async => 1); - - await client.deletePinnedMessageByCids(cids); - verify(() => mockDatabase.pinnedMessageDao.deleteMessageByCids(cids)) - .called(1); - }); - - test('deleteChannels', () async { - const cids = []; - when(() => mockDatabase.channelDao.deleteChannelByCids(cids)) - .thenAnswer((_) async => 1); - - await client.deleteChannels(cids); - verify(() => mockDatabase.channelDao.deleteChannelByCids(cids)).called(1); - }); - - test('updateMessages', () async { - const cid = 'testCid'; - final messages = List.generate(3, (index) => Message()); - - when(() => mockDatabase.messageDao.bulkUpdateMessages({cid: messages})) - .thenAnswer((_) => Future.value()); - - await client.updateMessages(cid, messages); - verify(() => mockDatabase.messageDao.bulkUpdateMessages({cid: messages})) - .called(1); - }); - - test('updatePinnedMessages', () async { - const cid = 'testCid'; - final messages = List.generate(3, (index) => Message()); - when( - () => mockDatabase.pinnedMessageDao.bulkUpdateMessages({cid: messages}), - ).thenAnswer((_) => Future.value()); - - await client.updatePinnedMessages(cid, messages); - verify( - () => mockDatabase.pinnedMessageDao.bulkUpdateMessages({cid: messages}), - ).called(1); - }); - - test('getChannelThreads', () async { - const cid = 'testCid'; - final messages = - List.generate(3, (index) => Message(parentId: 'testParentId$index')); - final threads = messages.fold>>( - {}, - (prev, curr) => prev - ..update( - curr.parentId!, - (value) => [...value, curr], - ifAbsent: () => [], - ), - ); - when(() => mockDatabase.messageDao.getThreadMessages(cid)) - .thenAnswer((realInvocation) async => messages); - - final fetchedThreads = await client.getChannelThreads(cid); - expect(fetchedThreads.length, threads.length); - for (var i = 0; i < fetchedThreads.length; i++) { - final original = threads.entries.elementAt(i); - final fetched = fetchedThreads.entries.elementAt(i); - expect(fetched.key, original.key); - } - - verify(() => mockDatabase.messageDao.getThreadMessages(cid)).called(1); - }); - - test('updateChannels', () async { - const cid = 'testType:testId'; - final channels = List.generate(3, (index) => ChannelModel(cid: cid)); - when(() => mockDatabase.channelDao.updateChannels(channels)) - .thenAnswer((_) => Future.value()); - - await client.updateChannels(channels); - verify(() => mockDatabase.channelDao.updateChannels(channels)).called(1); - }); - - test('updatePolls', () async { - const name = 'testPollName'; - final options = List.generate(3, (index) => PollOption(text: '$index')); - final polls = - List.generate(3, (index) => Poll(name: name, options: options)); - when(() => mockDatabase.pollDao.updatePolls(polls)) - .thenAnswer((_) => Future.value()); - - await client.updatePolls(polls); - verify(() => mockDatabase.pollDao.updatePolls(polls)).called(1); - }); - - test('deletePollsByIds', () async { - final pollIds = ['testPollId']; - when(() => mockDatabase.pollDao.deletePollsByIds(pollIds)) - .thenAnswer((_) => Future.value()); - - await client.deletePollsByIds(pollIds); - verify(() => mockDatabase.pollDao.deletePollsByIds(pollIds)).called(1); - }); - - test('updatePollVotes', () async { - final pollVotes = List.generate( - 3, (index) => PollVote(id: '$index', optionId: 'testOptionId$index')); - when(() => mockDatabase.pollVoteDao.updatePollVotes(pollVotes)) - .thenAnswer((_) => Future.value()); - - await client.updatePollVotes(pollVotes); - verify(() => mockDatabase.pollVoteDao.updatePollVotes(pollVotes)) - .called(1); - }); - - test('deletePollVotesByPollIds', () async { - final pollIds = ['testPollId']; - when(() => mockDatabase.pollVoteDao.deletePollVotesByPollIds(pollIds)) - .thenAnswer((_) => Future.value()); - - await client.deletePollVotesByPollIds(pollIds); - verify(() => mockDatabase.pollVoteDao.deletePollVotesByPollIds(pollIds)) - .called(1); - }); - - test('updateMembers', () async { - const cid = 'testCid'; - final members = List.generate(3, (index) => Member()); - when(() => mockDatabase.memberDao.bulkUpdateMembers({cid: members})) - .thenAnswer((_) => Future.value()); - - await client.updateMembers(cid, members); - verify(() => mockDatabase.memberDao.bulkUpdateMembers({cid: members})) - .called(1); - }); - - test('updateReads', () async { - const cid = 'testCid'; - final reads = List.generate( - 3, - (index) => Read( - user: User(id: 'testUserId$index'), - lastRead: DateTime.now(), - lastReadMessageId: 'lastMessageId$index', - ), - ); - when(() => mockDatabase.readDao.bulkUpdateReads({cid: reads})) - .thenAnswer((_) => Future.value()); - - await client.updateReads(cid, reads); - verify(() => mockDatabase.readDao.bulkUpdateReads({cid: reads})) - .called(1); - }); - - test('updateUsers', () async { - final users = List.generate(3, (index) => User(id: 'testUserId$index')); - when(() => mockDatabase.userDao.updateUsers(users)) - .thenAnswer((_) => Future.value()); - - await client.updateUsers(users); - verify(() => mockDatabase.userDao.updateUsers(users)).called(1); - }); - - test('updateReactions', () async { - final reactions = List.generate( - 3, - (index) => Reaction(type: 'testType$index'), - ); - when(() => mockDatabase.reactionDao.updateReactions(reactions)) - .thenAnswer((_) => Future.value()); - - await client.updateReactions(reactions); - verify(() => mockDatabase.reactionDao.updateReactions(reactions)) - .called(1); - }); - - test('updatePinnedMessageReactions', () async { - final reactions = List.generate( - 3, - (index) => Reaction(type: 'testType$index'), - ); - when(() => - mockDatabase.pinnedMessageReactionDao.updateReactions(reactions)) - .thenAnswer((_) => Future.value()); - - await client.updatePinnedMessageReactions(reactions); - verify(() => - mockDatabase.pinnedMessageReactionDao.updateReactions(reactions)) - .called(1); - }); - - test('deleteReactionsByMessageId', () async { - final messageIds = []; - when(() => - mockDatabase.reactionDao.deleteReactionsByMessageIds(messageIds)) - .thenAnswer((_) => Future.value()); - - await client.deleteReactionsByMessageId(messageIds); - verify(() => - mockDatabase.reactionDao.deleteReactionsByMessageIds(messageIds)) - .called(1); - }); - - test('deletePinnedMessageReactionsByMessageId', () async { - final messageIds = []; - when(() => mockDatabase.pinnedMessageReactionDao - .deleteReactionsByMessageIds(messageIds)) - .thenAnswer((_) => Future.value()); - - await client.deletePinnedMessageReactionsByMessageId(messageIds); - verify(() => mockDatabase.pinnedMessageReactionDao - .deleteReactionsByMessageIds(messageIds)).called(1); - }); - - test('deleteMembersByCids', () async { - final cids = []; - when(() => mockDatabase.memberDao.deleteMemberByCids(cids)) - .thenAnswer((_) => Future.value()); - - await client.deleteMembersByCids(cids); - verify(() => mockDatabase.memberDao.deleteMemberByCids(cids)).called(1); - }); - - tearDown(() async { - await client.disconnect(flush: true); - }); - }); -} diff --git a/pubspec.lock b/pubspec.lock deleted file mode 100644 index 9ce61453f6..0000000000 --- a/pubspec.lock +++ /dev/null @@ -1,450 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - sha256: "03f6da266a27a4538a69295ec142cb5717d7d4e5727b84658b63e1e1509bac9c" - url: "https://pub.dev" - source: hosted - version: "79.0.0" - _macros: - dependency: transitive - description: dart - source: sdk - version: "0.3.3" - analyzer: - dependency: transitive - description: - name: analyzer - sha256: c9040fc56483c22a5e04a9f6a251313118b1a3c42423770623128fa484115643 - url: "https://pub.dev" - source: hosted - version: "7.2.0" - ansi_styles: - dependency: transitive - description: - name: ansi_styles - sha256: "9c656cc12b3c27b17dd982b2cc5c0cfdfbdabd7bc8f3ae5e8542d9867b47ce8a" - url: "https://pub.dev" - source: hosted - version: "0.3.2+1" - args: - dependency: transitive - description: - name: args - sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 - url: "https://pub.dev" - source: hosted - version: "2.6.0" - async: - dependency: transitive - description: - name: async - sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 - url: "https://pub.dev" - source: hosted - version: "2.12.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - built_collection: - dependency: transitive - description: - name: built_collection - sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://pub.dev" - source: hosted - version: "5.1.1" - built_value: - dependency: transitive - description: - name: built_value - sha256: "28a712df2576b63c6c005c465989a348604960c0958d28be5303ba9baa841ac2" - url: "https://pub.dev" - source: hosted - version: "8.9.3" - charcode: - dependency: transitive - description: - name: charcode - sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a - url: "https://pub.dev" - source: hosted - version: "1.4.0" - cli_launcher: - dependency: transitive - description: - name: cli_launcher - sha256: "5e7e0282b79e8642edd6510ee468ae2976d847a0a29b3916e85f5fa1bfe24005" - url: "https://pub.dev" - source: hosted - version: "0.3.1" - cli_util: - dependency: transitive - description: - name: cli_util - sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c - url: "https://pub.dev" - source: hosted - version: "0.4.2" - clock: - dependency: transitive - description: - name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b - url: "https://pub.dev" - source: hosted - version: "1.1.2" - code_builder: - dependency: "direct dev" - description: - name: code_builder - sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" - url: "https://pub.dev" - source: hosted - version: "4.10.1" - collection: - dependency: transitive - description: - name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" - url: "https://pub.dev" - source: hosted - version: "1.19.1" - conventional_commit: - dependency: transitive - description: - name: conventional_commit - sha256: dec15ad1118f029c618651a4359eb9135d8b88f761aa24e4016d061cd45948f2 - url: "https://pub.dev" - source: hosted - version: "0.6.0+1" - convert: - dependency: transitive - description: - name: convert - sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 - url: "https://pub.dev" - source: hosted - version: "3.1.2" - crypto: - dependency: transitive - description: - name: crypto - sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" - url: "https://pub.dev" - source: hosted - version: "3.0.6" - dart_style: - dependency: "direct dev" - description: - name: dart_style - sha256: "27eb0ae77836989a3bc541ce55595e8ceee0992807f14511552a898ddd0d88ac" - url: "https://pub.dev" - source: hosted - version: "3.0.1" - file: - dependency: transitive - description: - name: file - sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 - url: "https://pub.dev" - source: hosted - version: "7.0.1" - fixnum: - dependency: transitive - description: - name: fixnum - sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be - url: "https://pub.dev" - source: hosted - version: "1.1.1" - glob: - dependency: transitive - description: - name: glob - sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - graphs: - dependency: transitive - description: - name: graphs - sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - http: - dependency: transitive - description: - name: http - sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 - url: "https://pub.dev" - source: hosted - version: "1.2.2" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "76d306a1c3afb33fe82e2bbacad62a61f409b5634c915fceb0d799de1a913360" - url: "https://pub.dev" - source: hosted - version: "4.1.1" - intl: - dependency: transitive - description: - name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf - url: "https://pub.dev" - source: hosted - version: "0.19.0" - io: - dependency: transitive - description: - name: io - sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b - url: "https://pub.dev" - source: hosted - version: "1.0.5" - json_annotation: - dependency: transitive - description: - name: json_annotation - sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" - url: "https://pub.dev" - source: hosted - version: "4.9.0" - macros: - dependency: transitive - description: - name: macros - sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656" - url: "https://pub.dev" - source: hosted - version: "0.1.3-main.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 - url: "https://pub.dev" - source: hosted - version: "0.12.17" - melos: - dependency: "direct dev" - description: - name: melos - sha256: a62abfa8c7826cec927f8585572bb9adf591be152150494d879ca2c75118809d - url: "https://pub.dev" - source: hosted - version: "6.2.0" - meta: - dependency: transitive - description: - name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c - url: "https://pub.dev" - source: hosted - version: "1.16.0" - mustache_template: - dependency: transitive - description: - name: mustache_template - sha256: a46e26f91445bfb0b60519be280555b06792460b27b19e2b19ad5b9740df5d1c - url: "https://pub.dev" - source: hosted - version: "2.0.0" - package_config: - dependency: transitive - description: - name: package_config - sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - path: - dependency: "direct dev" - description: - name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" - url: "https://pub.dev" - source: hosted - version: "1.9.1" - platform: - dependency: transitive - description: - name: platform - sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" - url: "https://pub.dev" - source: hosted - version: "3.1.6" - pool: - dependency: transitive - description: - name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" - url: "https://pub.dev" - source: hosted - version: "1.5.1" - process: - dependency: transitive - description: - name: process - sha256: "107d8be718f120bbba9dcd1e95e3bd325b1b4a4f07db64154635ba03f2567a0d" - url: "https://pub.dev" - source: hosted - version: "5.0.3" - prompts: - dependency: transitive - description: - name: prompts - sha256: "3773b845e85a849f01e793c4fc18a45d52d7783b4cb6c0569fad19f9d0a774a1" - url: "https://pub.dev" - source: hosted - version: "2.0.0" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd" - url: "https://pub.dev" - source: hosted - version: "2.1.5" - pub_updater: - dependency: transitive - description: - name: pub_updater - sha256: "54e8dc865349059ebe7f163d6acce7c89eb958b8047e6d6e80ce93b13d7c9e60" - url: "https://pub.dev" - source: hosted - version: "0.4.0" - pubspec: - dependency: transitive - description: - name: pubspec - sha256: f534a50a2b4d48dc3bc0ec147c8bd7c304280fff23b153f3f11803c4d49d927e - url: "https://pub.dev" - source: hosted - version: "2.3.0" - quiver: - dependency: transitive - description: - name: quiver - sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 - url: "https://pub.dev" - source: hosted - version: "3.2.2" - recase: - dependency: "direct dev" - description: - name: recase - sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 - url: "https://pub.dev" - source: hosted - version: "4.1.0" - source_span: - dependency: transitive - description: - name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" - url: "https://pub.dev" - source: hosted - version: "1.10.1" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" - url: "https://pub.dev" - source: hosted - version: "1.12.1" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: "4ac0537115a24d772c408a2520ecd0abb99bca2ea9c4e634ccbdbfae64fe17ec" - url: "https://pub.dev" - source: hosted - version: "2.1.3" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" - url: "https://pub.dev" - source: hosted - version: "1.4.1" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" - url: "https://pub.dev" - source: hosted - version: "1.2.2" - test_api: - dependency: transitive - description: - name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd - url: "https://pub.dev" - source: hosted - version: "0.7.4" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 - url: "https://pub.dev" - source: hosted - version: "1.4.0" - uri: - dependency: transitive - description: - name: uri - sha256: "889eea21e953187c6099802b7b4cf5219ba8f3518f604a1033064d45b1b8268a" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - watcher: - dependency: transitive - description: - name: watcher - sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" - url: "https://pub.dev" - source: hosted - version: "1.1.1" - web: - dependency: transitive - description: - name: web - sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb - url: "https://pub.dev" - source: hosted - version: "1.1.0" - yaml: - dependency: transitive - description: - name: yaml - sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce - url: "https://pub.dev" - source: hosted - version: "3.1.3" - yaml_edit: - dependency: transitive - description: - name: yaml_edit - sha256: fb38626579fb345ad00e674e2af3a5c9b0cc4b9bfb8fd7f7ff322c7c9e62aef5 - url: "https://pub.dev" - source: hosted - version: "2.2.2" -sdks: - dart: ">=3.6.2 <4.0.0" diff --git a/pubspec.yaml b/pubspec.yaml deleted file mode 100644 index ca94599d07..0000000000 --- a/pubspec.yaml +++ /dev/null @@ -1,11 +0,0 @@ -name: stream_chat_flutter_workspace - -environment: - sdk: ^3.6.2 - -dev_dependencies: - code_builder: ^4.10.1 - dart_style: ^3.0.1 - melos: ^6.2.0 - path: ^1.9.0 - recase: ^4.1.0 diff --git a/sample_app/.metadata b/sample_app/.metadata deleted file mode 100644 index c3217d62b0..0000000000 --- a/sample_app/.metadata +++ /dev/null @@ -1,30 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled. - -version: - revision: b06b8b2710955028a6b562f5aa6fe62941d6febf - channel: stable - -project_type: app - -# Tracks metadata for the flutter migrate command -migration: - platforms: - - platform: root - create_revision: b06b8b2710955028a6b562f5aa6fe62941d6febf - base_revision: b06b8b2710955028a6b562f5aa6fe62941d6febf - - platform: android - create_revision: b06b8b2710955028a6b562f5aa6fe62941d6febf - base_revision: b06b8b2710955028a6b562f5aa6fe62941d6febf - - # User provided section - - # List of Local paths (relative to this file) that should be - # ignored by the migrate tool. - # - # Files that are not part of the templates will be ignored by default. - unmanaged_files: - - 'lib/main.dart' - - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/sample_app/README.md b/sample_app/README.md deleted file mode 100644 index a7d543793d..0000000000 --- a/sample_app/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# Stream Chat v1 - -![](https://raw.githubusercontent.com/GetStream/flutter-samples/master/images/sdk_hero_v4.png) - -Stream Chat V1 is a sample app implemented using Stream Chat and Flutter. It is a fully fledged messaging app built using a combination of our pre-made widgets and custom Flutter widgets. - -It supports several advanced features like: - -- Channels list UI -- Channel UI -- Message reactions -- Link preview -- Image, video and file attachments -- Editing and deleting messages -- Typing indicators -- Read indicators -- Image gallery -- GIF support -- Light and dark themes -- Threads -- Slash commands -- Markdown message formatting -- Count for unread messages - -![Features iOS](https://user-images.githubusercontent.com/20601437/110333493-eb023a80-8021-11eb-8fb1-b74f9ef37897.gif) - -## Getting Started - -Before running this project please ensure Flutter is installed and configured on your machine. If you're new to Flutter, please checkout the [official guide](https://flutter.dev/docs/get-started/install) with installation instructions for your OS. - - - -This project is only configured to support the following platforms: - -- Android -- iOS - -Web and Desktop are not supported at this time. - -After installing Flutter and the necessary toolchain for your device (Android or iOS), connect your device or open your emulator before running the following: - -**Clone the repo** - -```bash -git clone https://github.com/GetStream/flutter-samples -``` - -**Open the app folder** - -```bash -cd flutter-samples/stream_chat_v1 -``` - -**Install package dependencies:** - -```bash -flutter packages get -``` - -**Open or create an emulator** - -```bash -# To run an emulator, run 'flutter emulators --launch '. -# To create a new emulator, run 'flutter emulators --create [--name xyz]'. -# You can find more information on managing emulators at the links below: -# [https://developer.android.com/studio/run/managing-avds](https://developer.android.com/studio/run/managing-avds)[https://developer.android.com/studio/command-line/avdmanager](https://developer.android.com/studio/command-line/avdmanager) -``` - -**Run the project on your device or emulator:** - -```bash -flutter run -``` diff --git a/sample_app/analysis_options.yaml b/sample_app/analysis_options.yaml deleted file mode 100644 index 84b4cef988..0000000000 --- a/sample_app/analysis_options.yaml +++ /dev/null @@ -1,7 +0,0 @@ -include: ../analysis_options.yaml - -linter: - rules: - cascade_invocations: false - public_member_api_docs: false - lines_longer_than_80_chars: false \ No newline at end of file diff --git a/sample_app/android/app/build.gradle b/sample_app/android/app/build.gradle deleted file mode 100644 index f19f37f25f..0000000000 --- a/sample_app/android/app/build.gradle +++ /dev/null @@ -1,92 +0,0 @@ -plugins { - id "com.android.application" - id "kotlin-android" - id "dev.flutter.flutter-gradle-plugin" -} - -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -android { - namespace "com.example.example" - - compileSdkVersion 35 - - ndkVersion "26.2.11394342" - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - lintOptions { - disable 'InvalidPackage' - checkReleaseBuilds false - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.example" - minSdkVersion 23 - targetSdkVersion 34 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - multiDexEnabled true - } - - compileOptions { - // Flag to enable support for the new language APIs - coreLibraryDesugaringEnabled true - } - - // Added this block: - afterEvaluate { project -> - if (project.hasProperty("kotlin")) { - project.tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { - kotlinOptions { - jvmTarget = "1.8" - } - } - } - } - - signingConfigs { - debug { - storeFile file('debug.keystore') - storePassword 'android' - keyAlias 'androiddebugkey' - keyPassword 'android' - } - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} - -dependencies { - coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.2.2' -} diff --git a/sample_app/android/app/debug.keystore b/sample_app/android/app/debug.keystore deleted file mode 100644 index cdfc792b63..0000000000 Binary files a/sample_app/android/app/debug.keystore and /dev/null differ diff --git a/sample_app/android/app/src/debug/AndroidManifest.xml b/sample_app/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index 57257d0647..0000000000 --- a/sample_app/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - diff --git a/sample_app/android/app/src/main/AndroidManifest.xml b/sample_app/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index 676306632e..0000000000 --- a/sample_app/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/sample_app/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/sample_app/android/app/src/main/kotlin/com/example/example/MainActivity.kt deleted file mode 100644 index e793a000d6..0000000000 --- a/sample_app/android/app/src/main/kotlin/com/example/example/MainActivity.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.example.example - -import io.flutter.embedding.android.FlutterActivity - -class MainActivity: FlutterActivity() { -} diff --git a/sample_app/android/app/src/main/res/drawable-anydpi-v24/ic_notification.xml b/sample_app/android/app/src/main/res/drawable-anydpi-v24/ic_notification.xml deleted file mode 100644 index b953a9cf5d..0000000000 --- a/sample_app/android/app/src/main/res/drawable-anydpi-v24/ic_notification.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - diff --git a/sample_app/android/app/src/main/res/drawable-anydpi-v24/ic_notification_in_app.xml b/sample_app/android/app/src/main/res/drawable-anydpi-v24/ic_notification_in_app.xml deleted file mode 100644 index 3c858bb018..0000000000 --- a/sample_app/android/app/src/main/res/drawable-anydpi-v24/ic_notification_in_app.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - diff --git a/sample_app/android/app/src/main/res/drawable-hdpi/ic_launcher_background.png b/sample_app/android/app/src/main/res/drawable-hdpi/ic_launcher_background.png deleted file mode 100644 index 83e8029b3a..0000000000 Binary files a/sample_app/android/app/src/main/res/drawable-hdpi/ic_launcher_background.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png b/sample_app/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png deleted file mode 100644 index 8c255db3ac..0000000000 Binary files a/sample_app/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/drawable-hdpi/ic_notification.png b/sample_app/android/app/src/main/res/drawable-hdpi/ic_notification.png deleted file mode 100644 index 89924d6f90..0000000000 Binary files a/sample_app/android/app/src/main/res/drawable-hdpi/ic_notification.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/drawable-hdpi/ic_notification_in_app.png b/sample_app/android/app/src/main/res/drawable-hdpi/ic_notification_in_app.png deleted file mode 100644 index 738f9dd5aa..0000000000 Binary files a/sample_app/android/app/src/main/res/drawable-hdpi/ic_notification_in_app.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/drawable-mdpi/ic_launcher_background.png b/sample_app/android/app/src/main/res/drawable-mdpi/ic_launcher_background.png deleted file mode 100644 index 6a78a3117d..0000000000 Binary files a/sample_app/android/app/src/main/res/drawable-mdpi/ic_launcher_background.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png b/sample_app/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png deleted file mode 100644 index 0145cae348..0000000000 Binary files a/sample_app/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/drawable-mdpi/ic_notification.png b/sample_app/android/app/src/main/res/drawable-mdpi/ic_notification.png deleted file mode 100644 index bc99f1b280..0000000000 Binary files a/sample_app/android/app/src/main/res/drawable-mdpi/ic_notification.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/drawable-mdpi/ic_notification_in_app.png b/sample_app/android/app/src/main/res/drawable-mdpi/ic_notification_in_app.png deleted file mode 100644 index e983bbde6a..0000000000 Binary files a/sample_app/android/app/src/main/res/drawable-mdpi/ic_notification_in_app.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/drawable-v21/launch_background.xml b/sample_app/android/app/src/main/res/drawable-v21/launch_background.xml deleted file mode 100644 index f74085f3f6..0000000000 --- a/sample_app/android/app/src/main/res/drawable-v21/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/sample_app/android/app/src/main/res/drawable-xhdpi/ic_launcher_background.png b/sample_app/android/app/src/main/res/drawable-xhdpi/ic_launcher_background.png deleted file mode 100644 index 58bbfe99a5..0000000000 Binary files a/sample_app/android/app/src/main/res/drawable-xhdpi/ic_launcher_background.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png b/sample_app/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png deleted file mode 100644 index f7bf2544c3..0000000000 Binary files a/sample_app/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/drawable-xhdpi/ic_notification.png b/sample_app/android/app/src/main/res/drawable-xhdpi/ic_notification.png deleted file mode 100644 index c75ff08340..0000000000 Binary files a/sample_app/android/app/src/main/res/drawable-xhdpi/ic_notification.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/drawable-xhdpi/ic_notification_in_app.png b/sample_app/android/app/src/main/res/drawable-xhdpi/ic_notification_in_app.png deleted file mode 100644 index 1eec20b635..0000000000 Binary files a/sample_app/android/app/src/main/res/drawable-xhdpi/ic_notification_in_app.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png b/sample_app/android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png deleted file mode 100644 index b1b4cace8f..0000000000 Binary files a/sample_app/android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png b/sample_app/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png deleted file mode 100644 index 837416c02b..0000000000 Binary files a/sample_app/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/drawable-xxhdpi/ic_notification.png b/sample_app/android/app/src/main/res/drawable-xxhdpi/ic_notification.png deleted file mode 100644 index f65b65435a..0000000000 Binary files a/sample_app/android/app/src/main/res/drawable-xxhdpi/ic_notification.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/drawable-xxhdpi/ic_notification_in_app.png b/sample_app/android/app/src/main/res/drawable-xxhdpi/ic_notification_in_app.png deleted file mode 100644 index c0c3912a44..0000000000 Binary files a/sample_app/android/app/src/main/res/drawable-xxhdpi/ic_notification_in_app.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_background.png b/sample_app/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_background.png deleted file mode 100644 index 8b20fd6058..0000000000 Binary files a/sample_app/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_background.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png b/sample_app/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png deleted file mode 100644 index 5f89936bd3..0000000000 Binary files a/sample_app/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/drawable/launch_background.xml b/sample_app/android/app/src/main/res/drawable/launch_background.xml deleted file mode 100644 index 304732f884..0000000000 --- a/sample_app/android/app/src/main/res/drawable/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/sample_app/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/sample_app/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 7e91a578c4..0000000000 --- a/sample_app/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/sample_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/sample_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 8ec56ee792..0000000000 Binary files a/sample_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/sample_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 2f7fdaa7e0..0000000000 Binary files a/sample_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/sample_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 2c20055da1..0000000000 Binary files a/sample_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/sample_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 89492aaa61..0000000000 Binary files a/sample_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/sample_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index dd30e06e28..0000000000 Binary files a/sample_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/sample_app/android/app/src/main/res/values-night/styles.xml b/sample_app/android/app/src/main/res/values-night/styles.xml deleted file mode 100644 index 06952be745..0000000000 --- a/sample_app/android/app/src/main/res/values-night/styles.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/sample_app/android/app/src/main/res/values/styles.xml b/sample_app/android/app/src/main/res/values/styles.xml deleted file mode 100644 index cb1ef88056..0000000000 --- a/sample_app/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/sample_app/android/app/src/profile/AndroidManifest.xml b/sample_app/android/app/src/profile/AndroidManifest.xml deleted file mode 100644 index c208884f30..0000000000 --- a/sample_app/android/app/src/profile/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/sample_app/android/build.gradle b/sample_app/android/build.gradle deleted file mode 100644 index 0ef75c8bc5..0000000000 --- a/sample_app/android/build.gradle +++ /dev/null @@ -1,28 +0,0 @@ -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" - - afterEvaluate { - // check if `android` block is available and namespace isn't set - if(it.hasProperty('android') && it.android.namespace == null){ - def manifest = new XmlSlurper().parse(file(it.android.sourceSets.main.manifest.srcFile)) - def packageName = manifest.@package.text() - android.namespace= packageName - } - } - -} -subprojects { - project.evaluationDependsOn(':app') -} - -tasks.register("clean", Delete) { - delete rootProject.buildDir -} diff --git a/sample_app/android/gradle.properties b/sample_app/android/gradle.properties deleted file mode 100644 index 38c8d4544f..0000000000 --- a/sample_app/android/gradle.properties +++ /dev/null @@ -1,4 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M -android.enableR8=true -android.useAndroidX=true -android.enableJetifier=true diff --git a/sample_app/android/gradle/wrapper/gradle-wrapper.properties b/sample_app/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index ed0d5cdc32..0000000000 --- a/sample_app/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Thu Oct 22 11:03:39 CEST 2020 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip diff --git a/sample_app/android/settings.gradle b/sample_app/android/settings.gradle deleted file mode 100644 index bcc097f859..0000000000 --- a/sample_app/android/settings.gradle +++ /dev/null @@ -1,25 +0,0 @@ -pluginManagement { - def flutterSdkPath = { - def properties = new Properties() - file("local.properties").withInputStream { properties.load(it) } - def flutterSdkPath = properties.getProperty("flutter.sdk") - assert flutterSdkPath != null, "flutter.sdk not set in local.properties" - return flutterSdkPath - }() - - includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") - - repositories { - google() - mavenCentral() - gradlePluginPortal() - } -} - -plugins { - id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version '8.3.2' apply false - id "org.jetbrains.kotlin.android" version "1.9.24" apply false -} - -include ":app" \ No newline at end of file diff --git a/sample_app/android/settings_aar.gradle b/sample_app/android/settings_aar.gradle deleted file mode 100644 index e7b4def49c..0000000000 --- a/sample_app/android/settings_aar.gradle +++ /dev/null @@ -1 +0,0 @@ -include ':app' diff --git a/sample_app/ios/.ruby-version b/sample_app/ios/.ruby-version deleted file mode 100644 index a0cd9f0ccb..0000000000 --- a/sample_app/ios/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -3.1.0 \ No newline at end of file diff --git a/sample_app/ios/Flutter/AppFrameworkInfo.plist b/sample_app/ios/Flutter/AppFrameworkInfo.plist deleted file mode 100644 index 8c6e56146e..0000000000 --- a/sample_app/ios/Flutter/AppFrameworkInfo.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - MinimumOSVersion - 12.0 - - diff --git a/sample_app/ios/Flutter/Debug.xcconfig b/sample_app/ios/Flutter/Debug.xcconfig deleted file mode 100644 index ec97fc6f30..0000000000 --- a/sample_app/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "Generated.xcconfig" diff --git a/sample_app/ios/Flutter/Release.xcconfig b/sample_app/ios/Flutter/Release.xcconfig deleted file mode 100644 index c4855bfe20..0000000000 --- a/sample_app/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "Generated.xcconfig" diff --git a/sample_app/ios/Gemfile b/sample_app/ios/Gemfile deleted file mode 100644 index cdd3a6b349..0000000000 --- a/sample_app/ios/Gemfile +++ /dev/null @@ -1,6 +0,0 @@ -source "https://rubygems.org" - -gem "fastlane" - -plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') -eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/sample_app/ios/Gemfile.lock b/sample_app/ios/Gemfile.lock deleted file mode 100644 index 85b5b38dd3..0000000000 --- a/sample_app/ios/Gemfile.lock +++ /dev/null @@ -1,216 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - CFPropertyList (3.0.4) - rexml - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) - artifactory (3.0.15) - atomos (0.1.3) - aws-eventstream (1.2.0) - aws-partitions (1.512.0) - aws-sdk-core (3.121.1) - aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.239.0) - aws-sigv4 (~> 1.1) - jmespath (~> 1.0) - aws-sdk-kms (1.49.0) - aws-sdk-core (~> 3, >= 3.120.0) - aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.103.0) - aws-sdk-core (~> 3, >= 3.120.0) - aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.4) - aws-sigv4 (1.4.0) - aws-eventstream (~> 1, >= 1.0.2) - babosa (1.0.4) - claide (1.0.3) - colored (1.2) - colored2 (3.1.2) - commander (4.6.0) - highline (~> 2.0.0) - declarative (0.0.20) - digest-crc (0.6.4) - rake (>= 12.0.0, < 14.0.0) - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) - dotenv (2.7.6) - emoji_regex (3.2.3) - excon (0.86.0) - faraday (1.8.0) - faraday-em_http (~> 1.0) - faraday-em_synchrony (~> 1.0) - faraday-excon (~> 1.1) - faraday-httpclient (~> 1.0.1) - faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.1) - faraday-patron (~> 1.0) - faraday-rack (~> 1.0) - multipart-post (>= 1.2, < 3) - 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-net_http (1.0.1) - faraday-net_http_persistent (1.2.0) - faraday-patron (1.0.0) - faraday-rack (1.0.0) - faraday_middleware (1.1.0) - faraday (~> 1.0) - fastimage (2.2.5) - fastlane (2.195.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 - 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) - gh_inspector (>= 1.1.2, < 2.0.0) - google-apis-androidpublisher_v3 (~> 0.3) - google-apis-playcustomapp_v1 (~> 0.1) - google-cloud-storage (~> 1.31) - highline (~> 2.0) - json (< 3.0.0) - jwt (>= 2.1.0, < 3) - mini_magick (>= 4.9.4, < 5.0.0) - multipart-post (~> 2.0.0) - naturally (~> 2.2) - optparse (~> 0.1.1) - plist (>= 3.1.0, < 4.0.0) - rubyzip (>= 2.0.0, < 3.0.0) - security (= 0.1.3) - simctl (~> 1.6.3) - terminal-notifier (>= 2.0.0, < 3.0.0) - terminal-table (>= 1.4.5, < 2.0.0) - 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) - fastlane-plugin-firebase_app_distribution (0.2.9) - gh_inspector (1.1.3) - google-apis-androidpublisher_v3 (0.11.0) - google-apis-core (>= 0.4, < 2.a) - google-apis-core (0.4.1) - 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 - webrick - google-apis-iamcredentials_v1 (0.7.0) - google-apis-core (>= 0.4, < 2.a) - google-apis-playcustomapp_v1 (0.5.0) - google-apis-core (>= 0.4, < 2.a) - google-apis-storage_v1 (0.8.0) - google-apis-core (>= 0.4, < 2.a) - google-cloud-core (1.6.0) - google-cloud-env (~> 1.0) - google-cloud-errors (~> 1.0) - google-cloud-env (1.5.0) - faraday (>= 0.17.3, < 2.0) - google-cloud-errors (1.2.0) - google-cloud-storage (1.34.1) - addressable (~> 2.5) - digest-crc (~> 0.4) - google-apis-iamcredentials_v1 (~> 0.1) - google-apis-storage_v1 (~> 0.1) - google-cloud-core (~> 1.6) - googleauth (>= 0.16.2, < 2.a) - mini_mime (~> 1.0) - googleauth (1.0.0) - faraday (>= 0.17.3, < 2.0) - jwt (>= 1.4, < 3.0) - memoist (~> 0.16) - multi_json (~> 1.11) - os (>= 0.9, < 2.0) - signet (>= 0.16, < 2.a) - highline (2.0.3) - http-cookie (1.0.4) - domain_name (~> 0.5) - httpclient (2.8.3) - jmespath (1.4.0) - json (2.5.1) - jwt (2.3.0) - memoist (0.16.2) - mini_magick (4.11.0) - mini_mime (1.1.1) - multi_json (1.15.0) - multipart-post (2.0.0) - nanaimo (0.3.0) - naturally (2.2.1) - optparse (0.1.1) - os (1.1.1) - plist (3.6.0) - public_suffix (4.0.6) - rake (13.0.6) - representable (3.1.1) - declarative (< 0.1.0) - trailblazer-option (>= 0.1.1, < 0.2.0) - uber (< 0.2.0) - retriable (3.1.2) - rexml (3.2.5) - rouge (2.0.7) - ruby2_keywords (0.0.5) - rubyzip (2.3.2) - security (0.1.3) - signet (0.16.0) - addressable (~> 2.8) - faraday (>= 0.17.3, < 2.0) - jwt (>= 1.5, < 3.0) - multi_json (~> 1.10) - simctl (1.6.8) - CFPropertyList - naturally - terminal-notifier (2.0.0) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) - trailblazer-option (0.1.1) - tty-cursor (0.7.1) - tty-screen (0.8.1) - tty-spinner (0.9.3) - tty-cursor (~> 0.7) - uber (0.1.0) - unf (0.1.4) - unf_ext - unf_ext (0.0.8) - unicode-display_width (1.8.0) - webrick (1.7.0) - word_wrap (1.0.0) - xcodeproj (1.21.0) - CFPropertyList (>= 2.3.3, < 4.0) - atomos (~> 0.1.3) - claide (>= 1.0.2, < 2.0) - colored2 (~> 3.1) - nanaimo (~> 0.3.0) - rexml (~> 3.2.4) - xcpretty (0.3.0) - rouge (~> 2.0.7) - xcpretty-travis-formatter (1.0.1) - xcpretty (~> 0.2, >= 0.0.7) - -PLATFORMS - ruby - -DEPENDENCIES - fastlane - fastlane-plugin-firebase_app_distribution - -BUNDLED WITH - 2.0.2 diff --git a/sample_app/ios/Podfile b/sample_app/ios/Podfile deleted file mode 100644 index 279576f388..0000000000 --- a/sample_app/ios/Podfile +++ /dev/null @@ -1,41 +0,0 @@ -# Uncomment this line to define a global platform for your project -# platform :ios, '12.0' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_ios_podfile_setup - -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_ios_build_settings(target) - end -end diff --git a/sample_app/ios/Runner.xcodeproj/project.pbxproj b/sample_app/ios/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index d5cbb11dc9..0000000000 --- a/sample_app/ios/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,721 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXBuildFile section */ - 042035C495B4FCF371FF6929 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = DF7052F0ED7A0F0A6487676A /* GoogleService-Info.plist */; }; - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 78DFDA8BF7ADD8988BA9B2C9 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4E5F7C8F1FB8AA92A6E2273 /* Pods_Runner.framework */; }; - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 0BC14C55242B5A7A0028DE94 /* Embed App Extensions */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 13; - files = ( - ); - name = "Embed App Extensions"; - runOnlyForDeploymentPostprocessing = 0; - }; - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 0BC14C5B242B5FF50028DE94 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 887F9B6BDC8480C6B3628366 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - 91C3AAF66766852887B9460F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - A4E5F7C8F1FB8AA92A6E2273 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - DF7052F0ED7A0F0A6487676A /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; }; - ED2D4AF300CE6F2241B57624 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 97C146EB1CF9000F007C117D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 78DFDA8BF7ADD8988BA9B2C9 /* Pods_Runner.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 9740EEB11CF90186004384FC /* Flutter */ = { - isa = PBXGroup; - children = ( - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 9740EEB31CF90195004384FC /* Generated.xcconfig */, - ); - name = Flutter; - sourceTree = ""; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - 97C146F01CF9000F007C117D /* Runner */, - 9740EEB11CF90186004384FC /* Flutter */, - 97C146EF1CF9000F007C117D /* Products */, - CF168B61BAB91958681C7C21 /* Pods */, - F93B8F215F5456A6442CFA78 /* Frameworks */, - DF7052F0ED7A0F0A6487676A /* GoogleService-Info.plist */, - ); - sourceTree = ""; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - ); - name = Products; - sourceTree = ""; - }; - 97C146F01CF9000F007C117D /* Runner */ = { - isa = PBXGroup; - children = ( - 0BC14C5B242B5FF50028DE94 /* Runner.entitlements */, - 97C146FA1CF9000F007C117D /* Main.storyboard */, - 97C146FD1CF9000F007C117D /* Assets.xcassets */, - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 97C147021CF9000F007C117D /* Info.plist */, - 97C146F11CF9000F007C117D /* Supporting Files */, - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, - ); - path = Runner; - sourceTree = ""; - }; - 97C146F11CF9000F007C117D /* Supporting Files */ = { - isa = PBXGroup; - children = ( - ); - name = "Supporting Files"; - sourceTree = ""; - }; - CF168B61BAB91958681C7C21 /* Pods */ = { - isa = PBXGroup; - children = ( - 91C3AAF66766852887B9460F /* Pods-Runner.debug.xcconfig */, - ED2D4AF300CE6F2241B57624 /* Pods-Runner.release.xcconfig */, - 887F9B6BDC8480C6B3628366 /* Pods-Runner.profile.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; - F93B8F215F5456A6442CFA78 /* Frameworks */ = { - isa = PBXGroup; - children = ( - A4E5F7C8F1FB8AA92A6E2273 /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 97C146ED1CF9000F007C117D /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - A83A30E8FF0298FDB3189FB1 /* [CP] Check Pods Manifest.lock */, - 9740EEB61CF901F6004384FC /* Run Script */, - 97C146EA1CF9000F007C117D /* Sources */, - 97C146EB1CF9000F007C117D /* Frameworks */, - 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 0BC14C55242B5A7A0028DE94 /* Embed App Extensions */, - 5468FC796DAC9C565B3989B4 /* [CP] Embed Pods Frameworks */, - 8519A78E4BDC9820D507B557 /* [CP] Copy Pods Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Runner; - productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* Runner.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 97C146E61CF9000F007C117D /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 1140; - LastUpgradeCheck = 1510; - ORGANIZATIONNAME = "The Chromium Authors"; - TargetAttributes = { - 97C146ED1CF9000F007C117D = { - CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 1100; - }; - }; - }; - buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 97C146E51CF9000F007C117D; - productRefGroup = 97C146EF1CF9000F007C117D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 97C146ED1CF9000F007C117D /* Runner */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 97C146EC1CF9000F007C117D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - 042035C495B4FCF371FF6929 /* GoogleService-Info.plist in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 5468FC796DAC9C565B3989B4 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/DKImagePickerController/DKImagePickerController.framework", - "${BUILT_PRODUCTS_DIR}/DKPhotoGallery/DKPhotoGallery.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseInstallations/FirebaseInstallations.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseMessaging/FirebaseMessaging.framework", - "${BUILT_PRODUCTS_DIR}/GoogleDataTransport/GoogleDataTransport.framework", - "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", - "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework", - "${BUILT_PRODUCTS_DIR}/Sentry/Sentry.framework", - "${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework", - "${BUILT_PRODUCTS_DIR}/audio_session/audio_session.framework", - "${BUILT_PRODUCTS_DIR}/connectivity_plus/connectivity_plus.framework", - "${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework", - "${BUILT_PRODUCTS_DIR}/file_selector_ios/file_selector_ios.framework", - "${BUILT_PRODUCTS_DIR}/flutter_app_badger/flutter_app_badger.framework", - "${BUILT_PRODUCTS_DIR}/flutter_local_notifications/flutter_local_notifications.framework", - "${BUILT_PRODUCTS_DIR}/flutter_secure_storage/flutter_secure_storage.framework", - "${BUILT_PRODUCTS_DIR}/gal/gal.framework", - "${BUILT_PRODUCTS_DIR}/get_thumbnail_video/get_thumbnail_video.framework", - "${BUILT_PRODUCTS_DIR}/image_picker_ios/image_picker_ios.framework", - "${BUILT_PRODUCTS_DIR}/just_audio/just_audio.framework", - "${BUILT_PRODUCTS_DIR}/libwebp/libwebp.framework", - "${BUILT_PRODUCTS_DIR}/media_kit_video/media_kit_video.framework", - "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", - "${BUILT_PRODUCTS_DIR}/package_info_plus/package_info_plus.framework", - "${BUILT_PRODUCTS_DIR}/path_provider_foundation/path_provider_foundation.framework", - "${BUILT_PRODUCTS_DIR}/photo_manager/photo_manager.framework", - "${BUILT_PRODUCTS_DIR}/record_darwin/record_darwin.framework", - "${BUILT_PRODUCTS_DIR}/screen_brightness_ios/screen_brightness_ios.framework", - "${BUILT_PRODUCTS_DIR}/sentry_flutter/sentry_flutter.framework", - "${BUILT_PRODUCTS_DIR}/share_plus/share_plus.framework", - "${BUILT_PRODUCTS_DIR}/shared_preferences_foundation/shared_preferences_foundation.framework", - "${BUILT_PRODUCTS_DIR}/sqflite_darwin/sqflite_darwin.framework", - "${BUILT_PRODUCTS_DIR}/sqlite3/sqlite3.framework", - "${BUILT_PRODUCTS_DIR}/sqlite3_flutter_libs/sqlite3_flutter_libs.framework", - "${BUILT_PRODUCTS_DIR}/url_launcher_ios/url_launcher_ios.framework", - "${BUILT_PRODUCTS_DIR}/video_player_avfoundation/video_player_avfoundation.framework", - "${BUILT_PRODUCTS_DIR}/volume_controller/volume_controller.framework", - "${BUILT_PRODUCTS_DIR}/wakelock_plus/wakelock_plus.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKImagePickerController.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKPhotoGallery.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreInternal.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseInstallations.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseMessaging.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleDataTransport.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sentry.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyGif.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/audio_session.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity_plus.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_picker.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_selector_ios.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_app_badger.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_local_notifications.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_secure_storage.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/gal.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/get_thumbnail_video.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/image_picker_ios.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/just_audio.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libwebp.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/media_kit_video.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info_plus.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_foundation.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/photo_manager.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/record_darwin.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/screen_brightness_ios.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sentry_flutter.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/share_plus.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_foundation.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqflite_darwin.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqlite3.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqlite3_flutter_libs.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher_ios.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_player_avfoundation.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/volume_controller.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/wakelock_plus.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 8519A78E4BDC9820D507B557 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/firebase_messaging/firebase_messaging_Privacy.bundle", - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/firebase_messaging_Privacy.bundle", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; - A83A30E8FF0298FDB3189FB1 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 97C146EA1CF9000F007C117D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 97C146FA1CF9000F007C117D /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C146FB1CF9000F007C117D /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 249021D3217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Profile; - }; - 249021D4217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = EHV7XZLAHA; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = io.getstream.flutter; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Profile; - }; - 97C147031CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 97C147041CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 97C147061CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = EHV7XZLAHA; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = io.getstream.flutter; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - 97C147071CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = EHV7XZLAHA; - ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - PRODUCT_BUNDLE_IDENTIFIER = io.getstream.flutter; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147031CF9000F007C117D /* Debug */, - 97C147041CF9000F007C117D /* Release */, - 249021D3217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147061CF9000F007C117D /* Debug */, - 97C147071CF9000F007C117D /* Release */, - 249021D4217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 97C146E61CF9000F007C117D /* Project object */; -} diff --git a/sample_app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/sample_app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a625..0000000000 --- a/sample_app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/sample_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/sample_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/sample_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/sample_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/sample_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5ea..0000000000 --- a/sample_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/sample_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Notifications.xcscheme b/sample_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Notifications.xcscheme deleted file mode 100644 index 9387f0d0e7..0000000000 --- a/sample_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Notifications.xcscheme +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/sample_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/sample_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index c53e2b314e..0000000000 --- a/sample_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/sample_app/ios/Runner.xcworkspace/contents.xcworkspacedata b/sample_app/ios/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 21a3cc14c7..0000000000 --- a/sample_app/ios/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/sample_app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/sample_app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/sample_app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/sample_app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/sample_app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5ea..0000000000 --- a/sample_app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/sample_app/ios/Runner/AppDelegate.swift b/sample_app/ios/Runner/AppDelegate.swift deleted file mode 100644 index 0f470eca1b..0000000000 --- a/sample_app/ios/Runner/AppDelegate.swift +++ /dev/null @@ -1,45 +0,0 @@ -import UIKit -import Flutter - -@main -@objc class AppDelegate: FlutterAppDelegate { - let sharedDefaults = UserDefaults(suiteName: "group.io.getstream.flutter") - - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - if let messageQueue = sharedDefaults?.stringArray(forKey: "messageQueue") { - UserDefaults.standard.setValue(messageQueue, forKey: "flutter.messageQueue") - sharedDefaults?.removeObject(forKey: "messageQueue") - } - - if #available(iOS 10.0, *) { - UNUserNotificationCenter.current().delegate = self - } - - GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } - - override func applicationDidEnterBackground(_ application: UIApplication) { - if let apiKey = UserDefaults.standard.string(forKey: "flutter.KEY_API_KEY") { - sharedDefaults?.setValue(apiKey, forKey: "KEY_API_KEY") - } - - if let token = UserDefaults.standard.string(forKey: "flutter.KEY_TOKEN") { - sharedDefaults?.setValue(token, forKey: "KEY_TOKEN") - } - - if let userId = UserDefaults.standard.string(forKey: "flutter.KEY_USER_ID") { - sharedDefaults?.setValue(userId, forKey: "KEY_USER_ID") - } - } - - override func applicationWillEnterForeground(_ application: UIApplication) { - if let messageQueue = sharedDefaults?.stringArray(forKey: "messageQueue") { - UserDefaults.standard.setValue(messageQueue, forKey: "flutter.messageQueue") - sharedDefaults?.removeObject(forKey: "messageQueue") - } - } -} diff --git a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d36b1fab2d..0000000000 --- a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - }, - { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "Icon-App-1024x1024@1x.png", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png deleted file mode 100644 index 6286cc9f76..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png deleted file mode 100644 index 95d13de016..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png deleted file mode 100644 index 5ad80187a2..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index 6e6c85b6a0..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png deleted file mode 100644 index 6f26f228f1..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index 7fe870d996..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png deleted file mode 100644 index 3ab319af19..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png deleted file mode 100644 index 5ad80187a2..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index 0a94384d61..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png deleted file mode 100644 index f049762f05..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png b/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png deleted file mode 100644 index 5fa61524ca..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png b/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png deleted file mode 100644 index 8874ac7b56..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png b/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png deleted file mode 100644 index bc00e9c326..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png b/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png deleted file mode 100644 index 527ae4cfa0..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index f049762f05..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index b51e00a7e5..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png deleted file mode 100644 index 8ec56ee792..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png b/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png deleted file mode 100644 index 89492aaa61..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png deleted file mode 100644 index 8c4973081f..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png deleted file mode 100644 index c59c5739cd..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png deleted file mode 100644 index b29e2f05dd..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json b/sample_app/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json deleted file mode 100644 index 9f447e1b38..0000000000 --- a/sample_app/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "background.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/sample_app/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png b/sample_app/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png deleted file mode 100644 index 36178800c0..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/sample_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json deleted file mode 100644 index 00cabce836..0000000000 --- a/sample_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "filename" : "LaunchImage.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "LaunchImage@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "LaunchImage@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/sample_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/sample_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png deleted file mode 100644 index 67774cf3da..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/sample_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png deleted file mode 100644 index 67774cf3da..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/sample_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png deleted file mode 100644 index 67774cf3da..0000000000 Binary files a/sample_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png and /dev/null differ diff --git a/sample_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/sample_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md deleted file mode 100644 index 89c2725b70..0000000000 --- a/sample_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Launch Screen Assets - -You can customize the launch screen with your own desired assets by replacing the image files in this directory. - -You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/sample_app/ios/Runner/Base.lproj/LaunchScreen.storyboard b/sample_app/ios/Runner/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index c9811f0205..0000000000 --- a/sample_app/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/sample_app/ios/Runner/Base.lproj/Main.storyboard b/sample_app/ios/Runner/Base.lproj/Main.storyboard deleted file mode 100644 index f3c28516fb..0000000000 --- a/sample_app/ios/Runner/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/sample_app/ios/Runner/GoogleService-Info.plist b/sample_app/ios/Runner/GoogleService-Info.plist deleted file mode 100644 index 9faee9ff20..0000000000 --- a/sample_app/ios/Runner/GoogleService-Info.plist +++ /dev/null @@ -1,38 +0,0 @@ - - - - - CLIENT_ID - 674907137625-flarfn9cefu4lermgpbc4b8rm8l15ian.apps.googleusercontent.com - REVERSED_CLIENT_ID - com.googleusercontent.apps.674907137625-flarfn9cefu4lermgpbc4b8rm8l15ian - ANDROID_CLIENT_ID - 674907137625-2scfo9a5cs074dced5vhm712ej6hhtpm.apps.googleusercontent.com - API_KEY - AIzaSyBTAsaKFUPLAJqfsLiz0yPUVzwrgJkOwSE - GCM_SENDER_ID - 674907137625 - PLIST_VERSION - 1 - BUNDLE_ID - io.getstream.flutter - PROJECT_ID - stream-chat-internal - STORAGE_BUCKET - stream-chat-internal.appspot.com - IS_ADS_ENABLED - - IS_ANALYTICS_ENABLED - - IS_APPINVITE_ENABLED - - IS_GCM_ENABLED - - IS_SIGNIN_ENABLED - - GOOGLE_APP_ID - 1:674907137625:ios:cafb9fb076a453c4d7f348 - DATABASE_URL - https://stream-chat-internal.firebaseio.com - - \ No newline at end of file diff --git a/sample_app/ios/Runner/Info.plist b/sample_app/ios/Runner/Info.plist deleted file mode 100644 index 27d9c14b17..0000000000 --- a/sample_app/ios/Runner/Info.plist +++ /dev/null @@ -1,74 +0,0 @@ - - - - - CADisableMinimumFrameDurationOnPhone - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLocalizations - - en - it - - CFBundleName - ChatSample - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - NSAppleMusicUsageDescription - Used to send message attachments - NSCameraUsageDescription - Used to send message attachments - NSMicrophoneUsageDescription - Used to send message attachments - NSPhotoLibraryUsageDescription - Used to send message attachments - UIApplicationSupportsIndirectInputEvents - - UIBackgroundModes - - fetch - remote-notification - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UIStatusBarHidden - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - - - diff --git a/sample_app/ios/Runner/Runner-Bridging-Header.h b/sample_app/ios/Runner/Runner-Bridging-Header.h deleted file mode 100644 index 7335fdf900..0000000000 --- a/sample_app/ios/Runner/Runner-Bridging-Header.h +++ /dev/null @@ -1 +0,0 @@ -#import "GeneratedPluginRegistrant.h" \ No newline at end of file diff --git a/sample_app/ios/Runner/Runner.entitlements b/sample_app/ios/Runner/Runner.entitlements deleted file mode 100644 index 433bdfe097..0000000000 --- a/sample_app/ios/Runner/Runner.entitlements +++ /dev/null @@ -1,12 +0,0 @@ - - - - - aps-environment - development - com.apple.security.application-groups - - group.io.getstream.flutter - - - diff --git a/sample_app/ios/fastlane/Appfile b/sample_app/ios/fastlane/Appfile deleted file mode 100644 index 1803063093..0000000000 --- a/sample_app/ios/fastlane/Appfile +++ /dev/null @@ -1,6 +0,0 @@ -# app_identifier("[[APP_IDENTIFIER]]") # The bundle identifier of your app -# apple_id("[[APPLE_ID]]") # Your Apple email address - - -# For more information about the Appfile, see: -# https://docs.fastlane.tools/advanced/#appfile diff --git a/sample_app/ios/fastlane/Fastfile b/sample_app/ios/fastlane/Fastfile deleted file mode 100644 index c072b9f034..0000000000 --- a/sample_app/ios/fastlane/Fastfile +++ /dev/null @@ -1,90 +0,0 @@ -fastlane_version "2.195.0" -default_platform :ios - -before_all do - if is_ci - setup_ci() - end -end - -desc "Installs all Certs and Profiles necessary for development and ad-hoc" -lane :match_me do - match( - type: "adhoc", - app_identifier: [ - "io.getstream.flutter", - ], - readonly: is_ci, - force_for_new_devices: true - ) -end - -desc "Installs all Certs and Profiles necessary for appstore" -lane :match_appstore do - match( - type: "appstore", - app_identifier: [ - "io.getstream.flutter", - ], - readonly: is_ci - ) -end - -platform :ios do - desc "Deploy build to Firebase" - lane :deploy_to_firebase do - match_me - - gym( - workspace: "./Runner.xcworkspace", - scheme: "Runner", - export_method: "ad-hoc", - export_options: "./fastlane/beta_gym_export_options.plist", - silent: true, - clean: true, - include_symbols: true, - output_directory: "./dist" - ) - - message = changelog_from_git_commits(commits_count: 10) - - firebase_app_distribution( - app: "1:674907137625:ios:cafb9fb076a453c4d7f348", - groups: "ios-stream-testers" - ) - end -end - -platform :ios do - desc "Deploy build to TestFlight" - lane :deploy_to_testflight do - match_appstore - - settings_to_override = { - :BUNDLE_IDENTIFIER => "io.getstream.flutter", - :PROVISIONING_PROFILE_SPECIFIER => "match AppStore io.getstream.flutter 1651569762" - } - - gym( - workspace: "./Runner.xcworkspace", - scheme: "Runner", - export_method: "app-store", - export_options: "./fastlane/testflight_gym_export_options.plist", - silent: true, - clean: true, - xcargs: settings_to_override, - include_symbols: true, - output_directory: "./dist", - ) - - message = changelog_from_git_commits(commits_count: 10) - - upload_to_testflight( - groups: ['Public'], - distribute_external: true, - changelog: message, - username: 'salvatore@getstream.io', - reject_build_waiting_for_review: true - ) - end -end diff --git a/sample_app/ios/fastlane/Matchfile b/sample_app/ios/fastlane/Matchfile deleted file mode 100644 index cc976de1a7..0000000000 --- a/sample_app/ios/fastlane/Matchfile +++ /dev/null @@ -1,16 +0,0 @@ -git_url("https://github.com/GetStream/ios-certificates") - -storage_mode("git") - -username("salvatore@getstream.io") - -team_id("EHV7XZLAHA") - -# app_identifier(["tools.fastlane.app", "tools.fastlane.app2"]) - -# username("user@fastlane.tools") # Your Apple Developer Portal username - -# For all available options run `fastlane match --help` -# Remove the # in the beginning of the line to enable the other options - -# The docs are available on https://docs.fastlane.tools/actions/match diff --git a/sample_app/ios/fastlane/Pluginfile b/sample_app/ios/fastlane/Pluginfile deleted file mode 100644 index b18539bc9b..0000000000 --- a/sample_app/ios/fastlane/Pluginfile +++ /dev/null @@ -1,5 +0,0 @@ -# Autogenerated by fastlane -# -# Ensure this file is checked in to source control! - -gem 'fastlane-plugin-firebase_app_distribution' diff --git a/sample_app/ios/fastlane/README.md b/sample_app/ios/fastlane/README.md deleted file mode 100644 index d6922829c2..0000000000 --- a/sample_app/ios/fastlane/README.md +++ /dev/null @@ -1,47 +0,0 @@ -fastlane documentation -================ -# Installation - -Make sure you have the latest version of the Xcode command line tools installed: - -``` -xcode-select --install -``` - -Install _fastlane_ using -``` -[sudo] gem install fastlane -NV -``` -or alternatively using `brew install fastlane` - -# Available Actions -### match_me -``` -fastlane match_me -``` -Installs all Certs and Profiles necessary for development and ad-hoc -### match_appstore -``` -fastlane match_appstore -``` -Installs all Certs and Profiles necessary for appstore - ----- - -## iOS -### ios deploy_to_firebase -``` -fastlane ios deploy_to_firebase -``` -Deploy build to Firebase -### ios deploy_to_testflight -``` -fastlane ios deploy_to_testflight -``` -Deploy build to TestFlight - ----- - -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). diff --git a/sample_app/ios/fastlane/beta_gym_export_options.plist b/sample_app/ios/fastlane/beta_gym_export_options.plist deleted file mode 100644 index e88559ac2a..0000000000 --- a/sample_app/ios/fastlane/beta_gym_export_options.plist +++ /dev/null @@ -1,13 +0,0 @@ - - - - - iCloudContainerEnvironment - Development - provisioningProfiles - - io.getstream.flutter - match AdHoc io.getstream.flutter - - - \ No newline at end of file diff --git a/sample_app/ios/fastlane/report.xml b/sample_app/ios/fastlane/report.xml deleted file mode 100644 index c91f10a976..0000000000 --- a/sample_app/ios/fastlane/report.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/sample_app/ios/fastlane/testflight_gym_export_options.plist b/sample_app/ios/fastlane/testflight_gym_export_options.plist deleted file mode 100644 index 16bb6afee5..0000000000 --- a/sample_app/ios/fastlane/testflight_gym_export_options.plist +++ /dev/null @@ -1,13 +0,0 @@ - - - - - iCloudContainerEnvironment - Production - provisioningProfiles - - io.getstream.flutter - match AppStore io.getstream.flutter 1651569762 - - - \ No newline at end of file diff --git a/sample_app/ios/firebase_app_id_file.json b/sample_app/ios/firebase_app_id_file.json deleted file mode 100644 index 1f8a4f7589..0000000000 --- a/sample_app/ios/firebase_app_id_file.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "file_generated_by": "FlutterFire CLI", - "purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory", - "GOOGLE_APP_ID": "1:674907137625:ios:cafb9fb076a453c4d7f348", - "FIREBASE_PROJECT_ID": "stream-chat-internal", - "GCM_SENDER_ID": "674907137625" -} \ No newline at end of file diff --git a/sample_app/lib/app.dart b/sample_app/lib/app.dart deleted file mode 100644 index a1c98ad593..0000000000 --- a/sample_app/lib/app.dart +++ /dev/null @@ -1,374 +0,0 @@ -import 'dart:async'; - -import 'package:firebase_core/firebase_core.dart'; -import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; -import 'package:go_router/go_router.dart'; -import 'package:provider/provider.dart'; -import 'package:sample_app/firebase_options.dart'; -import 'package:sample_app/pages/choose_user_page.dart'; -import 'package:sample_app/pages/splash_screen.dart'; -import 'package:sample_app/routes/app_routes.dart'; -import 'package:sample_app/routes/routes.dart'; -import 'package:sample_app/state/init_data.dart'; -import 'package:sample_app/utils/app_config.dart'; -import 'package:sample_app/utils/local_notification_observer.dart'; -import 'package:sample_app/utils/localizations.dart'; -import 'package:sentry_flutter/sentry_flutter.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:stream_chat_localizations/stream_chat_localizations.dart'; -import 'package:stream_chat_persistence/stream_chat_persistence.dart'; -import 'package:streaming_shared_preferences/streaming_shared_preferences.dart'; - -/// Constructs callback for background notification handling. -/// -/// Will be invoked from another Isolate, that's why it's required to -/// initialize everything again: -/// - Firebase -/// - StreamChatClient -/// - StreamChatPersistenceClient -@pragma('vm:entry-point') -Future _onFirebaseBackgroundMessage(RemoteMessage message) async { - debugPrint('[onBackgroundMessage] #firebase; message: ${message.toMap()}'); - final data = message.data; - // ensure that Push Notification was sent by Stream. - if (data['sender'] != 'stream.chat') { - return; - } - // ensure that Push Notification relates to a new message event. - if (data['type'] != 'message.new') { - return; - } - // If you're going to use Firebase services in the background, make sure - // you call `initializeApp` before using Firebase services. - await Firebase.initializeApp( - options: DefaultFirebaseOptions.currentPlatform, - ); - // read existing user info. - String? apiKey, userId, token; - if (!kIsWeb) { - const secureStorage = FlutterSecureStorage(); - apiKey = await secureStorage.read(key: kStreamApiKey); - userId = await secureStorage.read(key: kStreamUserId); - token = await secureStorage.read(key: kStreamToken); - } - if (userId == null || token == null) { - return; - } - final chatClient = buildStreamChatClient(apiKey ?? kDefaultStreamApiKey); - try { - await chatClient.connectUser( - User(id: userId), - token, - // do not open WS connection - connectWebSocket: false, - ); - // initialize persistence with current user - if (!chatPersistentClient.isConnected) { - await chatPersistentClient.connect(userId); - } - - final messageId = data['id']; - final cid = data['cid']; - // pre-cache the new message using client and persistence. - final response = await chatClient.getMessage(messageId); - await chatPersistentClient.updateMessages(cid, [response.message]); - } catch (e, stk) { - debugPrint('[onBackgroundMessage] #firebase; failed: $e; $stk'); - } -} - -final chatPersistentClient = StreamChatPersistenceClient( - logLevel: Level.SEVERE, -); - -void _sampleAppLogHandler(LogRecord record) async { - if (kDebugMode) StreamChatClient.defaultLogHandler(record); - - // report errors to sentry.io - if (record.error != null || record.stackTrace != null) { - await Sentry.captureException( - record.error, - stackTrace: record.stackTrace, - ); - } -} - -StreamChatClient buildStreamChatClient(String apiKey) { - late Level logLevel; - if (kDebugMode) { - logLevel = Level.INFO; - } else { - logLevel = Level.SEVERE; - } - return StreamChatClient( - apiKey, - logLevel: logLevel, - logHandlerFunction: _sampleAppLogHandler, - //baseURL: 'http://:3030', - //baseWsUrl: 'ws://:8800', - )..chatPersistenceClient = chatPersistentClient; -} - -class StreamChatSampleApp extends StatefulWidget { - const StreamChatSampleApp({super.key}); - - @override - State createState() => _StreamChatSampleAppState(); -} - -class _StreamChatSampleAppState extends State - with SplashScreenStateMixin, TickerProviderStateMixin { - final InitNotifier _initNotifier = InitNotifier(); - - final firebaseSubscriptions = >[]; - StreamSubscription? userIdSubscription; - - Future _initConnection() async { - String? apiKey, userId, token; - - if (!kIsWeb) { - const secureStorage = FlutterSecureStorage(); - apiKey = await secureStorage.read(key: kStreamApiKey); - userId = await secureStorage.read(key: kStreamUserId); - token = await secureStorage.read(key: kStreamToken); - } - final client = buildStreamChatClient(apiKey ?? kDefaultStreamApiKey); - - if (userId != null && token != null) { - await client.connectUser( - User(id: userId), - token, - ); - } - - final prefs = await StreamingSharedPreferences.instance; - - return InitData(client, prefs); - } - - Future _initFirebaseMessaging(StreamChatClient client) async { - userIdSubscription?.cancel(); - userIdSubscription = client.state.currentUserStream - .map((it) => it?.id) - .distinct() - .listen((userId) async { - // User logged in - if (userId != null) { - // Requests notification permission. - await FirebaseMessaging.instance.requestPermission(); - // Sets callback for background messages. - FirebaseMessaging.onBackgroundMessage(_onFirebaseBackgroundMessage); - // Sets callback for the notification click event. - firebaseSubscriptions.add(FirebaseMessaging.onMessageOpenedApp - .listen(_onFirebaseMessageOpenedApp(client))); - // Sets callback for foreground messages - firebaseSubscriptions.add(FirebaseMessaging.onMessage - .listen(_onFirebaseForegroundMessage(client))); - // Sets callback for the token refresh event. - firebaseSubscriptions.add(FirebaseMessaging.instance.onTokenRefresh - .listen(_onFirebaseTokenRefresh(client))); - - final token = await FirebaseMessaging.instance.getToken(); - debugPrint('[onTokenInit] #firebase; token: $token'); - if (token != null) { - // replace with your push provider, e.g., 'PushProvider.xiaomi' - const pushProvider = PushProvider.firebase; - - // add Token to Stream - await client.addDevice(token, pushProvider); - } - } - // User logged out - else { - firebaseSubscriptions.cancelAll(); - final token = await FirebaseMessaging.instance.getToken(); - if (token != null) { - // remove token from Stream - await client.removeDevice(token); - } - } - }); - } - - /// Constructs callback for notification click event. - OnRemoteMessage _onFirebaseMessageOpenedApp(StreamChatClient client) { - return (message) async { - debugPrint('[onMessageOpenedApp] #firebase; message: ${message.toMap()}'); - // This callback is getting invoked when the user clicks - // on the notification in case if notification was shown by OS. - final channelType = (message.data['channel_type'] as String?) ?? ''; - final channelId = (message.data['channel_id'] as String?) ?? ''; - final channelCid = (message.data['cid'] as String?) ?? ''; - var channel = client.state.channels[channelCid]; - if (channel == null) { - channel = client.channel( - channelType, - id: channelId, - ); - await channel.watch(); - } - // Navigates to Channel page, which is associated with the notification. - GoRouter.of(_navigatorKey.currentContext!).pushNamed( - Routes.CHANNEL_PAGE.name, - pathParameters: Routes.CHANNEL_PAGE.params(channel), - ); - }; - } - - /// Constructs callback for foreground notification handling. - OnRemoteMessage _onFirebaseForegroundMessage(StreamChatClient client) { - return (message) async { - debugPrint( - '[onForegroundMessage] #firebase; message: ${message.toMap()}'); - }; - } - - /// Constructs callback for notification refresh event. - Future Function(String) _onFirebaseTokenRefresh( - StreamChatClient client, - ) { - return (token) async { - debugPrint('[onTokenRefresh] #firebase; token: $token'); - // This callback is getting invoked when the token got refreshed. - await client.addDevice(token, PushProvider.firebase); - }; - } - - @override - void initState() { - final timeOfStartMs = DateTime.now().millisecondsSinceEpoch; - - _initConnection().then( - (initData) { - setState(() { - _initNotifier.initData = initData; - }); - - final now = DateTime.now().millisecondsSinceEpoch; - - if (now - timeOfStartMs > 1500) { - SchedulerBinding.instance.addPostFrameCallback((timeStamp) { - forwardAnimations(); - }); - } else { - Future.delayed(const Duration(milliseconds: 1500)).then((value) { - forwardAnimations(); - }); - } - _initFirebaseMessaging(initData.client); - }, - ); - - super.initState(); - } - - @override - void dispose() { - super.dispose(); - userIdSubscription?.cancel(); - firebaseSubscriptions.cancelAll(); - } - - final GlobalKey _navigatorKey = GlobalKey(); - LocalNotificationObserver? localNotificationObserver; - - /// Conditionally sets up the router and adding an observer for the - /// current chat client. - GoRouter _setupRouter() { - if (localNotificationObserver != null) { - localNotificationObserver!.dispose(); - } - localNotificationObserver = LocalNotificationObserver( - _initNotifier.initData!.client, _navigatorKey); - - return GoRouter( - refreshListenable: _initNotifier, - initialLocation: Routes.CHANNEL_LIST_PAGE.path, - navigatorKey: _navigatorKey, - observers: [localNotificationObserver!], - redirect: (context, state) { - final loggedIn = - _initNotifier.initData?.client.state.currentUser != null; - final loggingIn = state.matchedLocation == Routes.CHOOSE_USER.path || - state.matchedLocation == Routes.ADVANCED_OPTIONS.path; - - if (!loggedIn) { - return loggingIn ? null : Routes.CHOOSE_USER.path; - } - - // if the user is logged in but still on the login page, send them to - // the home page - if (loggedIn && state.matchedLocation == Routes.CHOOSE_USER.path) { - return Routes.CHANNEL_LIST_PAGE.path; - } - - return null; - }, - routes: appRoutes, - ); - } - - @override - Widget build(BuildContext context) { - return Stack( - alignment: Alignment.center, - children: [ - if (_initNotifier.initData != null) - ChangeNotifierProvider.value( - value: _initNotifier, - builder: (context, child) => Builder( - builder: (context) { - context.watch(); // rebuild on change - return PreferenceBuilder( - preference: _initNotifier.initData!.preferences.getInt( - 'theme', - defaultValue: 0, - ), - builder: (context, snapshot) => MaterialApp.router( - theme: ThemeData.light(), - darkTheme: ThemeData.dark(), - themeMode: const { - -1: ThemeMode.dark, - 0: ThemeMode.system, - 1: ThemeMode.light, - }[snapshot], - supportedLocales: const [ - Locale('en'), - Locale('it'), - ], - localizationsDelegates: const [ - AppLocalizationsDelegate(), - GlobalStreamChatLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - ], - builder: (context, child) => StreamChat( - client: _initNotifier.initData!.client, - child: child, - ), - routerConfig: _setupRouter(), - ), - ); - }, - ), - ), - if (!animationCompleted) buildAnimation(), - ], - ); - } -} - -typedef OnRemoteMessage = Future Function(RemoteMessage); - -extension on List { - void cancelAll() { - for (final subscription in this) { - unawaited(subscription.cancel()); - } - clear(); - } -} diff --git a/sample_app/lib/firebase_options.dart b/sample_app/lib/firebase_options.dart deleted file mode 100644 index 46d9b06bab..0000000000 --- a/sample_app/lib/firebase_options.dart +++ /dev/null @@ -1,93 +0,0 @@ -// File generated by FlutterFire CLI. -// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members -import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; -import 'package:flutter/foundation.dart' - show defaultTargetPlatform, kIsWeb, TargetPlatform; - -/// Default [FirebaseOptions] for use with your Firebase apps. -/// -/// Example: -/// ```dart -/// import 'firebase_options.dart'; -/// // ... -/// await Firebase.initializeApp( -/// options: DefaultFirebaseOptions.currentPlatform, -/// ); -/// ``` -class DefaultFirebaseOptions { - static FirebaseOptions get currentPlatform { - if (kIsWeb) { - return web; - } - switch (defaultTargetPlatform) { - case TargetPlatform.android: - return android; - case TargetPlatform.iOS: - return ios; - case TargetPlatform.macOS: - return macos; - case TargetPlatform.windows: - throw UnsupportedError( - 'DefaultFirebaseOptions have not been configured for windows - ' - 'you can reconfigure this by running the FlutterFire CLI again.', - ); - case TargetPlatform.linux: - throw UnsupportedError( - 'DefaultFirebaseOptions have not been configured for linux - ' - 'you can reconfigure this by running the FlutterFire CLI again.', - ); - default: - throw UnsupportedError( - 'DefaultFirebaseOptions are not supported for this platform.', - ); - } - } - - static const FirebaseOptions web = FirebaseOptions( - apiKey: 'AIzaSyA4Obi2paSQ1IZZ88OcMC98DJlpV16DAzE', - appId: '1:674907137625:web:a4b97e5d080ec165d7f348', - messagingSenderId: '674907137625', - projectId: 'stream-chat-internal', - authDomain: 'stream-chat-internal.firebaseapp.com', - databaseURL: 'https://stream-chat-internal.firebaseio.com', - storageBucket: 'stream-chat-internal.appspot.com', - measurementId: 'G-F2RV4P139L', - ); - - static const FirebaseOptions android = FirebaseOptions( - apiKey: 'AIzaSyBArS4RH7lUn3xbD-jhzWl5hVWZhliRgY0', - appId: '1:674907137625:android:e55b74fd5747e39ad7f348', - messagingSenderId: '674907137625', - projectId: 'stream-chat-internal', - databaseURL: 'https://stream-chat-internal.firebaseio.com', - storageBucket: 'stream-chat-internal.appspot.com', - ); - - static const FirebaseOptions ios = FirebaseOptions( - apiKey: 'AIzaSyBTAsaKFUPLAJqfsLiz0yPUVzwrgJkOwSE', - appId: '1:674907137625:ios:cafb9fb076a453c4d7f348', - messagingSenderId: '674907137625', - projectId: 'stream-chat-internal', - databaseURL: 'https://stream-chat-internal.firebaseio.com', - storageBucket: 'stream-chat-internal.appspot.com', - androidClientId: - '674907137625-2scfo9a5cs074dced5vhm712ej6hhtpm.apps.googleusercontent.com', - iosClientId: - '674907137625-flarfn9cefu4lermgpbc4b8rm8l15ian.apps.googleusercontent.com', - iosBundleId: 'io.getstream.flutter', - ); - - static const FirebaseOptions macos = FirebaseOptions( - apiKey: 'AIzaSyBTAsaKFUPLAJqfsLiz0yPUVzwrgJkOwSE', - appId: '1:674907137625:ios:c719c700198c28b1d7f348', - messagingSenderId: '674907137625', - projectId: 'stream-chat-internal', - databaseURL: 'https://stream-chat-internal.firebaseio.com', - storageBucket: 'stream-chat-internal.appspot.com', - androidClientId: - '674907137625-2scfo9a5cs074dced5vhm712ej6hhtpm.apps.googleusercontent.com', - iosClientId: - '674907137625-p3msks3snq0h22l7ekpqcf0frr0vt8mg.apps.googleusercontent.com', - iosBundleId: 'io.getstream.streamChatV1', - ); -} diff --git a/sample_app/lib/main.dart b/sample_app/lib/main.dart deleted file mode 100644 index e1b3079049..0000000000 --- a/sample_app/lib/main.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'dart:async'; - -import 'package:firebase_core/firebase_core.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:sample_app/app.dart'; -import 'package:sample_app/firebase_options.dart'; -import 'package:sample_app/utils/app_config.dart'; -import 'package:sentry_flutter/sentry_flutter.dart'; - -Future main() async { - /// Captures errors reported by the Flutter framework. - FlutterError.onError = (FlutterErrorDetails details) { - if (kDebugMode) { - // In development mode, simply print to console. - FlutterError.dumpErrorToConsole(details); - } else { - // In production mode, report to the application zone to report to sentry. - Zone.current.handleUncaughtError(details.exception, details.stack!); - } - }; - - /// Captures errors reported by the native environment, including native iOS - /// and Android code. - Future reportError(dynamic error, StackTrace stackTrace) async { - // Print the exception to the console. - if (kDebugMode) { - // Print the full stacktrace in debug mode. - print(stackTrace); - return; - } else { - // Send the Exception and Stacktrace to sentry in Production mode. - await Sentry.captureException(error, stackTrace: stackTrace); - } - } - - /// Runs the app wrapped in a [Zone] that captures errors and sends them to - /// sentry. - runZonedGuarded( - () async { - WidgetsFlutterBinding.ensureInitialized(); - - // Wait for Sentry and Firebase to initialize before running the app. - await Future.wait([ - SentryFlutter.init((options) => options.dsn = sentryDsn), - Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform), - ]); - - runApp(const StreamChatSampleApp()); - }, - reportError, - ); -} diff --git a/sample_app/lib/pages/advanced_options_page.dart b/sample_app/lib/pages/advanced_options_page.dart deleted file mode 100644 index 81ab732c02..0000000000 --- a/sample_app/lib/pages/advanced_options_page.dart +++ /dev/null @@ -1,365 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; -import 'package:go_router/go_router.dart'; -import 'package:provider/provider.dart'; -import 'package:sample_app/app.dart'; -import 'package:sample_app/pages/choose_user_page.dart'; -import 'package:sample_app/routes/routes.dart'; -import 'package:sample_app/state/init_data.dart'; -import 'package:sample_app/utils/localizations.dart'; -import 'package:sample_app/widgets/stream_version.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -class AdvancedOptionsPage extends StatefulWidget { - const AdvancedOptionsPage({super.key}); - - @override - State createState() => _AdvancedOptionsPageState(); -} - -class _AdvancedOptionsPageState extends State { - final _formKey = GlobalKey(); - - final TextEditingController _apiKeyController = TextEditingController(); - String? _apiKeyError; - - final TextEditingController _userIdController = TextEditingController(); - String? _userIdError; - - final TextEditingController _userTokenController = TextEditingController(); - String? _userTokenError; - - final TextEditingController _usernameController = TextEditingController(); - - bool loading = false; - - @override - void dispose() { - _apiKeyController.dispose(); - _userIdController.dispose(); - _userTokenController.dispose(); - _usernameController.dispose(); - super.dispose(); - } - - Future _login() async { - if (loading) { - return; - } - if (_formKey.currentState!.validate()) { - final apiKey = _apiKeyController.text; - final userId = _userIdController.text; - final userToken = _userTokenController.text; - final username = _usernameController.text; - - loading = true; - showDialog( - barrierDismissible: false, - context: context, - barrierColor: StreamChatTheme.of(context).colorTheme.overlay, - builder: (context) => Center( - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(16), - color: StreamChatTheme.of(context).colorTheme.barsBg, - ), - height: 100, - width: 100, - child: const Center( - child: CircularProgressIndicator(), - ), - ), - ), - ); - - final client = buildStreamChatClient(apiKey); - final router = GoRouter.of(context); - final initNotifier = context.read(); - - try { - await client.connectUser( - User( - id: userId, - extraData: { - 'name': username, - }, - ), - userToken, - ); - - const secureStorage = FlutterSecureStorage(); - await Future.wait([ - secureStorage.write( - key: kStreamApiKey, - value: apiKey, - ), - secureStorage.write( - key: kStreamUserId, - value: userId, - ), - secureStorage.write( - key: kStreamToken, - value: userToken, - ), - ]); - } catch (e) { - debugPrint(e.toString()); - var errorText = AppLocalizations.of(context).errorConnecting; - if (e is Map) { - errorText = e['message'] ?? errorText; - } - Navigator.of(context).pop(); - setState(() { - _apiKeyError = errorText.toUpperCase(); - }); - loading = false; - return; - } - loading = false; - initNotifier.initData = initNotifier.initData!.copyWith(client: client); - - router.goNamed(Routes.CHOOSE_USER.name); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: StreamChatTheme.of(context).colorTheme.appBg, - appBar: AppBar( - backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg, - elevation: 1, - centerTitle: true, - title: Text( - AppLocalizations.of(context).advancedOptions, - style: StreamChatTheme.of(context).textTheme.headlineBold.copyWith( - color: StreamChatTheme.of(context).colorTheme.textHighEmphasis), - ), - leading: IconButton( - icon: const StreamSvgIcon(icon: StreamSvgIcons.left), - color: StreamChatTheme.of(context).colorTheme.textHighEmphasis, - onPressed: () { - Navigator.pop(context); - }, - ), - ), - body: Builder( - builder: (context) { - return Padding( - padding: const EdgeInsets.fromLTRB(16, 16, 16, 0), - child: Form( - key: _formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - TextFormField( - controller: _apiKeyController, - onChanged: (_) { - if (_apiKeyError != null) { - setState(() { - _apiKeyError = null; - }); - } - }, - validator: (value) { - if (value!.isEmpty) { - setState(() { - _apiKeyError = AppLocalizations.of(context) - .apiKeyError - .toUpperCase(); - }); - return _apiKeyError; - } - return null; - }, - style: TextStyle( - fontSize: 14, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis, - ), - decoration: InputDecoration( - errorStyle: const TextStyle(height: 0, fontSize: 0), - labelStyle: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: _apiKeyError != null - ? StreamChatTheme.of(context).colorTheme.accentError - : StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis, - ), - border: UnderlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide.none, - ), - fillColor: StreamChatTheme.of(context).colorTheme.inputBg, - filled: true, - labelText: _apiKeyError != null - ? '${AppLocalizations.of(context).chatApiKey.toUpperCase()}: $_apiKeyError' - : AppLocalizations.of(context).chatApiKey, - ), - textInputAction: TextInputAction.next, - ), - const SizedBox(height: 8), - TextFormField( - controller: _userIdController, - onChanged: (_) { - if (_userIdError != null) { - setState(() { - _userIdError = null; - }); - } - }, - validator: (value) { - if (value!.isEmpty) { - setState(() { - _userIdError = AppLocalizations.of(context) - .userIdError - .toUpperCase(); - }); - return _userIdError; - } - return null; - }, - style: TextStyle( - fontSize: 14, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis, - ), - textInputAction: TextInputAction.next, - decoration: InputDecoration( - errorStyle: const TextStyle(height: 0, fontSize: 0), - labelStyle: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 14, - color: _userIdError != null - ? StreamChatTheme.of(context).colorTheme.accentError - : StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis, - ), - border: UnderlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide.none, - ), - fillColor: StreamChatTheme.of(context).colorTheme.inputBg, - filled: true, - labelText: _userIdError != null - ? '${AppLocalizations.of(context).userId.toUpperCase()}: $_userIdError' - : AppLocalizations.of(context).userId, - ), - ), - const SizedBox(height: 8), - TextFormField( - onChanged: (_) { - if (_userTokenError != null) { - setState(() { - _userTokenError = null; - }); - } - }, - controller: _userTokenController, - validator: (value) { - if (value!.isEmpty) { - setState(() { - _userTokenError = AppLocalizations.of(context) - .userTokenError - .toUpperCase(); - }); - return _userTokenError; - } - return null; - }, - style: TextStyle( - fontSize: 14, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis, - ), - textInputAction: TextInputAction.next, - decoration: InputDecoration( - errorStyle: const TextStyle(height: 0, fontSize: 0), - labelStyle: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 14, - color: _userTokenError != null - ? StreamChatTheme.of(context).colorTheme.accentError - : StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis, - ), - border: UnderlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide.none, - ), - fillColor: StreamChatTheme.of(context).colorTheme.inputBg, - filled: true, - labelText: _userTokenError != null - ? '${AppLocalizations.of(context).userToken.toUpperCase()}: $_userTokenError' - : AppLocalizations.of(context).userToken, - ), - ), - const SizedBox(height: 8), - TextFormField( - controller: _usernameController, - textInputAction: TextInputAction.done, - decoration: InputDecoration( - labelStyle: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis, - ), - border: UnderlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: BorderSide.none, - ), - fillColor: StreamChatTheme.of(context).colorTheme.inputBg, - filled: true, - labelText: AppLocalizations.of(context).usernameOptional, - ), - ), - const Spacer(), - ElevatedButton( - style: ButtonStyle( - backgroundColor: WidgetStateProperty.all( - Theme.of(context).brightness == Brightness.light - ? StreamChatTheme.of(context) - .colorTheme - .accentPrimary - : Colors.white), - elevation: WidgetStateProperty.all(0), - padding: WidgetStateProperty.all( - const EdgeInsets.symmetric(vertical: 16)), - shape: WidgetStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(26), - ), - ), - ), - onPressed: _login, - child: Text( - AppLocalizations.of(context).login, - style: TextStyle( - fontSize: 16, - color: Theme.of(context).brightness != Brightness.light - ? StreamChatTheme.of(context) - .colorTheme - .accentPrimary - : Colors.white, - ), - ), - ), - const StreamVersion(), - ], - ), - ), - ); - }, - ), - ); - } -} diff --git a/sample_app/lib/pages/channel_file_display_screen.dart b/sample_app/lib/pages/channel_file_display_screen.dart deleted file mode 100644 index 727dd6bb87..0000000000 --- a/sample_app/lib/pages/channel_file_display_screen.dart +++ /dev/null @@ -1,158 +0,0 @@ -// ignore_for_file: deprecated_member_use - -import 'package:flutter/material.dart'; -import 'package:sample_app/utils/localizations.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:video_player/video_player.dart'; - -class ChannelFileDisplayScreen extends StatefulWidget { - const ChannelFileDisplayScreen({ - super.key, - required this.messageTheme, - }); - final StreamMessageThemeData messageTheme; - - @override - State createState() => - _ChannelFileDisplayScreenState(); -} - -class _ChannelFileDisplayScreenState extends State { - final Map controllerCache = {}; - - late final controller = StreamMessageSearchListController( - client: StreamChat.of(context).client, - filter: Filter.in_( - 'cid', - [StreamChannel.of(context).channel.cid!], - ), - messageFilter: Filter.in_( - 'attachments.type', - const ['file'], - ), - sort: [ - const SortOption( - 'created_at', - direction: SortOption.ASC, - ), - ], - limit: 20, - ); - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg, - appBar: AppBar( - elevation: 1, - centerTitle: true, - title: Text( - AppLocalizations.of(context).files, - style: TextStyle( - color: StreamChatTheme.of(context).colorTheme.textHighEmphasis, - fontSize: 16, - ), - ), - leading: const StreamBackButton(), - backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg, - ), - body: ValueListenableBuilder( - valueListenable: controller, - builder: ( - BuildContext context, - PagedValue value, - Widget? child, - ) { - return value.when( - (items, nextPageKey, error) { - if (items.isEmpty) { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - StreamSvgIcon( - icon: StreamSvgIcons.files, - size: 136, - color: StreamChatTheme.of(context).colorTheme.disabled, - ), - const SizedBox(height: 16), - Text( - AppLocalizations.of(context).noFiles, - style: TextStyle( - fontSize: 14, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis, - ), - ), - const SizedBox(height: 8), - Text( - AppLocalizations.of(context).filesAppearHere, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5), - ), - ), - ], - ), - ); - } - final media = {}; - - for (final item in items) { - item.message.attachments - .where((e) => e.type == 'file') - .forEach((e) { - media[e] = item.message; - }); - } - - return LazyLoadScrollView( - onEndOfPage: () async { - if (nextPageKey != null) { - controller.loadMore(nextPageKey); - } - }, - child: ListView.builder( - itemBuilder: (context, position) { - return Padding( - padding: const EdgeInsets.all(1), - child: Padding( - padding: const EdgeInsets.all(8), - child: StreamFileAttachment( - message: media.values.toList()[position], - file: media.keys.toList()[position], - ), - ), - ); - }, - itemCount: media.length, - ), - ); - }, - loading: () => const Center( - child: CircularProgressIndicator(), - ), - error: (_) => const Offstage(), - ); - }, - ), - ); - } - - @override - void dispose() { - controller.dispose(); - super.dispose(); - } - - @override - void initState() { - controller.doInitialLoad(); - super.initState(); - } -} diff --git a/sample_app/lib/pages/channel_list_page.dart b/sample_app/lib/pages/channel_list_page.dart deleted file mode 100644 index 358e204bdb..0000000000 --- a/sample_app/lib/pages/channel_list_page.dart +++ /dev/null @@ -1,304 +0,0 @@ -// ignore_for_file: deprecated_member_use - -import 'dart:async'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_app_badger/flutter_app_badger.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; -import 'package:go_router/go_router.dart'; -import 'package:provider/provider.dart'; -import 'package:sample_app/app.dart'; -import 'package:sample_app/pages/thread_list_page.dart'; -import 'package:sample_app/pages/user_mentions_page.dart'; -import 'package:sample_app/routes/routes.dart'; -import 'package:sample_app/state/init_data.dart'; -import 'package:sample_app/utils/app_config.dart'; -import 'package:sample_app/utils/localizations.dart'; -import 'package:sample_app/widgets/channel_list.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:streaming_shared_preferences/streaming_shared_preferences.dart'; - -class ChannelListPage extends StatefulWidget { - const ChannelListPage({ - super.key, - }); - - @override - State createState() => _ChannelListPageState(); -} - -class _ChannelListPageState extends State { - int _currentIndex = 0; - - bool _isSelected(int index) => _currentIndex == index; - - List get _navBarItems { - return [ - BottomNavigationBarItem( - icon: Stack( - clipBehavior: Clip.none, - children: [ - StreamSvgIcon( - icon: StreamSvgIcons.message, - color: _isSelected(0) - ? StreamChatTheme.of(context).colorTheme.textHighEmphasis - : Colors.grey, - ), - PositionedDirectional( - top: -4, - start: 12, - child: StreamUnreadIndicator(), - ), - ], - ), - label: AppLocalizations.of(context).chats, - ), - BottomNavigationBarItem( - icon: StreamSvgIcon( - icon: StreamSvgIcons.mentions, - color: _isSelected(1) - ? StreamChatTheme.of(context).colorTheme.textHighEmphasis - : Colors.grey, - ), - label: AppLocalizations.of(context).mentions, - ), - BottomNavigationBarItem( - icon: Stack( - clipBehavior: Clip.none, - children: [ - Icon( - Icons.message_outlined, - color: _isSelected(2) - ? StreamChatTheme.of(context).colorTheme.textHighEmphasis - : Colors.grey, - ), - PositionedDirectional( - top: -4, - start: 12, - child: StreamUnreadIndicator.threads(), - ), - ], - ), - label: 'Threads', - ), - ]; - } - - @override - Widget build(BuildContext context) { - final user = StreamChat.of(context).currentUser; - if (user == null) { - return const Offstage(); - } - return Scaffold( - backgroundColor: StreamChatTheme.of(context).colorTheme.appBg, - appBar: StreamChannelListHeader( - onNewChatButtonTap: () { - GoRouter.of(context).pushNamed(Routes.NEW_CHAT.name); - }, - preNavigationCallback: () => - FocusScope.of(context).requestFocus(FocusNode()), - ), - drawer: LeftDrawer( - user: user, - ), - drawerEdgeDragWidth: 50, - bottomNavigationBar: BottomNavigationBar( - backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg, - currentIndex: _currentIndex, - items: _navBarItems, - selectedLabelStyle: StreamChatTheme.of(context).textTheme.footnoteBold, - unselectedLabelStyle: - StreamChatTheme.of(context).textTheme.footnoteBold, - type: BottomNavigationBarType.fixed, - selectedItemColor: - StreamChatTheme.of(context).colorTheme.textHighEmphasis, - unselectedItemColor: Colors.grey, - onTap: (index) { - setState(() => _currentIndex = index); - }, - ), - body: IndexedStack( - index: _currentIndex, - children: const [ - ChannelList(), - UserMentionsPage(), - ThreadListPage(), - ], - ), - ); - } - - StreamSubscription? badgeListener; - - @override - void initState() { - if (!kIsWeb) { - badgeListener = StreamChat.of(context) - .client - .state - .totalUnreadCountStream - .listen((count) { - if (count > 0) { - FlutterAppBadger.updateBadgeCount(count); - } else { - FlutterAppBadger.removeBadge(); - } - }); - } - super.initState(); - } - - @override - void dispose() { - badgeListener?.cancel(); - super.dispose(); - } -} - -class LeftDrawer extends StatelessWidget { - const LeftDrawer({ - super.key, - required this.user, - }); - - final User user; - - @override - Widget build(BuildContext context) { - return Drawer( - child: ColoredBox( - color: StreamChatTheme.of(context).colorTheme.barsBg, - child: SafeArea( - child: Padding( - padding: EdgeInsets.only( - top: MediaQuery.of(context).viewPadding.top + 8, - ), - child: Column( - children: [ - Padding( - padding: const EdgeInsets.only( - bottom: 20, - left: 8, - ), - child: Row( - children: [ - StreamUserAvatar( - user: user, - showOnlineStatus: false, - constraints: - BoxConstraints.tight(const Size.fromRadius(20)), - ), - Padding( - padding: const EdgeInsets.only(left: 16), - child: Text( - user.name, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - ), - ], - ), - ), - ListTile( - leading: StreamSvgIcon( - icon: StreamSvgIcons.penWrite, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(.5), - ), - onTap: () { - Navigator.of(context).pop(); - GoRouter.of(context).pushNamed(Routes.NEW_CHAT.name); - }, - title: Text( - AppLocalizations.of(context).newDirectMessage, - style: const TextStyle( - fontSize: 14.5, - ), - ), - ), - ListTile( - leading: StreamSvgIcon( - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(.5), - icon: StreamSvgIcons.contacts, - ), - onTap: () { - Navigator.of(context).pop(); - GoRouter.of(context).pushNamed(Routes.NEW_GROUP_CHAT.name); - }, - title: Text( - AppLocalizations.of(context).newGroup, - style: const TextStyle( - fontSize: 14.5, - ), - ), - ), - Expanded( - child: Container( - alignment: Alignment.bottomCenter, - child: ListTile( - onTap: () async { - final client = StreamChat.of(context).client; - final router = GoRouter.of(context); - final initNotifier = context.read(); - - if (!kIsWeb) { - const secureStorage = FlutterSecureStorage(); - await secureStorage.deleteAll(); - } - - await client.disconnectUser(flushChatPersistence: true); - await client.dispose(); - initNotifier.initData = initNotifier.initData!.copyWith( - client: - buildStreamChatClient(kDefaultStreamApiKey)); - - router.goNamed(Routes.CHOOSE_USER.name); - }, - leading: StreamSvgIcon( - icon: StreamSvgIcons.user, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(.5), - ), - title: Text( - AppLocalizations.of(context).signOut, - style: const TextStyle( - fontSize: 14.5, - ), - ), - trailing: IconButton( - iconSize: 24, - icon: const StreamSvgIcon(icon: StreamSvgIcons.moon), - color: StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis, - onPressed: () async { - final theme = Theme.of(context); - final sp = await StreamingSharedPreferences.instance; - sp.setInt( - 'theme', - theme.brightness == Brightness.dark ? 1 : -1, - ); - }, - ), - ), - ), - ), - ], - ), - ), - ), - ), - ); - } -} diff --git a/sample_app/lib/pages/channel_media_display_screen.dart b/sample_app/lib/pages/channel_media_display_screen.dart deleted file mode 100644 index d7b7a472d1..0000000000 --- a/sample_app/lib/pages/channel_media_display_screen.dart +++ /dev/null @@ -1,223 +0,0 @@ -// ignore_for_file: deprecated_member_use - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:sample_app/routes/routes.dart'; -import 'package:sample_app/utils/localizations.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:video_player/video_player.dart'; - -class ChannelMediaDisplayScreen extends StatefulWidget { - const ChannelMediaDisplayScreen({ - super.key, - required this.messageTheme, - }); - final StreamMessageThemeData messageTheme; - - @override - State createState() => - _ChannelMediaDisplayScreenState(); -} - -class _ChannelMediaDisplayScreenState extends State { - final Map controllerCache = {}; - - late final controller = StreamMessageSearchListController( - client: StreamChat.of(context).client, - filter: Filter.in_( - 'cid', - [StreamChannel.of(context).channel.cid!], - ), - messageFilter: Filter.in_( - 'attachments.type', - const ['image', 'video'], - ), - sort: [ - const SortOption( - 'created_at', - direction: SortOption.ASC, - ), - ], - limit: 20, - ); - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg, - appBar: AppBar( - elevation: 1, - centerTitle: true, - title: Text( - AppLocalizations.of(context).photosAndVideos, - style: TextStyle( - color: StreamChatTheme.of(context).colorTheme.textHighEmphasis, - fontSize: 16, - ), - ), - leading: const StreamBackButton(), - backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg, - ), - body: ValueListenableBuilder( - valueListenable: controller, - builder: (BuildContext context, - PagedValue value, Widget? child) { - return value.when( - (items, nextPageKey, error) { - if (items.isEmpty) { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - StreamSvgIcon( - icon: StreamSvgIcons.pictures, - size: 136, - color: StreamChatTheme.of(context).colorTheme.disabled, - ), - const SizedBox(height: 16), - Text( - AppLocalizations.of(context).noMedia, - style: TextStyle( - fontSize: 14, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis, - ), - ), - const SizedBox(height: 8), - Text( - AppLocalizations.of(context) - .photosOrVideosWillAppearHere, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5), - ), - ), - ], - ), - ); - } - final media = <_AssetPackage>[]; - - for (final item in value.asSuccess.items) { - item.message.attachments - .where((e) => - (e.type == 'image' || e.type == 'video') && - e.ogScrapeUrl == null) - .forEach((e) { - VideoPlayerController? controller; - if (e.type == 'video') { - final cachedController = controllerCache[e.assetUrl]; - - if (cachedController == null) { - final url = Uri.parse(e.assetUrl!); - controller = VideoPlayerController.networkUrl(url); - controller.initialize(); - controllerCache[e.assetUrl] = controller; - } else { - controller = cachedController; - } - } - media.add(_AssetPackage(e, item.message, controller)); - }); - } - - return LazyLoadScrollView( - onEndOfPage: () async { - if (nextPageKey != null) { - controller.loadMore(nextPageKey); - } - }, - child: GridView.builder( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3), - itemBuilder: (context, position) { - final channel = StreamChannel.of(context).channel; - return Padding( - padding: const EdgeInsets.all(1), - child: InkWell( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => StreamChannel( - channel: channel, - child: StreamFullScreenMedia( - mediaAttachmentPackages: media - .map( - (e) => StreamAttachmentPackage( - attachment: e.attachment, - message: e.message, - ), - ) - .toList(), - startIndex: position, - userName: media[position].message.user!.name, - onShowMessage: (m, c) async { - final router = GoRouter.of(context); - if (channel.state == null) { - await channel.watch(); - } - router.pushNamed( - Routes.CHANNEL_PAGE.name, - pathParameters: - Routes.CHANNEL_PAGE.params(channel), - queryParameters: - Routes.CHANNEL_PAGE.queryParams(m), - ); - }, - ), - ), - ), - ); - }, - child: media[position].attachment.type == 'image' - ? IgnorePointer( - child: StreamImageAttachment( - image: media[position].attachment, - message: media[position].message, - // showTitle: false, - // messageTheme: widget.messageTheme, - ), - ) - : VideoPlayer(media[position].videoPlayer!), - ), - ); - }, - itemCount: media.length, - ), - ); - }, - loading: () => const Center( - child: CircularProgressIndicator(), - ), - error: (_) => const Offstage(), - ); - }, - ), - ); - } - - @override - void dispose() { - controller.dispose(); - super.dispose(); - } - - @override - void initState() { - controller.doInitialLoad(); - super.initState(); - } -} - -class _AssetPackage { - _AssetPackage(this.attachment, this.message, this.videoPlayer); - Attachment attachment; - Message message; - VideoPlayerController? videoPlayer; -} diff --git a/sample_app/lib/pages/channel_page.dart b/sample_app/lib/pages/channel_page.dart deleted file mode 100644 index d922372b89..0000000000 --- a/sample_app/lib/pages/channel_page.dart +++ /dev/null @@ -1,174 +0,0 @@ -// ignore_for_file: deprecated_member_use - -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:sample_app/pages/thread_page.dart'; -import 'package:sample_app/routes/routes.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -class ChannelPage extends StatefulWidget { - const ChannelPage({ - super.key, - this.initialScrollIndex, - this.initialAlignment, - this.highlightInitialMessage = false, - }); - final int? initialScrollIndex; - final double? initialAlignment; - final bool highlightInitialMessage; - - @override - State createState() => _ChannelPageState(); -} - -class _ChannelPageState extends State { - FocusNode? _focusNode; - final StreamMessageInputController _messageInputController = - StreamMessageInputController(); - - @override - void initState() { - _focusNode = FocusNode(); - super.initState(); - } - - @override - void dispose() { - _focusNode!.dispose(); - super.dispose(); - } - - void _reply(Message message) { - _messageInputController.quotedMessage = message; - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - _focusNode!.requestFocus(); - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: StreamChatTheme.of(context).colorTheme.appBg, - appBar: StreamChannelHeader( - showTypingIndicator: false, - onBackPressed: () => GoRouter.of(context).pop(), - onImageTap: () async { - final channel = StreamChannel.of(context).channel; - final router = GoRouter.of(context); - - if (channel.memberCount == 2 && channel.isDistinct) { - final currentUser = StreamChat.of(context).currentUser; - final otherUser = channel.state!.members.firstWhereOrNull( - (element) => element.user!.id != currentUser!.id, - ); - if (otherUser != null) { - router.pushNamed( - Routes.CHAT_INFO_SCREEN.name, - pathParameters: Routes.CHAT_INFO_SCREEN.params(channel), - extra: otherUser.user, - ); - } - } else { - GoRouter.of(context).pushNamed( - Routes.GROUP_INFO_SCREEN.name, - pathParameters: Routes.GROUP_INFO_SCREEN.params(channel), - ); - } - }, - ), - body: Column( - children: [ - Expanded( - child: Stack( - children: [ - StreamMessageListView( - initialScrollIndex: widget.initialScrollIndex, - initialAlignment: widget.initialAlignment, - highlightInitialMessage: widget.highlightInitialMessage, - //onMessageSwiped: _reply, - messageFilter: defaultFilter, - messageBuilder: (context, details, messages, defaultMessage) { - final router = GoRouter.of(context); - return defaultMessage.copyWith( - onReplyTap: _reply, - onShowMessage: (m, c) async { - final client = StreamChat.of(context).client; - final message = m; - final channel = client.channel( - c.type, - id: c.id, - ); - if (channel.state == null) { - await channel.watch(); - } - router.goNamed( - Routes.CHANNEL_PAGE.name, - pathParameters: Routes.CHANNEL_PAGE.params(channel), - queryParameters: - Routes.CHANNEL_PAGE.queryParams(message), - ); - }, - bottomRowBuilderWithDefaultWidget: ( - context, - message, - defaultWidget, - ) { - return defaultWidget.copyWith( - deletedBottomRowBuilder: (context, message) { - return const StreamVisibleFootnote(); - }, - ); - }, - ); - }, - threadBuilder: (_, parentMessage) { - return ThreadPage(parent: parentMessage!); - }, - ), - Positioned( - bottom: 0, - left: 0, - right: 0, - child: Container( - alignment: Alignment.centerLeft, - color: StreamChatTheme.of(context) - .colorTheme - .appBg - .withOpacity(.9), - child: StreamTypingIndicator( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 4, - ), - style: StreamChatTheme.of(context) - .textTheme - .footnote - .copyWith( - color: StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis), - ), - ), - ), - ], - ), - ), - StreamMessageInput( - focusNode: _focusNode, - messageInputController: _messageInputController, - onQuotedMessageCleared: _messageInputController.clearQuotedMessage, - ), - ], - ), - ); - } - - bool defaultFilter(Message m) { - final currentUser = StreamChat.of(context).currentUser; - final isMyMessage = m.user?.id == currentUser?.id; - final isDeletedOrShadowed = m.isDeleted == true || m.shadowed == true; - if (isDeletedOrShadowed && !isMyMessage) return false; - return true; - } -} diff --git a/sample_app/lib/pages/chat_info_screen.dart b/sample_app/lib/pages/chat_info_screen.dart deleted file mode 100644 index 6caacbdfc4..0000000000 --- a/sample_app/lib/pages/chat_info_screen.dart +++ /dev/null @@ -1,582 +0,0 @@ -// ignore_for_file: deprecated_member_use - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:sample_app/pages/channel_file_display_screen.dart'; -import 'package:sample_app/pages/channel_media_display_screen.dart'; -import 'package:sample_app/pages/pinned_messages_screen.dart'; -import 'package:sample_app/utils/localizations.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Detail screen for a 1:1 chat correspondence -class ChatInfoScreen extends StatefulWidget { - const ChatInfoScreen({ - super.key, - required this.messageTheme, - this.user, - }); - - /// User in consideration - final User? user; - - final StreamMessageThemeData messageTheme; - - @override - State createState() => _ChatInfoScreenState(); -} - -class _ChatInfoScreenState extends State { - ValueNotifier mutedBool = ValueNotifier(false); - - @override - void initState() { - super.initState(); - mutedBool = ValueNotifier(StreamChannel.of(context).channel.isMuted); - } - - @override - Widget build(BuildContext context) { - final channel = StreamChannel.of(context).channel; - return Scaffold( - backgroundColor: StreamChatTheme.of(context).colorTheme.appBg, - body: ListView( - children: [ - _buildUserHeader(), - Container( - height: 8, - color: StreamChatTheme.of(context).colorTheme.disabled, - ), - _buildOptionListTiles(), - Container( - height: 8, - color: StreamChatTheme.of(context).colorTheme.disabled, - ), - if (channel.ownCapabilities.contains(PermissionType.deleteChannel)) - _buildDeleteListTile(), - ], - ), - ); - } - - Widget _buildUserHeader() { - return Material( - color: StreamChatTheme.of(context).colorTheme.appBg, - child: SafeArea( - child: Stack( - children: [ - Column( - children: [ - Padding( - padding: const EdgeInsets.all(16), - child: StreamUserAvatar( - user: widget.user!, - constraints: const BoxConstraints.tightFor( - width: 72, - height: 72, - ), - borderRadius: BorderRadius.circular(36), - showOnlineStatus: false, - ), - ), - Text( - widget.user!.name, - style: const TextStyle( - fontSize: 16, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 7), - _buildConnectedTitleState(), - const SizedBox(height: 15), - StreamOptionListTile( - title: '@${widget.user!.id}', - tileColor: StreamChatTheme.of(context).colorTheme.appBg, - trailing: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: Text( - widget.user!.name, - style: TextStyle( - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5), - fontSize: 16), - ), - ), - onTap: () {}, - ), - ], - ), - const Positioned( - top: 0, - left: 0, - width: 58, - child: StreamBackButton(), - ), - ], - ), - ), - ); - } - - Widget _buildOptionListTiles() { - final channel = StreamChannel.of(context); - - return Column( - children: [ - StreamBuilder( - stream: StreamChannel.of(context).channel.isMutedStream, - builder: (context, snapshot) { - mutedBool.value = snapshot.data; - - return StreamOptionListTile( - tileColor: StreamChatTheme.of(context).colorTheme.appBg, - title: AppLocalizations.of(context).muteUser, - titleTextStyle: StreamChatTheme.of(context).textTheme.body, - leading: Padding( - padding: const EdgeInsets.symmetric(horizontal: 22), - child: StreamSvgIcon( - icon: StreamSvgIcons.mute, - size: 24, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5), - ), - ), - trailing: snapshot.data == null - ? const CircularProgressIndicator() - : ValueListenableBuilder( - valueListenable: mutedBool, - builder: (context, value, _) { - return CupertinoSwitch( - value: value!, - onChanged: (val) { - mutedBool.value = val; - - if (snapshot.data!) { - channel.channel.unmute(); - } else { - channel.channel.mute(); - } - }, - ); - }), - onTap: () {}, - ); - }), - StreamOptionListTile( - title: AppLocalizations.of(context).pinnedMessages, - tileColor: StreamChatTheme.of(context).colorTheme.appBg, - titleTextStyle: StreamChatTheme.of(context).textTheme.body, - leading: Padding( - padding: const EdgeInsets.symmetric(horizontal: 22), - child: StreamSvgIcon( - icon: StreamSvgIcons.pin, - size: 24, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5), - ), - ), - trailing: StreamSvgIcon( - icon: StreamSvgIcons.right, - color: StreamChatTheme.of(context).colorTheme.textLowEmphasis, - ), - onTap: () { - final channel = StreamChannel.of(context).channel; - - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => StreamChannel( - channel: channel, - child: const PinnedMessagesScreen(), - ), - ), - ); - }, - ), - StreamOptionListTile( - title: AppLocalizations.of(context).photosAndVideos, - tileColor: StreamChatTheme.of(context).colorTheme.appBg, - titleTextStyle: StreamChatTheme.of(context).textTheme.body, - leading: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: StreamSvgIcon( - icon: StreamSvgIcons.pictures, - size: 36, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5), - ), - ), - trailing: StreamSvgIcon( - icon: StreamSvgIcons.right, - color: StreamChatTheme.of(context).colorTheme.textLowEmphasis, - ), - onTap: () { - final channel = StreamChannel.of(context).channel; - - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => StreamChannel( - channel: channel, - child: ChannelMediaDisplayScreen( - messageTheme: widget.messageTheme, - ), - ), - ), - ); - }, - ), - StreamOptionListTile( - title: AppLocalizations.of(context).files, - tileColor: StreamChatTheme.of(context).colorTheme.appBg, - titleTextStyle: StreamChatTheme.of(context).textTheme.body, - leading: Padding( - padding: const EdgeInsets.symmetric(horizontal: 18), - child: StreamSvgIcon( - icon: StreamSvgIcons.files, - size: 32, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5), - ), - ), - trailing: StreamSvgIcon( - icon: StreamSvgIcons.right, - color: StreamChatTheme.of(context).colorTheme.textLowEmphasis, - ), - onTap: () { - final channel = StreamChannel.of(context).channel; - - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => StreamChannel( - channel: channel, - child: ChannelFileDisplayScreen( - messageTheme: widget.messageTheme, - ), - ), - ), - ); - }, - ), - StreamOptionListTile( - title: AppLocalizations.of(context).sharedGroups, - tileColor: StreamChatTheme.of(context).colorTheme.appBg, - titleTextStyle: StreamChatTheme.of(context).textTheme.body, - leading: Padding( - padding: const EdgeInsets.symmetric(horizontal: 22), - child: StreamSvgIcon( - icon: StreamSvgIcons.group, - size: 24, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5), - ), - ), - trailing: StreamSvgIcon( - icon: StreamSvgIcons.right, - color: StreamChatTheme.of(context).colorTheme.textLowEmphasis, - ), - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => _SharedGroupsScreen( - StreamChat.of(context).currentUser, widget.user))); - }, - ), - ], - ); - } - - Widget _buildDeleteListTile() { - return StreamOptionListTile( - title: 'Delete Conversation', - tileColor: StreamChatTheme.of(context).colorTheme.appBg, - titleTextStyle: StreamChatTheme.of(context).textTheme.body.copyWith( - color: StreamChatTheme.of(context).colorTheme.accentError, - ), - leading: Padding( - padding: const EdgeInsets.symmetric(horizontal: 22), - child: StreamSvgIcon( - icon: StreamSvgIcons.delete, - size: 24, - color: StreamChatTheme.of(context).colorTheme.accentError, - ), - ), - onTap: _showDeleteDialog, - titleColor: StreamChatTheme.of(context).colorTheme.accentError, - ); - } - - void _showDeleteDialog() async { - final streamChannel = StreamChannel.of(context); - final res = await showConfirmationBottomSheet( - context, - title: AppLocalizations.of(context).deleteConversationTitle, - okText: AppLocalizations.of(context).delete.toUpperCase(), - question: AppLocalizations.of(context).deleteConversationAreYouSure, - cancelText: AppLocalizations.of(context).cancel.toUpperCase(), - icon: StreamSvgIcon( - icon: StreamSvgIcons.delete, - color: StreamChatTheme.of(context).colorTheme.accentError, - ), - ); - final channel = streamChannel.channel; - if (res == true) { - await channel.delete().then((value) { - Navigator.pop(context); - Navigator.pop(context); - }); - } - } - - Widget _buildConnectedTitleState() { - late Text alternativeWidget; - - final otherMember = widget.user; - - if (otherMember != null) { - if (otherMember.online) { - alternativeWidget = Text( - AppLocalizations.of(context).online, - style: TextStyle( - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5)), - ); - } else { - alternativeWidget = Text( - '${AppLocalizations.of(context).lastSeen} ${Jiffy.parseFromDateTime(otherMember.lastActive!).fromNow()}', - style: TextStyle( - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5)), - ); - } - } - - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (widget.user!.online) - Material( - type: MaterialType.circle, - color: StreamChatTheme.of(context).colorTheme.barsBg, - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 8), - constraints: const BoxConstraints.tightFor( - width: 24, - height: 12, - ), - child: Material( - shape: const CircleBorder(), - color: StreamChatTheme.of(context).colorTheme.accentInfo, - ), - ), - ), - alternativeWidget, - if (widget.user!.online) - const SizedBox( - width: 24, - ), - ], - ); - } -} - -class _SharedGroupsScreen extends StatefulWidget { - const _SharedGroupsScreen(this.mainUser, this.otherUser); - final User? mainUser; - final User? otherUser; - - @override - __SharedGroupsScreenState createState() => __SharedGroupsScreenState(); -} - -class __SharedGroupsScreenState extends State<_SharedGroupsScreen> { - @override - Widget build(BuildContext context) { - final chat = StreamChat.of(context); - - return Scaffold( - backgroundColor: StreamChatTheme.of(context).colorTheme.appBg, - appBar: AppBar( - elevation: 1, - centerTitle: true, - title: Text( - AppLocalizations.of(context).sharedGroups, - style: TextStyle( - color: StreamChatTheme.of(context).colorTheme.textHighEmphasis, - fontSize: 16), - ), - leading: const StreamBackButton(), - backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg, - ), - body: StreamBuilder>( - stream: chat.client.queryChannels( - filter: Filter.and([ - Filter.in_('members', [widget.otherUser!.id]), - Filter.in_('members', [widget.mainUser!.id]), - ]), - ), - builder: (context, snapshot) { - if (!snapshot.hasData) { - return const Center( - child: CircularProgressIndicator(), - ); - } - - if (snapshot.data!.isEmpty) { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - StreamSvgIcon( - icon: StreamSvgIcons.message, - size: 136, - color: StreamChatTheme.of(context).colorTheme.disabled, - ), - const SizedBox(height: 16), - Text( - AppLocalizations.of(context).noSharedGroups, - style: TextStyle( - fontSize: 14, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis, - ), - ), - const SizedBox(height: 8), - Text( - AppLocalizations.of(context).groupSharedWithUserAppearHere, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5), - ), - ), - ], - ), - ); - } - - final channels = snapshot.data! - .where((c) => - c.state!.members.any((m) => - m.userId != widget.mainUser!.id && - m.userId != widget.otherUser!.id) || - !c.isDistinct) - .toList(); - - return ListView.builder( - itemCount: channels.length, - itemBuilder: (context, position) { - return StreamChannel( - channel: channels[position], - child: _buildListTile(channels[position]), - ); - }, - ); - }, - ), - ); - } - - Widget _buildListTile(Channel channel) { - final extraData = channel.extraData; - final members = channel.state!.members; - - const textStyle = TextStyle(fontSize: 14, fontWeight: FontWeight.bold); - - return SizedBox( - height: 64, - child: LayoutBuilder( - builder: (context, constraints) { - String? title; - if (extraData['name'] == null) { - final otherMembers = members.where((member) => - member.userId != StreamChat.of(context).currentUser!.id); - if (otherMembers.isNotEmpty) { - final maxWidth = constraints.maxWidth; - final maxChars = maxWidth / textStyle.fontSize!; - var currentChars = 0; - final currentMembers = []; - for (final element in otherMembers) { - final newLength = currentChars + element.user!.name.length; - if (newLength < maxChars) { - currentChars = newLength; - currentMembers.add(element); - } - } - - final exceedingMembers = - otherMembers.length - currentMembers.length; - title = - '${currentMembers.map((e) => e.user!.name).join(', ')} ${exceedingMembers > 0 ? '+ $exceedingMembers' : ''}'; - } else { - title = 'No title'; - } - } else { - title = extraData['name']! as String; - } - - return Column( - children: [ - Expanded( - child: Row( - children: [ - Padding( - padding: const EdgeInsets.all(8), - child: StreamChannelAvatar( - channel: channel, - constraints: - const BoxConstraints(maxWidth: 40, maxHeight: 40), - ), - ), - Expanded( - child: Text( - title, - style: textStyle, - )), - Padding( - padding: const EdgeInsets.all(8), - child: Text( - '${channel.memberCount} ${AppLocalizations.of(context).members.toLowerCase()}', - style: TextStyle( - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5)), - ), - ) - ], - ), - ), - Container( - height: 1, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(.08), - ), - ], - ); - }, - ), - ); - } -} diff --git a/sample_app/lib/pages/choose_user_page.dart b/sample_app/lib/pages/choose_user_page.dart deleted file mode 100644 index b2aa66391a..0000000000 --- a/sample_app/lib/pages/choose_user_page.dart +++ /dev/null @@ -1,205 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:go_router/go_router.dart'; -import 'package:provider/provider.dart'; -import 'package:sample_app/routes/routes.dart'; -import 'package:sample_app/state/init_data.dart'; -import 'package:sample_app/utils/app_config.dart'; -import 'package:sample_app/utils/localizations.dart'; -import 'package:sample_app/widgets/stream_version.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -const kStreamApiKey = 'STREAM_API_KEY'; -const kStreamUserId = 'STREAM_USER_ID'; -const kStreamToken = 'STREAM_TOKEN'; - -class ChooseUserPage extends StatelessWidget { - const ChooseUserPage({super.key}); - - @override - Widget build(BuildContext context) { - final users = defaultUsers; - - return Scaffold( - backgroundColor: StreamChatTheme.of(context).colorTheme.appBg, - body: SafeArea( - child: Column( - children: [ - Padding( - padding: const EdgeInsets.only( - top: 34, - bottom: 20, - ), - child: Center( - child: SvgPicture.asset( - 'assets/logo.svg', - height: 40, - colorFilter: ColorFilter.mode( - StreamChatTheme.of(context).colorTheme.accentPrimary, - BlendMode.srcIn, - ), - ), - ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 13), - child: Text( - AppLocalizations.of(context).welcomeToStreamChat, - style: StreamChatTheme.of(context).textTheme.title, - ), - ), - Text( - '${AppLocalizations.of(context).selectUserToTryFlutterSDK}:', - style: StreamChatTheme.of(context).textTheme.body, - ), - Expanded( - child: Padding( - padding: const EdgeInsets.only(top: 32), - child: ListView.separated( - separatorBuilder: (context, i) { - return Container( - height: 1, - color: StreamChatTheme.of(context).colorTheme.borders, - ); - }, - itemCount: users.length + 1, - itemBuilder: (context, i) { - return [ - ...users.entries.map((entry) { - final token = entry.key; - final user = entry.value; - return ListTile( - visualDensity: VisualDensity.compact, - onTap: () async { - showDialog( - barrierDismissible: false, - context: context, - barrierColor: StreamChatTheme.of(context) - .colorTheme - .overlay, - builder: (context) => Center( - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(16), - color: StreamChatTheme.of(context) - .colorTheme - .barsBg, - ), - height: 100, - width: 100, - child: const Center( - child: CircularProgressIndicator(), - ), - ), - ), - ); - - final client = - context.read().initData!.client; - - final router = GoRouter.of(context); - - await client.connectUser( - user, - token, - ); - - if (!kIsWeb) { - const secureStorage = FlutterSecureStorage(); - secureStorage.write( - key: kStreamApiKey, - value: kDefaultStreamApiKey, - ); - secureStorage.write( - key: kStreamUserId, - value: user.id, - ); - secureStorage.write( - key: kStreamToken, - value: token, - ); - } - - // Pop the progress dialog. - router.pop(); - router.replaceNamed(Routes.CHANNEL_LIST_PAGE.name); - }, - leading: StreamUserAvatar( - user: user, - constraints: BoxConstraints.tight( - const Size.fromRadius(20), - ), - ), - title: Text( - user.name, - style: - StreamChatTheme.of(context).textTheme.bodyBold, - ), - subtitle: Text( - AppLocalizations.of(context).streamTestAccount, - style: StreamChatTheme.of(context) - .textTheme - .footnote - .copyWith( - color: StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis, - ), - ), - trailing: StreamSvgIcon( - icon: StreamSvgIcons.arrowRight, - color: StreamChatTheme.of(context) - .colorTheme - .accentPrimary, - ), - ); - }), - ListTile( - onTap: () => GoRouter.of(context) - .pushNamed(Routes.ADVANCED_OPTIONS.name), - leading: CircleAvatar( - backgroundColor: - StreamChatTheme.of(context).colorTheme.borders, - child: StreamSvgIcon( - icon: StreamSvgIcons.settings, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis, - ), - ), - title: Text( - AppLocalizations.of(context).advancedOptions, - style: StreamChatTheme.of(context).textTheme.bodyBold, - ), - subtitle: Text( - AppLocalizations.of(context).customSettings, - style: StreamChatTheme.of(context) - .textTheme - .footnote - .copyWith( - color: StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis, - ), - ), - trailing: SvgPicture.asset( - 'assets/icon_arrow_right.svg', - height: 24, - width: 24, - clipBehavior: Clip.none, - ), - ), - ][i]; - }, - ), - ), - ), - const StreamVersion(), - ], - ), - ), - ); - } -} diff --git a/sample_app/lib/pages/group_chat_details_screen.dart b/sample_app/lib/pages/group_chat_details_screen.dart deleted file mode 100644 index 5df7ae5075..0000000000 --- a/sample_app/lib/pages/group_chat_details_screen.dart +++ /dev/null @@ -1,334 +0,0 @@ -// ignore_for_file: deprecated_member_use - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:sample_app/routes/routes.dart'; -import 'package:sample_app/state/new_group_chat_state.dart'; -import 'package:sample_app/utils/localizations.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -class GroupChatDetailsScreen extends StatefulWidget { - const GroupChatDetailsScreen({ - super.key, - required this.groupChatState, - }); - final NewGroupChatState groupChatState; - - @override - State createState() => _GroupChatDetailsScreenState(); -} - -class _GroupChatDetailsScreenState extends State { - late final TextEditingController _groupNameController = - TextEditingController()..addListener(_groupNameListener); - - bool _isGroupNameEmpty = true; - - int get _totalUsers => widget.groupChatState.users.length; - - void _groupNameListener() { - final name = _groupNameController.text; - if (mounted) { - setState(() { - _isGroupNameEmpty = name.isEmpty; - }); - } - } - - @override - void dispose() { - _groupNameController.removeListener(_groupNameListener); - _groupNameController.clear(); - _groupNameController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return WillPopScope( - onWillPop: () async { - GoRouter.of(context).pop(); - return false; - }, - child: Scaffold( - backgroundColor: StreamChatTheme.of(context).colorTheme.appBg, - appBar: AppBar( - elevation: 1, - backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg, - leading: const StreamBackButton(), - title: Text( - AppLocalizations.of(context).nameOfGroupChat, - style: TextStyle( - color: StreamChatTheme.of(context).colorTheme.textHighEmphasis, - fontSize: 16, - ), - ), - centerTitle: true, - bottom: PreferredSize( - preferredSize: const Size.fromHeight(kToolbarHeight), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 16), - child: Row( - children: [ - Text( - AppLocalizations.of(context).name.toUpperCase(), - style: TextStyle( - fontSize: 12, - color: StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis, - ), - ), - const SizedBox(width: 16), - Expanded( - child: TextField( - controller: _groupNameController, - decoration: InputDecoration( - isDense: true, - border: InputBorder.none, - focusedBorder: InputBorder.none, - enabledBorder: InputBorder.none, - errorBorder: InputBorder.none, - disabledBorder: InputBorder.none, - contentPadding: EdgeInsets.zero, - hintText: - AppLocalizations.of(context).chooseAGroupChatName, - hintStyle: TextStyle( - fontSize: 14, - color: StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis, - ), - ), - ), - ), - ], - ), - ), - ), - actions: [ - StreamNeumorphicButton( - child: IconButton( - iconSize: 24, - padding: EdgeInsets.zero, - color: _isGroupNameEmpty - ? StreamChatTheme.of(context).colorTheme.textLowEmphasis - : StreamChatTheme.of(context).colorTheme.accentPrimary, - icon: const StreamSvgIcon(icon: StreamSvgIcons.check), - onPressed: _isGroupNameEmpty - ? null - : () async { - try { - final groupName = _groupNameController.text; - final client = StreamChat.of(context).client; - final router = GoRouter.of(context); - final channel = client.channel('messaging', - id: const Uuid().v4(), - extraData: { - 'members': [ - client.state.currentUser!.id, - ...widget.groupChatState.users - .map((e) => e.id), - ], - 'name': groupName, - }); - await channel.watch(); - router.goNamed( - Routes.CHANNEL_PAGE.name, - pathParameters: Routes.CHANNEL_PAGE.params(channel), - ); - } catch (err) { - _showErrorAlert(); - } - }, - ), - ), - ], - ), - body: StreamConnectionStatusBuilder( - statusBuilder: (context, status) { - var statusString = ''; - var showStatus = true; - - switch (status) { - case ConnectionStatus.connected: - statusString = AppLocalizations.of(context).connected; - showStatus = false; - break; - case ConnectionStatus.connecting: - statusString = AppLocalizations.of(context).reconnecting; - break; - case ConnectionStatus.disconnected: - statusString = AppLocalizations.of(context).disconnected; - break; - } - return StreamInfoTile( - showMessage: showStatus, - tileAnchor: Alignment.topCenter, - childAnchor: Alignment.topCenter, - message: statusString, - child: Column( - children: [ - Container( - width: double.maxFinite, - decoration: BoxDecoration( - gradient: - StreamChatTheme.of(context).colorTheme.bgGradient, - ), - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 8, - ), - child: Text( - '$_totalUsers ${_totalUsers > 1 ? AppLocalizations.of(context).members : AppLocalizations.of(context).member}', - style: TextStyle( - color: StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis, - ), - ), - ), - ), - AnimatedBuilder( - animation: widget.groupChatState, - builder: (context, child) { - return Expanded( - child: GestureDetector( - behavior: HitTestBehavior.opaque, - onPanDown: (_) => FocusScope.of(context).unfocus(), - child: ListView.separated( - itemCount: widget.groupChatState.users.length + 1, - separatorBuilder: (_, __) => Container( - height: 1, - color: StreamChatTheme.of(context) - .colorTheme - .borders, - ), - itemBuilder: (_, index) { - if (index == - widget.groupChatState.users.length) { - return Container( - height: 1, - color: StreamChatTheme.of(context) - .colorTheme - .borders, - ); - } - final user = widget.groupChatState.users - .elementAt(index); - return ListTile( - key: ObjectKey(user), - leading: StreamUserAvatar( - user: user, - constraints: const BoxConstraints.tightFor( - width: 40, - height: 40, - ), - ), - title: Text( - user.name, - style: const TextStyle( - fontWeight: FontWeight.bold), - ), - contentPadding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 8, - ), - trailing: IconButton( - icon: Icon( - Icons.clear_rounded, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis, - ), - padding: EdgeInsets.zero, - splashRadius: 24, - onPressed: () { - widget.groupChatState.removeUser(user); - if (widget.groupChatState.users.isEmpty) { - GoRouter.of(context).pop(); - } - }, - ), - ); - }, - ), - ), - ); - }), - ], - ), - ); - }, - ), - ), - ); - } - - void _showErrorAlert() { - showModalBottomSheet( - backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg, - context: context, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16), - )), - builder: (context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox( - height: 26, - ), - StreamSvgIcon( - icon: StreamSvgIcons.error, - color: StreamChatTheme.of(context).colorTheme.accentError, - size: 24, - ), - const SizedBox( - height: 26, - ), - Text( - AppLocalizations.of(context).somethingWentWrongErrorMessage, - style: StreamChatTheme.of(context).textTheme.headlineBold, - ), - const SizedBox( - height: 7, - ), - Text(AppLocalizations.of(context).operationCouldNotBeCompleted), - const SizedBox( - height: 36, - ), - Container( - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(.08), - height: 1, - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextButton( - onPressed: GoRouter.of(context).pop, - child: Text( - AppLocalizations.of(context).ok, - style: StreamChatTheme.of(context) - .textTheme - .bodyBold - .copyWith( - color: StreamChatTheme.of(context) - .colorTheme - .accentPrimary), - ), - ), - ], - ), - ], - ); - }, - ); - } -} diff --git a/sample_app/lib/pages/group_info_screen.dart b/sample_app/lib/pages/group_info_screen.dart deleted file mode 100644 index 664e1fb667..0000000000 --- a/sample_app/lib/pages/group_info_screen.dart +++ /dev/null @@ -1,1088 +0,0 @@ -// ignore_for_file: deprecated_member_use - -import 'dart:async'; - -import 'package:collection/collection.dart' show IterableExtension; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:sample_app/pages/channel_file_display_screen.dart'; -import 'package:sample_app/pages/channel_media_display_screen.dart'; -import 'package:sample_app/pages/pinned_messages_screen.dart'; -import 'package:sample_app/routes/routes.dart'; -import 'package:sample_app/utils/localizations.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -class GroupInfoScreen extends StatefulWidget { - const GroupInfoScreen({ - super.key, - required this.messageTheme, - }); - final StreamMessageThemeData messageTheme; - - @override - State createState() => _GroupInfoScreenState(); -} - -class _GroupInfoScreenState extends State { - late final TextEditingController _nameController = - TextEditingController.fromValue( - TextEditingValue(text: (channel.extraData['name'] as String?) ?? ''), - ); - - late final TextEditingController _searchController = TextEditingController() - ..addListener(_userNameListener); - - String _userNameQuery = ''; - - Timer? _debounce; - Function? modalSetStateCallback; - - final FocusNode _focusNode = FocusNode(); - - bool listExpanded = false; - - late ValueNotifier mutedBool = ValueNotifier(channel.isMuted); - - late final channel = StreamChannel.of(context).channel; - - late StreamUserListController _userListController; - - void _userNameListener() { - if (_searchController.text == _userNameQuery) { - return; - } - if (_debounce?.isActive ?? false) _debounce!.cancel(); - _debounce = Timer(const Duration(milliseconds: 350), () { - if (mounted) { - _userNameQuery = _searchController.text; - _userListController.filter = Filter.and( - [ - if (_searchController.text.isNotEmpty) - Filter.autoComplete('name', _userNameQuery), - Filter.notIn('id', [ - StreamChat.of(context).currentUser!.id, - ...channel.state!.members - .map((e) => e.userId) - .whereType(), - ]), - ], - ); - _userListController.doInitialLoad(); - } - }); - } - - @override - void initState() { - super.initState(); - - _nameController.addListener(() { - setState(() {}); - }); - mutedBool = ValueNotifier(channel.isMuted); - } - - @override - void didChangeDependencies() { - _userListController = StreamUserListController( - client: StreamChat.of(context).client, - limit: 25, - filter: Filter.and( - [ - if (_searchController.text.isNotEmpty) - Filter.autoComplete('name', _userNameQuery), - Filter.notIn('id', [ - StreamChat.of(context).currentUser!.id, - ...channel.state!.members - .map((e) => e.userId) - .whereType(), - ]), - ], - ), - sort: [ - const SortOption( - 'name', - direction: 1, - ), - ], - ); - super.didChangeDependencies(); - } - - @override - void dispose() { - _nameController.dispose(); - _searchController.dispose(); - _userListController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return StreamBuilder>( - stream: channel.state!.membersStream, - builder: (context, snapshot) { - if (!snapshot.hasData) { - return ColoredBox( - color: StreamChatTheme.of(context).colorTheme.disabled, - child: const Center(child: CircularProgressIndicator()), - ); - } - - return Scaffold( - backgroundColor: StreamChatTheme.of(context).colorTheme.appBg, - appBar: AppBar( - elevation: 1, - toolbarHeight: 56, - backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg, - leading: const StreamBackButton(), - title: Column( - children: [ - StreamBuilder( - stream: channel.state?.channelStateStream, - builder: (context, state) { - if (!state.hasData) { - return Text( - AppLocalizations.of(context).loading, - style: TextStyle( - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis, - fontSize: 16, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ); - } - - return Text( - _getChannelName( - 2 * MediaQuery.of(context).size.width / 3, - members: snapshot.data, - extraData: state.data!.channel!.extraData, - maxFontSize: 16, - )!, - style: TextStyle( - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis, - fontSize: 16, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ); - }), - const SizedBox( - height: 3, - ), - Text( - '${channel.memberCount} ${AppLocalizations.of(context).members}, ${snapshot.data?.where((e) => e.user!.online).length ?? 0} ${AppLocalizations.of(context).online}', - style: TextStyle( - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5), - fontSize: 12, - ), - ), - ], - ), - centerTitle: true, - actions: [ - if (channel.ownCapabilities - .contains(PermissionType.updateChannelMembers)) - StreamNeumorphicButton( - child: InkWell( - onTap: () { - _buildAddUserModal(context); - }, - child: Padding( - padding: const EdgeInsets.all(8), - child: StreamSvgIcon( - icon: StreamSvgIcons.userAdd, - color: StreamChatTheme.of(context) - .colorTheme - .accentPrimary), - ), - ), - ), - ], - ), - body: ListView( - children: [ - _buildMembers(snapshot.data!), - Container( - height: 8, - color: StreamChatTheme.of(context).colorTheme.disabled, - ), - if (channel.ownCapabilities - .contains(PermissionType.updateChannel)) - _buildNameTile(), - _buildOptionListTiles(), - ], - ), - ); - }); - } - - Widget _buildMembers(List members) { - final groupMembers = members - ..sort((prev, curr) { - if (curr.userId == channel.createdBy?.id) return 1; - return 0; - }); - - int groupMembersLength; - - if (listExpanded) { - groupMembersLength = groupMembers.length; - } else { - groupMembersLength = groupMembers.length > 6 ? 6 : groupMembers.length; - } - - return Column( - children: [ - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: groupMembersLength, - itemBuilder: (context, index) { - final member = groupMembers[index]; - return Material( - color: StreamChatTheme.of(context).colorTheme.appBg, - child: InkWell( - onTap: () { - final userMember = groupMembers.firstWhereOrNull( - (e) => e.user!.id == StreamChat.of(context).currentUser!.id, - ); - _showUserInfoModal( - member.user, userMember?.userId == channel.createdBy?.id); - }, - child: SizedBox( - height: 65, - child: Column( - children: [ - Row( - children: [ - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 12, - ), - child: StreamUserAvatar( - user: member.user!, - constraints: const BoxConstraints.tightFor( - height: 40, - width: 40, - ), - ), - ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - member.user!.name, - style: const TextStyle( - fontWeight: FontWeight.bold), - ), - const SizedBox( - height: 1, - ), - Text( - _getLastSeen(member.user!), - style: TextStyle( - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5)), - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.all(8), - child: Text( - member.userId == channel.createdBy?.id - ? AppLocalizations.of(context).owner - : '', - style: TextStyle( - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5)), - ), - ), - ], - ), - Container( - height: 1, - color: StreamChatTheme.of(context).colorTheme.disabled, - ), - ], - ), - ), - ), - ); - }, - ), - if (groupMembersLength != groupMembers.length) - InkWell( - onTap: () { - setState(() { - listExpanded = true; - }); - }, - child: Material( - color: StreamChatTheme.of(context).colorTheme.appBg, - child: SizedBox( - height: 65, - child: Column( - children: [ - Expanded( - child: Row( - children: [ - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 21, vertical: 12), - child: StreamSvgIcon( - icon: StreamSvgIcons.down, - color: StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis, - ), - ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - '${members.length - groupMembersLength} ${AppLocalizations.of(context).more}', - style: TextStyle( - color: StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis), - ), - ], - ), - ), - ], - ), - ), - Container( - height: 1, - color: StreamChatTheme.of(context).colorTheme.disabled, - ), - ], - ), - ), - ), - ), - ], - ); - } - - Widget _buildNameTile() { - final channelName = (channel.extraData['name'] as String?) ?? ''; - - return Material( - color: StreamChatTheme.of(context).colorTheme.appBg, - child: Container( - height: 56, - alignment: Alignment.center, - child: Row( - children: [ - Padding( - padding: const EdgeInsets.all(7), - child: Text( - AppLocalizations.of(context).name.toUpperCase(), - style: StreamChatTheme.of(context).textTheme.footnote.copyWith( - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5)), - ), - ), - const SizedBox( - width: 7, - ), - Expanded( - child: TextField( - enabled: channel.ownCapabilities - .contains(PermissionType.updateChannel), - focusNode: _focusNode, - controller: _nameController, - cursorColor: - StreamChatTheme.of(context).colorTheme.textHighEmphasis, - decoration: InputDecoration.collapsed( - hintText: AppLocalizations.of(context).addAGroupName, - hintStyle: StreamChatTheme.of(context) - .textTheme - .bodyBold - .copyWith( - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5))), - style: const TextStyle( - fontWeight: FontWeight.bold, - height: 0.82, - ), - ), - ), - if (channelName != _nameController.text.trim()) - Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - icon: const StreamSvgIcon(icon: StreamSvgIcons.closeSmall), - onPressed: () { - setState(() { - _nameController.text = _getChannelName( - 2 * MediaQuery.of(context).size.width / 3, - members: channel.state!.members, - extraData: channel.extraData, - maxFontSize: 16, - )!; - _focusNode.unfocus(); - }); - }, - ), - IconButton( - color: StreamChatTheme.of(context).colorTheme.accentPrimary, - icon: const StreamSvgIcon(icon: StreamSvgIcons.check), - onPressed: () { - try { - channel.update({ - 'name': _nameController.text.trim(), - }); - } catch (_) { - setState(() { - _nameController.text = channelName; - _focusNode.unfocus(); - }); - } - }, - ), - ], - ), - ], - ), - ), - ); - } - - Widget _buildOptionListTiles() { - return Column( - children: [ - if (channel.ownCapabilities.contains(PermissionType.muteChannel)) - StreamBuilder( - stream: channel.isMutedStream, - builder: (context, snapshot) { - mutedBool.value = snapshot.data; - - return StreamOptionListTile( - tileColor: StreamChatTheme.of(context).colorTheme.appBg, - separatorColor: - StreamChatTheme.of(context).colorTheme.disabled, - title: AppLocalizations.of(context).muteGroup, - titleTextStyle: StreamChatTheme.of(context).textTheme.body, - leading: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: StreamSvgIcon( - icon: StreamSvgIcons.mute, - size: 24, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5), - ), - ), - trailing: snapshot.data == null - ? const CircularProgressIndicator() - : ValueListenableBuilder( - valueListenable: mutedBool, - builder: (context, value, _) { - return CupertinoSwitch( - value: value!, - onChanged: (val) { - mutedBool.value = val; - - if (snapshot.data!) { - channel.unmute(); - } else { - channel.mute(); - } - }, - ); - }), - onTap: () {}, - ); - }), - StreamOptionListTile( - title: AppLocalizations.of(context).pinnedMessages, - tileColor: StreamChatTheme.of(context).colorTheme.appBg, - titleTextStyle: StreamChatTheme.of(context).textTheme.body, - leading: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: StreamSvgIcon( - icon: StreamSvgIcons.pin, - size: 24, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5), - ), - ), - trailing: StreamSvgIcon( - icon: StreamSvgIcons.right, - color: StreamChatTheme.of(context).colorTheme.textLowEmphasis, - ), - onTap: () { - final channel = StreamChannel.of(context).channel; - - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => StreamChannel( - channel: channel, - child: const PinnedMessagesScreen(), - ), - ), - ); - }, - ), - StreamOptionListTile( - tileColor: StreamChatTheme.of(context).colorTheme.appBg, - separatorColor: StreamChatTheme.of(context).colorTheme.disabled, - title: AppLocalizations.of(context).photosAndVideos, - titleTextStyle: StreamChatTheme.of(context).textTheme.body, - leading: Padding( - padding: const EdgeInsets.symmetric(horizontal: 12), - child: StreamSvgIcon( - icon: StreamSvgIcons.pictures, - size: 32, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5), - ), - ), - trailing: StreamSvgIcon( - icon: StreamSvgIcons.right, - color: StreamChatTheme.of(context).colorTheme.textLowEmphasis, - ), - onTap: () { - final channel = StreamChannel.of(context).channel; - - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => StreamChannel( - channel: channel, - child: ChannelMediaDisplayScreen( - messageTheme: widget.messageTheme, - ), - ), - ), - ); - }, - ), - StreamOptionListTile( - tileColor: StreamChatTheme.of(context).colorTheme.appBg, - separatorColor: StreamChatTheme.of(context).colorTheme.disabled, - title: AppLocalizations.of(context).files, - titleTextStyle: StreamChatTheme.of(context).textTheme.body, - leading: Padding( - padding: const EdgeInsets.symmetric(horizontal: 12), - child: StreamSvgIcon( - icon: StreamSvgIcons.files, - size: 32, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5), - ), - ), - trailing: StreamSvgIcon( - icon: StreamSvgIcons.right, - color: StreamChatTheme.of(context).colorTheme.textLowEmphasis, - ), - onTap: () { - final channel = StreamChannel.of(context).channel; - - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => StreamChannel( - channel: channel, - child: ChannelFileDisplayScreen( - messageTheme: widget.messageTheme, - ), - ), - ), - ); - }, - ), - if (!channel.isDistinct && - channel.ownCapabilities.contains(PermissionType.leaveChannel)) - StreamOptionListTile( - tileColor: StreamChatTheme.of(context).colorTheme.appBg, - separatorColor: StreamChatTheme.of(context).colorTheme.disabled, - title: AppLocalizations.of(context).leaveGroup, - titleTextStyle: StreamChatTheme.of(context).textTheme.body, - leading: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: StreamSvgIcon( - icon: StreamSvgIcons.userRemove, - size: 24, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5), - ), - ), - trailing: const SizedBox( - height: 24, - width: 24, - ), - onTap: () async { - final streamChannel = StreamChannel.of(context); - final streamChat = StreamChat.of(context); - final router = GoRouter.of(context); - final res = await showConfirmationBottomSheet( - context, - title: AppLocalizations.of(context).leaveConversation, - okText: AppLocalizations.of(context).leave.toUpperCase(), - question: - AppLocalizations.of(context).leaveConversationAreYouSure, - cancelText: AppLocalizations.of(context).cancel.toUpperCase(), - icon: StreamSvgIcon( - icon: StreamSvgIcons.userRemove, - color: StreamChatTheme.of(context).colorTheme.accentError, - ), - ); - if (res == true) { - final channel = streamChannel.channel; - await channel.removeMembers([streamChat.currentUser!.id]); - router.pop(); - } - }, - ), - ], - ); - } - - void _buildAddUserModal(context) { - showDialog( - useRootNavigator: false, - context: context, - barrierColor: StreamChatTheme.of(context).colorTheme.overlay, - builder: (context) { - return Padding( - padding: const EdgeInsets.only(top: 16, left: 8, right: 8), - child: Material( - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16), - ), - clipBehavior: Clip.antiAlias, - child: Scaffold( - body: Column( - children: [ - Padding( - padding: const EdgeInsets.all(16), - child: _buildTextInputSection(), - ), - Expanded( - child: StreamUserGridView( - controller: _userListController, - onUserTap: (user) async { - _searchController.clear(); - final navigator = Navigator.of(context); - - await channel.addMembers([user.id]); - navigator.pop(); - setState(() {}); - }, - emptyBuilder: (_) { - return LayoutBuilder( - builder: (context, viewportConstraints) { - return SingleChildScrollView( - physics: const AlwaysScrollableScrollPhysics(), - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: viewportConstraints.maxHeight, - ), - child: Center( - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(24), - child: StreamSvgIcon( - icon: StreamSvgIcons.search, - size: 96, - color: StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis, - ), - ), - Text(AppLocalizations.of(context) - .noUserMatchesTheseKeywords), - ], - ), - ), - ), - ); - }, - ); - }, - ), - ), - ], - ), - ), - ), - ); - }, - ).whenComplete(() { - _searchController.clear(); - }); - } - - Widget _buildTextInputSection() { - final theme = StreamChatTheme.of(context); - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: SizedBox( - height: 36, - child: TextField( - controller: _searchController, - cursorColor: theme.colorTheme.textHighEmphasis, - autofocus: true, - decoration: InputDecoration( - hintText: AppLocalizations.of(context).search, - hintStyle: theme.textTheme.body.copyWith( - color: theme.colorTheme.textLowEmphasis, - ), - prefixIconConstraints: BoxConstraints.tight(const Size(40, 24)), - prefixIcon: StreamSvgIcon( - icon: StreamSvgIcons.search, - color: theme.colorTheme.textHighEmphasis, - size: 24, - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(24), - borderSide: BorderSide( - color: theme.colorTheme.borders, - ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(24), - borderSide: BorderSide( - color: theme.colorTheme.borders, - )), - contentPadding: EdgeInsets.zero, - ), - ), - ), - ), - IconButton( - icon: const StreamSvgIcon(icon: StreamSvgIcons.closeSmall), - color: theme.colorTheme.textHighEmphasis, - onPressed: () => Navigator.pop(context), - ) - ], - ); - } - - void _showUserInfoModal(User? user, bool isUserAdmin) { - final color = StreamChatTheme.of(context).colorTheme.barsBg; - - showModalBottomSheet( - context: context, - clipBehavior: Clip.antiAlias, - isScrollControlled: true, - backgroundColor: color, - builder: (context) { - return SafeArea( - child: StreamChannel( - channel: channel, - child: Material( - color: color, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox( - height: 24, - ), - Center( - child: Text( - user!.name, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - ), - const SizedBox( - height: 5, - ), - _buildConnectedTitleState(user)!, - Center( - child: Padding( - padding: const EdgeInsets.all(16), - child: StreamUserAvatar( - user: user, - constraints: const BoxConstraints.tightFor( - height: 64, - width: 64, - ), - borderRadius: BorderRadius.circular(32), - ), - ), - ), - if (StreamChat.of(context).currentUser!.id != user.id) - _buildModalListTile( - context, - StreamSvgIcon( - color: StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis, - size: 24, - icon: StreamSvgIcons.user, - ), - AppLocalizations.of(context).viewInfo, - () async { - final client = StreamChat.of(context).client; - final router = GoRouter.of(context); - - final c = client.channel('messaging', extraData: { - 'members': [ - user.id, - StreamChat.of(context).currentUser!.id, - ], - }); - - await c.watch(); - - router.pushNamed( - Routes.CHAT_INFO_SCREEN.name, - pathParameters: Routes.CHAT_INFO_SCREEN.params(c), - extra: user, - ); - }, - ), - if (StreamChat.of(context).currentUser!.id != user.id) - _buildModalListTile( - context, - StreamSvgIcon( - color: StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis, - size: 24, - icon: StreamSvgIcons.message, - ), - AppLocalizations.of(context).message, - () async { - final client = StreamChat.of(context).client; - final router = GoRouter.of(context); - - final c = client.channel('messaging', extraData: { - 'members': [ - user.id, - StreamChat.of(context).currentUser!.id, - ], - }); - - await c.watch(); - - router.pushNamed( - Routes.CHANNEL_PAGE.name, - pathParameters: Routes.CHANNEL_PAGE.params(c), - ); - }, - ), - if (!channel.isDistinct && - StreamChat.of(context).currentUser!.id != user.id && - isUserAdmin) - _buildModalListTile( - context, - StreamSvgIcon( - color: StreamChatTheme.of(context) - .colorTheme - .accentError, - size: 24, - icon: StreamSvgIcons.userRemove, - ), - AppLocalizations.of(context).removeFromGroup, () async { - final router = GoRouter.of(context); - final res = await showConfirmationBottomSheet( - context, - title: AppLocalizations.of(context).removeMember, - okText: - AppLocalizations.of(context).remove.toUpperCase(), - question: - AppLocalizations.of(context).removeMemberAreYouSure, - cancelText: - AppLocalizations.of(context).cancel.toUpperCase(), - ); - - if (res == true) { - await channel.removeMembers([user.id]); - } - router.pop(); - }, - color: - StreamChatTheme.of(context).colorTheme.accentError), - _buildModalListTile( - context, - StreamSvgIcon( - color: StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis, - size: 24, - icon: StreamSvgIcons.closeSmall, - ), - AppLocalizations.of(context).cancel, - () { - Navigator.pop(context); - }, - ), - ], - ), - ), - ), - ); - }, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16), - ), - ), - ); - } - - Widget? _buildConnectedTitleState(User? user) { - late Text alternativeWidget; - - final otherMember = user; - - if (otherMember != null) { - if (otherMember.online) { - alternativeWidget = Text( - AppLocalizations.of(context).online, - style: TextStyle( - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5)), - ); - } else { - alternativeWidget = Text( - '${AppLocalizations.of(context).lastSeen} ${Jiffy.parseFromDateTime(otherMember.lastActive!).fromNow()}', - style: TextStyle( - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5)), - ); - } - } - - return alternativeWidget; - } - - Widget _buildModalListTile( - BuildContext context, Widget leading, String title, VoidCallback onTap, - {Color? color}) { - color ??= StreamChatTheme.of(context).colorTheme.textHighEmphasis; - - return Material( - color: StreamChatTheme.of(context).colorTheme.barsBg, - child: InkWell( - onTap: onTap, - child: Column( - children: [ - Container( - height: 1, - color: StreamChatTheme.of(context).colorTheme.disabled, - ), - SizedBox( - height: 64, - child: Row( - children: [ - Padding( - padding: const EdgeInsets.all(16), - child: leading, - ), - Expanded( - child: Text( - title, - style: - TextStyle(color: color, fontWeight: FontWeight.bold), - ), - ) - ], - ), - ), - ], - ), - ), - ); - } - - String? _getChannelName( - double width, { - List? members, - required Map extraData, - double? maxFontSize, - }) { - String? title; - final client = StreamChat.of(context); - if (extraData['name'] == null) { - final otherMembers = - members!.where((member) => member.user!.id != client.currentUser!.id); - if (otherMembers.isNotEmpty) { - final maxWidth = width; - final maxChars = maxWidth / maxFontSize!; - var currentChars = 0; - final currentMembers = []; - for (final element in otherMembers) { - final newLength = currentChars + element.user!.name.length; - if (newLength < maxChars) { - currentChars = newLength; - currentMembers.add(element); - } - } - - final exceedingMembers = otherMembers.length - currentMembers.length; - title = - '${currentMembers.map((e) => e.user!.name).join(', ')} ${exceedingMembers > 0 ? '+ $exceedingMembers' : ''}'; - } else { - title = AppLocalizations.of(context).noTitle; - } - } else { - title = extraData['name']; - } - return title; - } - - String _getLastSeen(User user) { - if (user.online) { - return AppLocalizations.of(context).online; - } else { - if (user.lastActive == null) { - return ''; - } - - return '${AppLocalizations.of(context).lastSeen} ${Jiffy.parseFromDateTime(user.lastActive!).fromNow()}'; - } - } -} diff --git a/sample_app/lib/pages/new_chat_screen.dart b/sample_app/lib/pages/new_chat_screen.dart deleted file mode 100644 index dfa5daaaf0..0000000000 --- a/sample_app/lib/pages/new_chat_screen.dart +++ /dev/null @@ -1,426 +0,0 @@ -// ignore_for_file: deprecated_member_use - -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:sample_app/routes/routes.dart'; -import 'package:sample_app/utils/localizations.dart'; -import 'package:sample_app/widgets/chips_input_text_field.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -class NewChatScreen extends StatefulWidget { - const NewChatScreen({super.key}); - - @override - State createState() => _NewChatScreenState(); -} - -class _NewChatScreenState extends State { - final _chipInputTextFieldStateKey = - GlobalKey>(); - - late TextEditingController _controller; - - late final userListController = StreamUserListController( - client: StreamChat.of(context).client, - limit: 25, - filter: Filter.and([ - Filter.notEqual('id', StreamChat.of(context).currentUser!.id), - ]), - sort: [ - const SortOption( - 'name', - direction: 1, - ), - ], - ); - - ChipInputTextFieldState? get _chipInputTextFieldState => - _chipInputTextFieldStateKey.currentState; - - String _userNameQuery = ''; - - final _selectedUsers = {}; - - final _searchFocusNode = FocusNode(); - final _messageInputFocusNode = FocusNode(); - - bool _isSearchActive = false; - - Channel? channel; - - Timer? _debounce; - - bool _showUserList = true; - - void _userNameListener() { - if (_debounce?.isActive ?? false) _debounce!.cancel(); - _debounce = Timer(const Duration(milliseconds: 350), () { - if (mounted) { - setState(() { - _userNameQuery = _controller.text; - _isSearchActive = _userNameQuery.isNotEmpty; - }); - - userListController.filter = Filter.and([ - if (_userNameQuery.isNotEmpty) - Filter.autoComplete('name', _userNameQuery), - Filter.notEqual('id', StreamChat.of(context).currentUser!.id), - ]); - userListController.doInitialLoad(); - } - }); - } - - @override - void initState() { - super.initState(); - channel = StreamChat.of(context).client.channel('messaging'); - _controller = TextEditingController()..addListener(_userNameListener); - - _searchFocusNode.addListener(() async { - if (_searchFocusNode.hasFocus && !_showUserList) { - setState(() { - _showUserList = true; - }); - } - }); - - _messageInputFocusNode.addListener(() async { - if (_messageInputFocusNode.hasFocus && _selectedUsers.isNotEmpty) { - final chatState = StreamChat.of(context); - - final res = await chatState.client.queryChannelsOnline( - state: false, - watch: false, - filter: Filter.raw(value: { - 'members': [ - ..._selectedUsers.map((e) => e.id), - chatState.currentUser!.id, - ], - 'distinct': true, - }), - messageLimit: 0, - paginationParams: const PaginationParams( - limit: 1, - ), - ); - - final channelExisted = res.length == 1; - if (channelExisted) { - channel = res.first; - await channel!.watch(); - } else { - channel = chatState.client.channel( - 'messaging', - extraData: { - 'members': [ - ..._selectedUsers.map((e) => e.id), - chatState.currentUser!.id, - ], - }, - ); - } - - setState(() { - _showUserList = false; - }); - } - }); - } - - @override - void dispose() { - _searchFocusNode.dispose(); - _messageInputFocusNode.dispose(); - _controller.clear(); - _controller.removeListener(_userNameListener); - _controller.dispose(); - userListController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: StreamChatTheme.of(context).colorTheme.appBg, - appBar: AppBar( - elevation: 0, - backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg, - leading: const StreamBackButton(), - title: Text( - AppLocalizations.of(context).newChat, - style: StreamChatTheme.of(context).textTheme.headlineBold.copyWith( - color: StreamChatTheme.of(context).colorTheme.textHighEmphasis), - ), - centerTitle: true, - ), - body: StreamConnectionStatusBuilder( - statusBuilder: (context, status) { - var statusString = ''; - var showStatus = true; - - switch (status) { - case ConnectionStatus.connected: - statusString = AppLocalizations.of(context).connected; - showStatus = false; - break; - case ConnectionStatus.connecting: - statusString = AppLocalizations.of(context).reconnecting; - break; - case ConnectionStatus.disconnected: - statusString = AppLocalizations.of(context).disconnected; - break; - } - return StreamInfoTile( - showMessage: showStatus, - tileAnchor: Alignment.topCenter, - childAnchor: Alignment.topCenter, - message: statusString, - child: StreamChannel( - showLoading: false, - channel: channel!, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ChipsInputTextField( - key: _chipInputTextFieldStateKey, - controller: _controller, - focusNode: _searchFocusNode, - hint: AppLocalizations.of(context).typeANameHint, - chipBuilder: (context, user) { - return GestureDetector( - onTap: () { - _chipInputTextFieldState?.removeItem(user); - _searchFocusNode.requestFocus(); - }, - child: Stack( - alignment: AlignmentDirectional.centerStart, - children: [ - Container( - decoration: BoxDecoration( - color: StreamChatTheme.of(context) - .colorTheme - .disabled, - borderRadius: BorderRadius.circular(12), - ), - padding: const EdgeInsets.only(left: 24), - child: Padding( - padding: const EdgeInsets.fromLTRB(8, 4, 12, 4), - child: Text( - user.name, - maxLines: 1, - style: TextStyle( - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis, - ), - ), - ), - ), - Container( - foregroundDecoration: BoxDecoration( - color: StreamChatTheme.of(context) - .colorTheme - .overlay, - shape: BoxShape.circle, - ), - child: StreamUserAvatar( - showOnlineStatus: false, - user: user, - constraints: const BoxConstraints.tightFor( - height: 24, - width: 24, - ), - ), - ), - const StreamSvgIcon(icon: StreamSvgIcons.close), - ], - ), - ); - }, - onChipAdded: (user) { - setState(() => _selectedUsers.add(user)); - }, - onChipRemoved: (user) { - setState(() => _selectedUsers.remove(user)); - }, - ), - if (!_isSearchActive && !_selectedUsers.isNotEmpty) - InkWell( - onTap: () { - GoRouter.of(context).pushNamed( - Routes.NEW_GROUP_CHAT.name, - ); - }, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: Row( - children: [ - StreamNeumorphicButton( - child: Center( - child: StreamSvgIcon( - color: StreamChatTheme.of(context) - .colorTheme - .accentPrimary, - size: 24, - icon: StreamSvgIcons.contacts, - ), - ), - ), - const SizedBox(width: 8), - Text( - AppLocalizations.of(context).createAGroup, - style: StreamChatTheme.of(context) - .textTheme - .bodyBold, - ), - ], - ), - ), - ), - if (_showUserList) - Container( - width: double.maxFinite, - decoration: BoxDecoration( - gradient: - StreamChatTheme.of(context).colorTheme.bgGradient, - ), - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 8, - ), - child: Text( - _isSearchActive - ? '${AppLocalizations.of(context).matchesFor} "$_userNameQuery"' - : AppLocalizations.of(context).onThePlatorm, - style: StreamChatTheme.of(context) - .textTheme - .footnote - .copyWith( - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(.5))), - ), - ), - Expanded( - child: _showUserList - ? GestureDetector( - behavior: HitTestBehavior.opaque, - onPanDown: (_) => FocusScope.of(context).unfocus(), - child: StreamUserListView( - controller: userListController, - onUserTap: (user) { - _controller.clear(); - if (!_selectedUsers.contains(user)) { - _chipInputTextFieldState - ?..addItem(user) - ..pauseItemAddition(); - } else { - _chipInputTextFieldState!.removeItem(user); - } - }, - itemBuilder: ( - context, - users, - index, - defaultWidget, - ) { - return defaultWidget.copyWith( - selected: - _selectedUsers.contains(users[index]), - ); - }, - emptyBuilder: (_) { - return LayoutBuilder( - builder: (context, viewportConstraints) { - return SingleChildScrollView( - physics: - const AlwaysScrollableScrollPhysics(), - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: - viewportConstraints.maxHeight, - ), - child: Center( - child: Column( - children: [ - const Padding( - padding: EdgeInsets.all(24), - child: StreamSvgIcon( - icon: StreamSvgIcons.search, - size: 96, - color: Colors.grey, - ), - ), - Text( - AppLocalizations.of(context) - .noUserMatchesTheseKeywords, - style: StreamChatTheme.of( - context) - .textTheme - .footnote - .copyWith( - color: StreamChatTheme - .of(context) - .colorTheme - .textHighEmphasis - .withOpacity(.5)), - ), - ], - ), - ), - ), - ); - }, - ); - }, - ), - ) - : FutureBuilder( - future: channel!.initialized, - builder: (context, snapshot) { - if (snapshot.data == true) { - return const StreamMessageListView(); - } - - return Center( - child: Text( - AppLocalizations.of(context).noChatsHereYet, - style: TextStyle( - fontSize: 12, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(.5), - ), - ), - ); - }, - ), - ), - StreamMessageInput( - focusNode: _messageInputFocusNode, - preMessageSending: (message) async { - await channel!.watch(); - return message; - }, - onMessageSent: (m) { - GoRouter.of(context).goNamed( - Routes.CHANNEL_PAGE.name, - pathParameters: Routes.CHANNEL_PAGE.params(channel!), - ); - }, - ), - ], - ), - ), - ); - }, - ), - ); - } -} diff --git a/sample_app/lib/pages/new_group_chat_screen.dart b/sample_app/lib/pages/new_group_chat_screen.dart deleted file mode 100644 index c30f9c0bd8..0000000000 --- a/sample_app/lib/pages/new_group_chat_screen.dart +++ /dev/null @@ -1,336 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:sample_app/routes/routes.dart'; -import 'package:sample_app/state/new_group_chat_state.dart'; -import 'package:sample_app/utils/localizations.dart'; -import 'package:sample_app/widgets/search_text_field.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -class NewGroupChatScreen extends StatefulWidget { - const NewGroupChatScreen({super.key}); - - @override - State createState() => _NewGroupChatScreenState(); -} - -class _NewGroupChatScreenState extends State { - late final TextEditingController _controller = TextEditingController() - ..addListener(_userNameListener); - - String _userNameQuery = ''; - - final groupChatState = NewGroupChatState(); - - bool _isSearchActive = false; - - Timer? _debounce; - - late final userListController = StreamUserListController( - client: StreamChat.of(context).client, - sort: [ - const SortOption( - 'name', - direction: 1, - ), - ], - limit: 25, - filter: Filter.and([ - Filter.notEqual('id', StreamChat.of(context).currentUser!.id), - ]), - ); - - void _userNameListener() { - if (_debounce?.isActive ?? false) _debounce!.cancel(); - _debounce = Timer(const Duration(milliseconds: 350), () { - if (mounted) { - setState(() { - _userNameQuery = _controller.text; - _isSearchActive = _userNameQuery.isNotEmpty; - }); - userListController.filter = Filter.and([ - if (_userNameQuery.isNotEmpty) - Filter.autoComplete('name', _userNameQuery), - Filter.notEqual('id', StreamChat.of(context).currentUser!.id), - ]); - userListController.doInitialLoad(); - } - }); - } - - @override - void dispose() { - _controller.clear(); - _controller.removeListener(_userNameListener); - _controller.dispose(); - userListController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return AnimatedBuilder( - animation: groupChatState, - builder: (context, child) { - final state = groupChatState; - return Scaffold( - backgroundColor: StreamChatTheme.of(context).colorTheme.appBg, - appBar: AppBar( - elevation: 1, - backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg, - leading: const StreamBackButton(), - title: Text( - AppLocalizations.of(context).addGroupMembers, - style: TextStyle( - color: StreamChatTheme.of(context).colorTheme.textHighEmphasis, - fontSize: 16, - ), - ), - centerTitle: true, - actions: [ - if (state.users.isNotEmpty) - IconButton( - color: StreamChatTheme.of(context).colorTheme.accentPrimary, - icon: const StreamSvgIcon(icon: StreamSvgIcons.arrowRight), - onPressed: () async { - GoRouter.of(context).pushNamed( - Routes.NEW_GROUP_CHAT_DETAILS.name, - extra: state, - ); - }, - ) - ], - ), - body: StreamConnectionStatusBuilder( - statusBuilder: (context, status) { - var statusString = ''; - var showStatus = true; - - switch (status) { - case ConnectionStatus.connected: - statusString = AppLocalizations.of(context).connected; - showStatus = false; - break; - case ConnectionStatus.connecting: - statusString = AppLocalizations.of(context).reconnecting; - break; - case ConnectionStatus.disconnected: - statusString = AppLocalizations.of(context).disconnected; - break; - } - return StreamInfoTile( - showMessage: showStatus, - tileAnchor: Alignment.topCenter, - childAnchor: Alignment.topCenter, - message: statusString, - child: NestedScrollView( - floatHeaderSlivers: true, - headerSliverBuilder: - (BuildContext context, bool innerBoxIsScrolled) { - return [ - SliverToBoxAdapter( - child: SearchTextField( - controller: _controller, - hintText: AppLocalizations.of(context).search, - ), - ), - if (state.users.isNotEmpty) - SliverToBoxAdapter( - child: SizedBox( - height: 104, - child: ListView.separated( - scrollDirection: Axis.horizontal, - itemCount: state.users.length, - padding: const EdgeInsets.all(8), - separatorBuilder: (_, __) => - const SizedBox(width: 16), - itemBuilder: (_, index) { - final user = state.users.elementAt(index); - return Column( - children: [ - Stack( - children: [ - StreamUserAvatar( - onlineIndicatorAlignment: - const Alignment(0.9, 0.9), - user: user, - borderRadius: - BorderRadius.circular(32), - constraints: - const BoxConstraints.tightFor( - height: 64, - width: 64, - ), - ), - Positioned( - top: -4, - right: -4, - child: GestureDetector( - onTap: () { - groupChatState.removeUser(user); - }, - child: DecoratedBox( - decoration: BoxDecoration( - color: - StreamChatTheme.of(context) - .colorTheme - .appBg, - shape: BoxShape.circle, - border: Border.all( - color: StreamChatTheme.of( - context) - .colorTheme - .appBg, - ), - ), - child: StreamSvgIcon( - color: - StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis, - size: 24, - icon: StreamSvgIcons.close, - ), - ), - ), - ) - ], - ), - const SizedBox(height: 4), - Text( - user.name.split(' ')[0], - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 12, - ), - ), - ], - ); - }, - ), - ), - ), - SliverPersistentHeader( - pinned: true, - delegate: _HeaderDelegate( - height: 32, - child: Container( - width: double.maxFinite, - decoration: BoxDecoration( - gradient: StreamChatTheme.of(context) - .colorTheme - .bgGradient, - ), - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 8, - ), - child: Text( - _isSearchActive - ? '${AppLocalizations.of(context).matchesFor} "$_userNameQuery"' - : AppLocalizations.of(context).onThePlatorm, - style: TextStyle( - color: StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis, - ), - ), - ), - ), - ), - ), - ]; - }, - body: GestureDetector( - behavior: HitTestBehavior.opaque, - onPanDown: (_) => FocusScope.of(context).unfocus(), - child: StreamUserListView( - controller: userListController, - itemBuilder: (context, items, index, defaultWidget) { - return defaultWidget.copyWith( - selected: state.users.contains(items[index]), - ); - }, - onUserTap: groupChatState.addOrRemoveUser, - emptyBuilder: (_) { - return LayoutBuilder( - builder: (context, viewportConstraints) { - return SingleChildScrollView( - physics: const AlwaysScrollableScrollPhysics(), - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: viewportConstraints.maxHeight, - ), - child: Center( - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(24), - child: StreamSvgIcon( - icon: StreamSvgIcons.search, - size: 96, - color: StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis, - ), - ), - Text( - AppLocalizations.of(context) - .noUserMatchesTheseKeywords, - style: StreamChatTheme.of(context) - .textTheme - .footnote - .copyWith( - color: StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis, - ), - ), - ], - ), - ), - ), - ); - }, - ); - }, - ), - ), - ), - ); - }, - ), - ); - }, - ); - } -} - -class _HeaderDelegate extends SliverPersistentHeaderDelegate { - const _HeaderDelegate({ - required this.child, - required this.height, - }); - final Widget child; - final double height; - - @override - Widget build( - BuildContext context, double shrinkOffset, bool overlapsContent) { - return ColoredBox( - color: StreamChatTheme.of(context).colorTheme.barsBg, - child: child, - ); - } - - @override - double get maxExtent => height; - - @override - double get minExtent => height; - - @override - bool shouldRebuild(_HeaderDelegate oldDelegate) => true; -} diff --git a/sample_app/lib/pages/pinned_messages_screen.dart b/sample_app/lib/pages/pinned_messages_screen.dart deleted file mode 100644 index 2bcc792fc6..0000000000 --- a/sample_app/lib/pages/pinned_messages_screen.dart +++ /dev/null @@ -1,132 +0,0 @@ -// ignore_for_file: deprecated_member_use - -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:sample_app/routes/routes.dart'; -import 'package:sample_app/utils/localizations.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -class PinnedMessagesScreen extends StatefulWidget { - const PinnedMessagesScreen({super.key}); - - @override - State createState() => _PinnedMessagesScreenState(); -} - -class _PinnedMessagesScreenState extends State { - late final controller = StreamMessageSearchListController( - client: StreamChat.of(context).client, - filter: Filter.in_( - 'cid', - [StreamChannel.of(context).channel.cid!], - ), - messageFilter: Filter.equal( - 'pinned', - true, - ), - sort: [ - const SortOption( - 'created_at', - direction: SortOption.ASC, - ), - ], - limit: 20, - ); - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg, - appBar: AppBar( - elevation: 1, - centerTitle: true, - title: Text( - AppLocalizations.of(context).pinnedMessages, - style: TextStyle( - color: StreamChatTheme.of(context).colorTheme.textHighEmphasis, - fontSize: 16, - ), - ), - leading: const StreamBackButton(), - backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg, - ), - body: StreamMessageSearchListView( - controller: controller, - emptyBuilder: (_) { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - StreamSvgIcon( - icon: StreamSvgIcons.pin, - size: 136, - color: StreamChatTheme.of(context).colorTheme.disabled, - ), - const SizedBox(height: 16), - Text( - AppLocalizations.of(context).noPinnedItems, - style: TextStyle( - fontSize: 17, - color: - StreamChatTheme.of(context).colorTheme.textHighEmphasis, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 8), - RichText( - textAlign: TextAlign.center, - text: TextSpan(children: [ - TextSpan( - text: '${AppLocalizations.of(context).longPressMessage} ', - style: TextStyle( - fontSize: 14, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5), - ), - ), - TextSpan( - text: AppLocalizations.of(context).pinToConversation, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5), - ), - ), - ]), - ), - ], - ), - ); - }, - onMessageTap: (messageResponse) async { - final client = StreamChat.of(context).client; - final router = GoRouter.of(context); - final message = messageResponse.message; - final channel = client.channel( - messageResponse.channel!.type, - id: messageResponse.channel!.id, - ); - if (channel.state == null) { - await channel.watch(); - } - router.pushNamed( - Routes.CHANNEL_PAGE.name, - pathParameters: Routes.CHANNEL_PAGE.params(channel), - queryParameters: Routes.CHANNEL_PAGE.queryParams(message), - ); - }, - ), - ); - } - - @override - void dispose() { - controller.dispose(); - super.dispose(); - } -} diff --git a/sample_app/lib/pages/splash_screen.dart b/sample_app/lib/pages/splash_screen.dart deleted file mode 100644 index 0bbdcd9ebe..0000000000 --- a/sample_app/lib/pages/splash_screen.dart +++ /dev/null @@ -1,124 +0,0 @@ -// ignore_for_file: deprecated_member_use - -import 'package:flutter/material.dart'; -import 'package:lottie/lottie.dart'; - -mixin SplashScreenStateMixin on State - implements TickerProvider { - late final _animationController = AnimationController( - vsync: this, - duration: const Duration( - milliseconds: 1000, - ), - ); - - late final _scaleAnimationController = AnimationController( - vsync: this, - value: 0, - duration: const Duration( - milliseconds: 500, - ), - ); - - late final _circleAnimation = Tween( - begin: 0, - end: 1000, - ).animate(CurvedAnimation( - parent: _animationController, - curve: Curves.easeInOut, - )); - - late final _colorAnimation = ColorTween( - begin: const Color(0xff005FFF), - end: Colors.transparent, - ).animate(CurvedAnimation( - parent: _animationController, - curve: Curves.easeInOut, - )); - - late final _scaleAnimation = Tween( - begin: 1, - end: 1.5, - ).animate(CurvedAnimation( - parent: _scaleAnimationController, - curve: Curves.easeInOutCubic, - )); - - bool animationCompleted = false; - - void forwardAnimations() { - _scaleAnimationController.forward().whenComplete(() { - _animationController.forward(); - }); - } - - void _onAnimationComplete(AnimationStatus status) { - if (status == AnimationStatus.completed) { - setState(() { - animationCompleted = true; - }); - } - } - - @override - void initState() { - super.initState(); - _animationController.addStatusListener(_onAnimationComplete); - } - - @override - void dispose() { - _animationController.removeStatusListener(_onAnimationComplete); - _animationController.dispose(); - _scaleAnimationController.dispose(); - super.dispose(); - } - - Widget buildAnimation() => Stack( - clipBehavior: Clip.none, - alignment: Alignment.center, - children: [ - AnimatedBuilder( - animation: _scaleAnimation, - builder: (context, child) => - Transform.scale(scale: _scaleAnimation.value, child: child), - child: AnimatedBuilder( - animation: _colorAnimation, - builder: (context, child) { - return DecoratedBox( - decoration: BoxDecoration(color: _colorAnimation.value), - child: Center( - child: !_animationController.isAnimating - ? child - : const SizedBox(), - ), - ); - }, - child: RepaintBoundary( - child: Lottie.asset( - 'assets/floating_boat.json', - alignment: Alignment.center, - ), - ), - ), - ), - AnimatedBuilder( - animation: _circleAnimation, - builder: (context, snapshot) { - return Transform.scale( - scale: _circleAnimation.value, - child: Container( - width: 1, - height: 1, - decoration: BoxDecoration( - color: Colors.white - .withOpacity(1 - _animationController.value), - shape: BoxShape.circle, - ), - ), - ); - }, - ), - ], - ); -} diff --git a/sample_app/lib/pages/thread_list_page.dart b/sample_app/lib/pages/thread_list_page.dart deleted file mode 100644 index a2e10ce1b9..0000000000 --- a/sample_app/lib/pages/thread_list_page.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:sample_app/pages/thread_page.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -class ThreadListPage extends StatefulWidget { - const ThreadListPage({super.key}); - - @override - State createState() => _ThreadListPageState(); -} - -class _ThreadListPageState extends State { - late final controller = StreamThreadListController( - client: StreamChat.of(context).client, - ); - - @override - void dispose() { - controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - ValueListenableBuilder( - valueListenable: controller.unseenThreadIds, - builder: (_, unreadThreads, __) => StreamUnreadThreadsBanner( - unreadThreads: unreadThreads, - onTap: () => controller - .refresh(resetValue: false) - .then((_) => controller.clearUnseenThreadIds()), - ), - ), - Expanded( - child: StreamThreadListView( - controller: controller, - onThreadTap: (thread) async { - final channelCid = thread.channelCid; - - final channel = StreamChat.of(context).client.channel( - channelCid.split(':')[0], - id: channelCid.split(':')[1], - ); - - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) { - return StreamChannel( - channel: channel, - child: ThreadPage(parent: thread.parentMessage!), - ); - }, - ), - ); - }, - ), - ), - ], - ); - } -} diff --git a/sample_app/lib/pages/thread_page.dart b/sample_app/lib/pages/thread_page.dart deleted file mode 100644 index cdd3e85390..0000000000 --- a/sample_app/lib/pages/thread_page.dart +++ /dev/null @@ -1,98 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -class ThreadPage extends StatefulWidget { - const ThreadPage({ - super.key, - required this.parent, - this.initialScrollIndex, - this.initialAlignment, - }); - final Message parent; - final int? initialScrollIndex; - final double? initialAlignment; - - @override - State createState() => _ThreadPageState(); -} - -class _ThreadPageState extends State { - final FocusNode _focusNode = FocusNode(); - late StreamMessageInputController _messageInputController; - - @override - void initState() { - super.initState(); - _messageInputController = StreamMessageInputController( - message: Message(parentId: widget.parent.id), - ); - } - - @override - void dispose() { - _focusNode.dispose(); - super.dispose(); - } - - void _reply(Message message) { - _messageInputController.quotedMessage = message; - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - _focusNode.requestFocus(); - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: StreamChatTheme.of(context).colorTheme.appBg, - appBar: StreamThreadHeader( - parent: widget.parent, - ), - body: Column( - children: [ - Expanded( - child: StreamMessageListView( - parentMessage: widget.parent, - initialScrollIndex: widget.initialScrollIndex, - initialAlignment: widget.initialAlignment, - markReadWhenAtTheBottom: true, - //onMessageSwiped: _reply, - messageFilter: defaultFilter, - showScrollToBottom: false, - highlightInitialMessage: true, - messageBuilder: (context, details, messages, defaultMessage) { - return defaultMessage.copyWith( - onReplyTap: _reply, - bottomRowBuilderWithDefaultWidget: ( - context, - message, - defaultWidget, - ) { - return defaultWidget.copyWith( - deletedBottomRowBuilder: (context, message) { - return const StreamVisibleFootnote(); - }, - ); - }, - ); - }, - ), - ), - if (widget.parent.type != 'deleted') - StreamMessageInput( - focusNode: _focusNode, - messageInputController: _messageInputController, - ), - ], - ), - ); - } - - bool defaultFilter(Message m) { - final currentUser = StreamChat.of(context).currentUser; - final isMyMessage = m.user?.id == currentUser?.id; - final isDeletedOrShadowed = m.isDeleted == true || m.shadowed == true; - if (isDeletedOrShadowed && !isMyMessage) return false; - return true; - } -} diff --git a/sample_app/lib/pages/user_mentions_page.dart b/sample_app/lib/pages/user_mentions_page.dart deleted file mode 100644 index 820a38f78b..0000000000 --- a/sample_app/lib/pages/user_mentions_page.dart +++ /dev/null @@ -1,98 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:sample_app/routes/routes.dart'; -import 'package:sample_app/utils/localizations.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -class UserMentionsPage extends StatefulWidget { - const UserMentionsPage({super.key}); - - @override - State createState() => _UserMentionsPageState(); -} - -class _UserMentionsPageState extends State { - late final controller = StreamMessageSearchListController( - client: StreamChat.of(context).client, - filter: Filter.in_('members', [StreamChat.of(context).currentUser!.id]), - messageFilter: Filter.custom( - operator: r'$contains', - key: 'mentioned_users.id', - value: StreamChat.of(context).currentUser!.id, - ), - sort: [ - const SortOption( - 'created_at', - direction: SortOption.ASC, - ), - ], - limit: 20, - ); - @override - Widget build(BuildContext context) { - return StreamMessageSearchListView( - controller: controller, - emptyBuilder: (_) { - return LayoutBuilder( - builder: (context, viewportConstraints) { - return SingleChildScrollView( - physics: const AlwaysScrollableScrollPhysics(), - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: viewportConstraints.maxHeight, - ), - child: Center( - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(24), - child: StreamSvgIcon( - icon: StreamSvgIcons.mentions, - size: 96, - color: - StreamChatTheme.of(context).colorTheme.disabled, - ), - ), - Text( - AppLocalizations.of(context).noMentionsExistYet, - style: - StreamChatTheme.of(context).textTheme.body.copyWith( - color: StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis, - ), - ), - ], - ), - ), - ), - ); - }, - ); - }, - onMessageTap: (messageResponse) async { - final client = StreamChat.of(context).client; - final router = GoRouter.of(context); - final message = messageResponse.message; - final channel = client.channel( - messageResponse.channel!.type, - id: messageResponse.channel!.id, - ); - if (channel.state == null) { - await channel.watch(); - } - router.pushNamed( - Routes.CHANNEL_PAGE.name, - pathParameters: Routes.CHANNEL_PAGE.params(channel), - queryParameters: Routes.CHANNEL_PAGE.queryParams(message), - ); - }, - ); - } - - @override - void dispose() { - controller.dispose(); - super.dispose(); - } -} diff --git a/sample_app/lib/routes/app_routes.dart b/sample_app/lib/routes/app_routes.dart deleted file mode 100644 index 094060eb44..0000000000 --- a/sample_app/lib/routes/app_routes.dart +++ /dev/null @@ -1,128 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:sample_app/pages/advanced_options_page.dart'; -import 'package:sample_app/pages/channel_list_page.dart'; -import 'package:sample_app/pages/channel_page.dart'; -import 'package:sample_app/pages/chat_info_screen.dart'; -import 'package:sample_app/pages/choose_user_page.dart'; -import 'package:sample_app/pages/group_chat_details_screen.dart'; -import 'package:sample_app/pages/group_info_screen.dart'; -import 'package:sample_app/pages/new_chat_screen.dart'; -import 'package:sample_app/pages/new_group_chat_screen.dart'; -import 'package:sample_app/pages/thread_page.dart'; -import 'package:sample_app/routes/routes.dart'; -import 'package:sample_app/state/new_group_chat_state.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -final appRoutes = [ - GoRoute( - name: Routes.CHANNEL_LIST_PAGE.name, - path: Routes.CHANNEL_LIST_PAGE.path, - builder: (BuildContext context, GoRouterState state) => - const ChannelListPage(), - routes: [ - GoRoute( - name: Routes.CHANNEL_PAGE.name, - path: Routes.CHANNEL_PAGE.path, - builder: (context, state) { - final channel = StreamChat.of(context) - .client - .state - .channels[state.pathParameters['cid']]; - final messageId = state.uri.queryParameters['mid']; - final parentId = state.uri.queryParameters['pid']; - - Message? parentMessage; - if (parentId != null) { - parentMessage = channel?.state!.messages - .firstWhereOrNull((it) => it.id == parentId); - } - - return StreamChannel( - channel: channel!, - initialMessageId: messageId, - child: Builder( - builder: (context) { - return (parentMessage != null) - ? ThreadPage(parent: parentMessage) - : ChannelPage( - highlightInitialMessage: messageId != null, - ); - }, - ), - ); - }, - routes: [ - GoRoute( - name: Routes.CHAT_INFO_SCREEN.name, - path: Routes.CHAT_INFO_SCREEN.path, - builder: (BuildContext context, GoRouterState state) { - final channel = StreamChat.of(context) - .client - .state - .channels[state.pathParameters['cid']]; - return StreamChannel( - channel: channel!, - child: ChatInfoScreen( - user: state.extra as User?, - messageTheme: StreamChatTheme.of(context).ownMessageTheme, - ), - ); - }, - ), - GoRoute( - name: Routes.GROUP_INFO_SCREEN.name, - path: Routes.GROUP_INFO_SCREEN.path, - builder: (BuildContext context, GoRouterState state) { - final channel = StreamChat.of(context) - .client - .state - .channels[state.pathParameters['cid']]; - return StreamChannel( - channel: channel!, - child: GroupInfoScreen( - messageTheme: StreamChatTheme.of(context).ownMessageTheme, - ), - ); - }, - ), - ], - ), - ], - ), - GoRoute( - name: Routes.NEW_CHAT.name, - path: Routes.NEW_CHAT.path, - builder: (BuildContext context, GoRouterState state) { - return const NewChatScreen(); - }, - ), - GoRoute( - name: Routes.NEW_GROUP_CHAT.name, - path: Routes.NEW_GROUP_CHAT.path, - builder: (BuildContext context, GoRouterState state) { - return const NewGroupChatScreen(); - }, - ), - GoRoute( - name: Routes.NEW_GROUP_CHAT_DETAILS.name, - path: Routes.NEW_GROUP_CHAT_DETAILS.path, - builder: (BuildContext context, GoRouterState state) { - final groupChatState = state.extra! as NewGroupChatState; - return GroupChatDetailsScreen(groupChatState: groupChatState); - }, - ), - GoRoute( - name: Routes.CHOOSE_USER.name, - path: Routes.CHOOSE_USER.path, - builder: (BuildContext context, GoRouterState state) => - const ChooseUserPage(), - ), - GoRoute( - name: Routes.ADVANCED_OPTIONS.name, - path: Routes.ADVANCED_OPTIONS.path, - builder: (BuildContext context, GoRouterState state) => - const AdvancedOptionsPage(), - ), -]; diff --git a/sample_app/lib/routes/routes.dart b/sample_app/lib/routes/routes.dart deleted file mode 100644 index d60383edd2..0000000000 --- a/sample_app/lib/routes/routes.dart +++ /dev/null @@ -1,42 +0,0 @@ -// ignore_for_file: constant_identifier_names - -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Application routes -abstract class Routes { - static const RouteConfig CHOOSE_USER = - RouteConfig(name: 'choose_user', path: '/users'); - static const RouteConfig ADVANCED_OPTIONS = - RouteConfig(name: 'advanced_options', path: '/options'); - static const ChannelRouteConfig CHANNEL_PAGE = - ChannelRouteConfig(name: 'channel_page', path: 'channel/:cid'); - static const RouteConfig NEW_CHAT = - RouteConfig(name: 'new_chat', path: '/new_chat'); - static const RouteConfig NEW_GROUP_CHAT = - RouteConfig(name: 'new_group_chat', path: '/new_group_chat'); - static const RouteConfig NEW_GROUP_CHAT_DETAILS = RouteConfig( - name: 'new_group_chat_details', path: '/new_group_chat_details'); - static const ChannelRouteConfig CHAT_INFO_SCREEN = - ChannelRouteConfig(name: 'chat_info_screen', path: 'chat_info_screen'); - static const ChannelRouteConfig GROUP_INFO_SCREEN = - ChannelRouteConfig(name: 'group_info_screen', path: 'group_info_screen'); - static const RouteConfig CHANNEL_LIST_PAGE = - RouteConfig(name: 'channel_list_page', path: '/channels'); -} - -class RouteConfig { - const RouteConfig({required this.name, required this.path}); - final String name; - final String path; -} - -class ChannelRouteConfig extends RouteConfig { - const ChannelRouteConfig({required super.name, required super.path}); - - Map params(Channel channel) => {'cid': channel.cid!}; - - Map queryParams(Message message) => { - 'mid': message.id, - if (message.parentId != null) 'pid': message.parentId! - }; -} diff --git a/sample_app/lib/state/init_data.dart b/sample_app/lib/state/init_data.dart deleted file mode 100644 index 7f23896afd..0000000000 --- a/sample_app/lib/state/init_data.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:streaming_shared_preferences/streaming_shared_preferences.dart'; - -/// {@template init_notifier} -/// [ChangeNotifier] to store [InitData] and notify listeners on change. -/// {@endtemplate} -class InitNotifier extends ChangeNotifier { - /// {@macro init_notifier} - InitNotifier(); - - InitData? _initData; - - set initData(InitData? data) { - _initData = data; - notifyListeners(); - } - - InitData? get initData => _initData; -} - -/// {@template init_data} -/// Manages the initialization data for the sample application. -/// -/// Stores a reference to the current [StreamChatClient]. -/// {@endtemplate} -class InitData { - /// {@macro init_data} - InitData(this.client, this.preferences); - - final StreamChatClient client; - final StreamingSharedPreferences preferences; - - InitData copyWith({required StreamChatClient client}) => - InitData(client, preferences); -} diff --git a/sample_app/lib/state/new_group_chat_state.dart b/sample_app/lib/state/new_group_chat_state.dart deleted file mode 100644 index 2b6f6460e1..0000000000 --- a/sample_app/lib/state/new_group_chat_state.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -class NewGroupChatState extends ChangeNotifier { - final users = {}; - - void addUser(User user) { - if (!users.contains(user)) { - users.add(user); - notifyListeners(); - } - } - - void removeUser(User user) { - if (users.contains(user)) { - users.remove(user); - notifyListeners(); - } - } - - void addOrRemoveUser(User user) { - if (users.contains(user)) { - users.remove(user); - } else { - users.add(user); - } - notifyListeners(); - } -} diff --git a/sample_app/lib/utils/app_config.dart b/sample_app/lib/utils/app_config.dart deleted file mode 100644 index e006cddfee..0000000000 --- a/sample_app/lib/utils/app_config.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -const sentryDsn = - 'https://6381ef88de4140db8f5e25ab37e0f08c@o1213503.ingest.sentry.io/6352870'; - -const kDefaultStreamApiKey = 'kv7mcsxr24p8'; - -final defaultUsers = { - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoic2FsdmF0b3JlIn0.pgiJz7sIc7iP29BHKFwe3nLm5-OaR_1l2P-SlgiC9a8': - User( - id: 'salvatore', - extraData: const { - 'name': 'Salvatore Giordano', - 'image': - 'https://avatars.githubusercontent.com/u/20601437?s=460&u=3f66c22a7483980624804054ae7f357cf102c784&v=4', - }, - ), - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoic2FoaWwifQ.WnIUoB5gR2kcAsFhiDvkiD6zdHXZ-VSU2aQWWkhsvfo': - User( - id: 'sahil', - extraData: const { - 'name': 'Sahil Kumar', - 'image': - 'https://avatars.githubusercontent.com/u/25670178?s=400&u=30ded3784d8d2310c5748f263fd5e6433c119aa1&v=4', - }, - ), - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiYmVuIn0.nAz2sNFGQwY7rl2Og2z3TGHUsdpnN53tOsUglJFvLmg': - User( - id: 'ben', - extraData: const { - 'name': 'Ben Golden', - 'image': 'https://avatars.githubusercontent.com/u/1581974?s=400&v=4', - }, - ), - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoidGhpZXJyeSJ9.lEq6TrZtHzjoNtf7HHRufUPyGo_pa8vg4_XhEBp4ckY': - User( - id: 'thierry', - extraData: const { - 'name': 'Thierry Schellenbach', - 'image': - 'https://avatars.githubusercontent.com/u/265409?s=400&u=2d0e3bb1820db992066196bff7b004f0eee8e28d&v=4', - }, - ), - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoidG9tbWFzbyJ9.GLSI0ESshERMo2WjUpysD709NEtn1zmGimUN2an7g9o': - User( - id: 'tommaso', - extraData: const { - 'name': 'Tommaso Barbugli', - 'image': 'https://avatars.githubusercontent.com/u/88735?s=400&v=4', - }, - ), - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiZGV2ZW4ifQ.z3zI4PqJnNhc-1o-VKcmb6BnnQ0oxFNCRHwEulHqcWc': - User( - id: 'deven', - extraData: const { - 'name': 'Deven Joshi', - 'image': - 'https://avatars.githubusercontent.com/u/26357843?s=400&u=0c61d890866e67bf69f58878be58915e9bfd39ee&v=4', - }, - ), - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoibmVldmFzaCJ9.3EdHegTxibrz3A9cTiKmpEyawwcCVB8FXnoFzr4eKvw': - User( - id: 'neevash', - extraData: const { - 'name': 'Neevash Ramdial', - 'image': - 'https://avatars.githubusercontent.com/u/25674767?s=400&u=1d7333baf7dd9d143db8bfcdb31a838b89cfff9c&v=4', - }, - ), - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoicWF0ZXN0MSJ9.fnelU7HcP7QoEEsCGteNlF1fppofzNlrnpDQuIgeKCU': - User( - id: 'qatest1', - extraData: const { - 'name': 'QA test 1', - }, - ), - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoicWF0ZXN0MiJ9.vSCqAEbs2WVmMWsOsa7065Fsjq-rsTih6qsHPynl7XM': - User( - id: 'qatest2', - extraData: const { - 'name': 'QA test 2', - }, - ), -}; diff --git a/sample_app/lib/utils/local_notification_observer.dart b/sample_app/lib/utils/local_notification_observer.dart deleted file mode 100644 index 4b2fc12a69..0000000000 --- a/sample_app/lib/utils/local_notification_observer.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:sample_app/routes/routes.dart'; -import 'package:sample_app/utils/notifications_service.dart' as pn; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -class LocalNotificationObserver extends NavigatorObserver { - LocalNotificationObserver( - StreamChatClient client, - GlobalKey navigatorKey, - ) { - _subscription = client - .on( - EventType.messageNew, - EventType.notificationMessageNew, - ) - .listen((event) { - _handleEvent(event, client, navigatorKey); - }); - } - Route? currentRoute; - late final StreamSubscription _subscription; - - void _handleEvent(Event event, StreamChatClient client, - GlobalKey navigatorKey) { - if (event.message?.user?.id == client.state.currentUser?.id) { - return; - } - final channelId = event.cid; - if (currentRoute?.settings.name == Routes.CHANNEL_PAGE.name) { - final args = currentRoute!.settings.arguments! as Map; - if (args['cid'] == channelId) { - return; - } - } - - pn.showLocalNotification( - event, - client.state.currentUser!.id, - navigatorKey.currentState!.context, - ); - } - - @override - void didPop(Route route, Route? previousRoute) { - currentRoute = route; - } - - @override - void didPush(Route route, Route? previousRoute) { - currentRoute = route; - } - - @override - void didRemove(Route route, Route? previousRoute) { - currentRoute = route; - } - - @override - void didReplace({Route? newRoute, Route? oldRoute}) { - currentRoute = newRoute; - } - - void dispose() { - _subscription.cancel(); - } -} diff --git a/sample_app/lib/utils/localizations.dart b/sample_app/lib/utils/localizations.dart deleted file mode 100644 index 8a6bee4ed5..0000000000 --- a/sample_app/lib/utils/localizations.dart +++ /dev/null @@ -1,557 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -class AppLocalizations { - AppLocalizations(this.locale); - static const _localizedValues = >{ - 'en': { - 'add_a_group_name': 'Add a group name', - 'add_group_members': 'Add Group Members', - 'advanced_options': 'Advanced Options', - 'api_key_error': 'Please enter the Chat API Key', - 'attachment': 'attachment', - 'attachments': 'attachments', - 'cancel': 'Cancel', - 'chat_api_key': 'Chat API Key', - 'chats': 'Chats', - 'choose_a_group_chat_name': 'Choose a group chat name', - 'connected': 'Connected', - 'create_a_group': 'Create a Group', - 'custom_settings': 'Custom settings', - 'delete': 'Delete', - 'delete_conversation_are_you_sure': - 'Are you sure you want to delete this conversation?', - 'delete_conversation_title': 'Delete Conversation', - 'disconnected': 'Disconnected', - 'error_connecting': 'Error connecting, retry', - 'files': 'Files', - 'files_appear_here': 'Files sent in this chat will appear here', - 'group_shared_with_user_appear_here': - 'Group shared with User will appear here.', - 'last_seen': 'Last seen', - 'leave': 'Leave', - 'leave_conversation': 'Leave conversation', - 'leave_conversation_are_you_sure': - 'Are you sure you want to leave this conversation?', - 'leave_group': 'Leave Group', - 'loading': 'Loading...', - 'login': 'Login', - 'long_press_message': 'Long-press an important message and\nchoose', - 'matches_for': 'Matches for', - 'member': 'Member', - 'members': 'Members', - 'mentions': 'Mentions', - 'message': 'Message', - 'message_channel_description': 'Channel used for showing messages', - 'message_channel_name': 'Message channel', - 'more': 'more', - 'mute_group': 'Mute group', - 'mute_user': 'Mute user', - 'name': 'Name', - 'name_of_group_chat': 'Name of Group Chat', - 'new_chat': 'New Chat', - 'new_direct_message': 'New direct message', - 'new_group': 'New group', - 'no_chats_here_yet': 'No chats here yet...', - 'no_files': 'No Files', - 'no_media': 'No Media', - 'no_mentions_exist_yet': 'No mentions exist yet...', - 'no_pinned_items': 'No pinned items', - 'no_results': 'No results...', - 'no_shared_groups': 'No Shared Groups', - 'no_title': 'No title', - 'no_user_matches_these_keywords': 'No user matches these keywords...', - 'ok': 'OK', - 'online': 'Online', - 'on_the_platform': 'On the platform', - 'operation_could_not_be_completed': - "The operation couldn't be completed.", - 'owner': 'Owner', - 'photos_and_videos': 'Photos & Videos', - 'photos_or_videos_will_appear_here': - 'Photos or videos sent in this chat will \nappear here', - 'pinned_messages': 'Pinned Messages', - 'pin_to_conversation': 'Pin to conversation', - 'reconnecting': 'Reconnecting...', - 'remove': 'Remove', - 'remove_from_group': 'Remove From Group', - 'remove_member': 'Remove member', - 'remove_member_are_you_sure': - 'Are you sure you want to remove this member?', - 'search': 'Search', - 'select_user_to_try_flutter_sdk': 'Select a user to try the Flutter SDK', - 'shared_groups': 'Shared Groups', - 'sign_out': 'Sign out', - 'something_went_wrong_error_message': 'Something went wrong', - 'stream_sdk': 'Stream SDK', - 'stream_test_account': 'Stream test account', - 'to': 'To', - 'type_a_name_hint': 'Type a name', - 'user_id': 'User ID', - 'user_id_error': 'Please enter the User ID', - 'username_optional': 'Username (optional)', - 'user_token': 'User Token', - 'user_token_error': 'Please enter the user token', - 'view_info': 'View info', - 'welcome_to_stream_chat': 'Welcome to Stream Chat', - }, - 'it': { - 'add_a_group_name': 'Aggiungi un nome al gruppo', - 'add_group_members': 'Aggiungi un membro', - 'advanced_options': 'Opzioni Avanzate', - 'api_key_error': "Per favore inserisci l'API Key", - 'attachment': 'allegato', - 'attachments': 'allegati', - 'cancel': 'Annulla', - 'chat_api_key': 'Chat API Key', - 'chats': 'Conversazioni', - 'choose_a_group_chat_name': 'Scegli un nome per il gruppo', - 'connected': 'Connesso', - 'create_a_group': 'Crea un Gruppo', - 'custom_settings': 'Opzioni Personalizzate', - 'delete': 'Cancella', - 'delete_conversation_are_you_sure': - 'Sei sicuro di voler eliminare la conversazione?', - 'delete_conversation_title': 'Elimina Conversazione', - 'disconnected': 'Disconnesso', - 'error_connecting': 'Errore durante la connessione, riprova', - 'files': 'File', - 'files_appear_here': 'I file inviati in questa chat compariranno qui', - 'group_shared_with_user_appear_here': - "I gruppi in comune con quest'utente compariranno qui", - 'last_seen': 'Ultimo accesso', - 'leave': 'Lascia', - 'leave_conversation': 'Lascia conversazione', - 'leave_conversation_are_you_sure': - 'Sei sicuro di voler lasciare questa conversazione?', - 'leave_group': 'Lascia Gruppo', - 'loading': 'Caricamento...', - 'login': 'Login', - 'long_press_message': 'Premi a lungo su un messaggio importante e scegli', - 'matches_for': 'Risultati per', - 'member': 'Membro', - 'members': 'Membri', - 'mentions': 'Menzioni', - 'message': 'Messaggi', - 'message_channel_description': 'Canale usato per mostrare i messaggi', - 'message_channel_name': 'Invia un messaggio al canale', - 'more': 'altro', - 'mute_group': 'Silenzia gruppo', - 'mute_user': 'Silenzia utente', - 'name': 'Nome', - 'name_of_group_chat': 'Nome del gruppo', - 'new_chat': 'Nuova conversazione', - 'new_direct_message': 'Nuovo messaggio diretto', - 'new_group': 'Nuovo gruppo', - 'no_chats_here_yet': 'Ancora nessun messaggio...', - 'no_files': 'Nessun File', - 'no_media': 'Nessun Media', - 'no_mentions_exist_yet': 'Ancora nessuna menzione...', - 'no_pinned_items': 'Nessun messaggio in evidenza', - 'no_results': 'Nessun risultato...', - 'no_shared_groups': 'Nessun gruppo in comune', - 'no_title': 'Nessun titolo', - 'no_user_matches_these_keywords': 'Nessun utente per questa ricerca...', - 'ok': 'OK', - 'online': 'Online', - 'on_the_platform': 'Sulla piattaforma', - 'operation_could_not_be_completed': - "Non Ć© stato possibile completare l'operazione.", - 'owner': 'Proprietario', - 'photos_and_videos': 'Foto & Video', - 'photos_or_videos_will_appear_here': - 'Foto or video inviati in questa chat \ncompariranno qui', - 'pinned_messages': 'Messaggi in evidenza', - 'pin_to_conversation': 'Metti in evidenza', - 'reconnecting': 'Riconnessione...', - 'remove': 'Rimuovi', - 'remove_from_group': 'Rimuovi Dal Gruppo', - 'remove_member': 'Rimuovi membro', - 'remove_member_are_you_sure': - 'Sei sicuro di voler rimuovere questo membro?', - 'search': 'Cerca', - 'select_user_to_try_flutter_sdk': - "Seleziona un utente per provare l'SDK Flutter", - 'shared_groups': 'Gruppi in comune', - 'sign_out': 'Sign out', - 'something_went_wrong_error_message': 'Qualcosa Ć© andato storto', - 'stream_sdk': 'Stream SDK', - 'stream_test_account': 'Account di test', - 'to': 'A', - 'type_a_name_hint': 'Scrivi un nome', - 'user_id': 'User ID', - 'user_id_error': "Per favore inserisci l'ID dell'utente", - 'username_optional': 'Username (opzionale)', - 'user_token': 'Token Utente', - 'user_token_error': 'Per favore inserisci il token', - 'view_info': 'Vedi info', - 'welcome_to_stream_chat': 'Benvenuto in Stream Chat', - }, - }; - - final Locale locale; - - String get addAGroupName { - return _localizedValues[locale.languageCode]!['add_a_group_name']!; - } - - String get addGroupMembers { - return _localizedValues[locale.languageCode]!['add_group_members']!; - } - - String get advancedOptions { - return _localizedValues[locale.languageCode]!['advanced_options']!; - } - - String get apiKeyError { - return _localizedValues[locale.languageCode]!['api_key_error']!; - } - - String get attachment { - return _localizedValues[locale.languageCode]!['attachment']!; - } - - String get attachments { - return _localizedValues[locale.languageCode]!['attachments']!; - } - - String get cancel { - return _localizedValues[locale.languageCode]!['cancel']!; - } - - String get chatApiKey { - return _localizedValues[locale.languageCode]!['chat_api_key']!; - } - - String get chats { - return _localizedValues[locale.languageCode]!['chats']!; - } - - String get chooseAGroupChatName { - return _localizedValues[locale.languageCode]!['choose_a_group_chat_name']!; - } - - String get connected { - return _localizedValues[locale.languageCode]!['connected']!; - } - - String get createAGroup { - return _localizedValues[locale.languageCode]!['create_a_group']!; - } - - String get customSettings { - return _localizedValues[locale.languageCode]!['custom_settings']!; - } - - String get delete { - return _localizedValues[locale.languageCode]!['delete']!; - } - - String get deleteConversationAreYouSure { - return _localizedValues[locale.languageCode]![ - 'delete_conversation_are_you_sure']!; - } - - String get deleteConversationTitle { - return _localizedValues[locale.languageCode]!['delete_conversation_title']!; - } - - String get disconnected { - return _localizedValues[locale.languageCode]!['disconnected']!; - } - - String get errorConnecting { - return _localizedValues[locale.languageCode]!['error_connecting']!; - } - - String get files { - return _localizedValues[locale.languageCode]!['files']!; - } - - String get filesAppearHere { - return _localizedValues[locale.languageCode]!['files_appear_here']!; - } - - String get groupSharedWithUserAppearHere { - return _localizedValues[locale.languageCode]![ - 'group_shared_with_user_appear_here']!; - } - - String get lastSeen { - return _localizedValues[locale.languageCode]!['last_seen']!; - } - - String get leave { - return _localizedValues[locale.languageCode]!['leave']!; - } - - String get leaveConversation { - return _localizedValues[locale.languageCode]!['leave_conversation']!; - } - - String get leaveConversationAreYouSure { - return _localizedValues[locale.languageCode]![ - 'leave_conversation_are_you_sure']!; - } - - String get leaveGroup { - return _localizedValues[locale.languageCode]!['leave_group']!; - } - - String get loading { - return _localizedValues[locale.languageCode]!['loading']!; - } - - String get login { - return _localizedValues[locale.languageCode]!['login']!; - } - - String get longPressMessage { - return _localizedValues[locale.languageCode]!['long_press_message']!; - } - - String get matchesFor { - return _localizedValues[locale.languageCode]!['matches_for']!; - } - - String get member { - return _localizedValues[locale.languageCode]!['member']!; - } - - String get members { - return _localizedValues[locale.languageCode]!['members']!; - } - - String get mentions { - return _localizedValues[locale.languageCode]!['mentions']!; - } - - String get message { - return _localizedValues[locale.languageCode]!['message']!; - } - - String get messageChannelDescription { - return _localizedValues[locale.languageCode]![ - 'message_channel_description']!; - } - - String get messageChannelName { - return _localizedValues[locale.languageCode]!['message_channel_name']!; - } - - String get more { - return _localizedValues[locale.languageCode]!['more']!; - } - - String get muteGroup { - return _localizedValues[locale.languageCode]!['mute_group']!; - } - - String get muteUser { - return _localizedValues[locale.languageCode]!['mute_user']!; - } - - String get name { - return _localizedValues[locale.languageCode]!['name']!; - } - - String get nameOfGroupChat { - return _localizedValues[locale.languageCode]!['name_of_group_chat']!; - } - - String get newChat { - return _localizedValues[locale.languageCode]!['new_chat']!; - } - - String get newDirectMessage { - return _localizedValues[locale.languageCode]!['new_direct_message']!; - } - - String get newGroup { - return _localizedValues[locale.languageCode]!['new_group']!; - } - - String get noChatsHereYet { - return _localizedValues[locale.languageCode]!['no_chats_here_yet']!; - } - - String get noFiles { - return _localizedValues[locale.languageCode]!['no_files']!; - } - - String get noMedia { - return _localizedValues[locale.languageCode]!['no_media']!; - } - - String get noMentionsExistYet { - return _localizedValues[locale.languageCode]!['no_mentions_exist_yet']!; - } - - String get noPinnedItems { - return _localizedValues[locale.languageCode]!['no_pinned_items']!; - } - - String get noResults { - return _localizedValues[locale.languageCode]!['no_results']!; - } - - String get noSharedGroups { - return _localizedValues[locale.languageCode]!['no_shared_groups']!; - } - - String get noTitle { - return _localizedValues[locale.languageCode]!['no_title']!; - } - - String get noUserMatchesTheseKeywords { - return _localizedValues[locale.languageCode]![ - 'no_user_matches_these_keywords']!; - } - - String get ok { - return _localizedValues[locale.languageCode]!['ok']!; - } - - String get online { - return _localizedValues[locale.languageCode]!['online']!; - } - - String get onThePlatorm { - return _localizedValues[locale.languageCode]!['on_the_platform']!; - } - - String get operationCouldNotBeCompleted { - return _localizedValues[locale.languageCode]![ - 'operation_could_not_be_completed']!; - } - - String get owner { - return _localizedValues[locale.languageCode]!['owner']!; - } - - String get photosAndVideos { - return _localizedValues[locale.languageCode]!['photos_and_videos']!; - } - - String get photosOrVideosWillAppearHere { - return _localizedValues[locale.languageCode]![ - 'photos_or_videos_will_appear_here']!; - } - - String get pinnedMessages { - return _localizedValues[locale.languageCode]!['pinned_messages']!; - } - - String get pinToConversation { - return _localizedValues[locale.languageCode]!['pin_to_conversation']!; - } - - String get reconnecting { - return _localizedValues[locale.languageCode]!['reconnecting']!; - } - - String get remove { - return _localizedValues[locale.languageCode]!['remove']!; - } - - String get removeFromGroup { - return _localizedValues[locale.languageCode]!['remove_from_group']!; - } - - String get removeMember { - return _localizedValues[locale.languageCode]!['remove_member']!; - } - - String get removeMemberAreYouSure { - return _localizedValues[locale.languageCode]![ - 'remove_member_are_you_sure']!; - } - - String get search { - return _localizedValues[locale.languageCode]!['search']!; - } - - String get selectUserToTryFlutterSDK { - return _localizedValues[locale.languageCode]![ - 'select_user_to_try_flutter_sdk']!; - } - - String get sharedGroups { - return _localizedValues[locale.languageCode]!['shared_groups']!; - } - - String get signOut { - return _localizedValues[locale.languageCode]!['sign_out']!; - } - - String get somethingWentWrongErrorMessage { - return _localizedValues[locale.languageCode]![ - 'something_went_wrong_error_message']!; - } - - String get streamSDK { - return _localizedValues[locale.languageCode]!['stream_sdk']!; - } - - String get streamTestAccount { - return _localizedValues[locale.languageCode]!['stream_test_account']!; - } - - String get to { - return _localizedValues[locale.languageCode]!['to']!; - } - - String get typeANameHint { - return _localizedValues[locale.languageCode]!['type_a_name_hint']!; - } - - String get userId { - return _localizedValues[locale.languageCode]!['user_id']!; - } - - String get userIdError { - return _localizedValues[locale.languageCode]!['user_id_error']!; - } - - String get usernameOptional { - return _localizedValues[locale.languageCode]!['username_optional']!; - } - - String get userToken { - return _localizedValues[locale.languageCode]!['user_token']!; - } - - String get userTokenError { - return _localizedValues[locale.languageCode]!['user_token_error']!; - } - - String get viewInfo { - return _localizedValues[locale.languageCode]!['view_info']!; - } - - String get welcomeToStreamChat { - return _localizedValues[locale.languageCode]!['welcome_to_stream_chat']!; - } - - static List languages() => _localizedValues.keys.toList(); - - static AppLocalizations of(BuildContext context) { - return Localizations.of(context, AppLocalizations)!; - } -} - -class AppLocalizationsDelegate extends LocalizationsDelegate { - const AppLocalizationsDelegate(); - - @override - bool isSupported(Locale locale) => - AppLocalizations.languages().contains(locale.languageCode); - - @override - Future load(Locale locale) { - return SynchronousFuture(AppLocalizations(locale)); - } - - @override - bool shouldReload(AppLocalizationsDelegate old) => false; -} diff --git a/sample_app/lib/utils/notifications_service.dart b/sample_app/lib/utils/notifications_service.dart deleted file mode 100644 index 9c34099c4f..0000000000 --- a/sample_app/lib/utils/notifications_service.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_local_notifications/flutter_local_notifications.dart' - hide Message; -import 'package:go_router/go_router.dart'; -import 'package:sample_app/routes/routes.dart'; -import 'package:sample_app/utils/localizations.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -void showLocalNotification( - Event event, - String currentUserId, - BuildContext context, -) async { - // Don't show notification if the event is from the current user. - if (event.user!.id == currentUserId) return; - - // Don't show notification if the event is not a message. - if (![ - EventType.messageNew, - EventType.notificationMessageNew, - ].contains(event.type)) { - return; - } - - // Return if the message is null. - if (event.message == null) return; - - final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); - const initializationSettings = InitializationSettings( - iOS: DarwinInitializationSettings(), - android: AndroidInitializationSettings('ic_notification_in_app'), - ); - - final appLocalizations = AppLocalizations.of(context); - - await flutterLocalNotificationsPlugin.initialize( - initializationSettings, - onDidReceiveNotificationResponse: (response) async { - final channelCid = response.payload; - if (channelCid == null) return; - - final channelType = channelCid.split(':')[0]; - final channelId = channelCid.split(':')[1]; - - final client = StreamChat.of(context).client; - final router = GoRouter.of(context); - - final channel = client.channel(channelType, id: channelId); - await channel.watch(); - - router.pushNamed( - Routes.CHANNEL_PAGE.name, - pathParameters: Routes.CHANNEL_PAGE.params(channel), - ); - }, - ); - - await flutterLocalNotificationsPlugin.show( - event.message!.id.hashCode, - event.message!.user!.name, - event.message!.text, - NotificationDetails( - android: AndroidNotificationDetails( - 'message channel', - appLocalizations.messageChannelName, - channelDescription: appLocalizations.messageChannelDescription, - priority: Priority.high, - importance: Importance.high, - ), - iOS: const DarwinNotificationDetails(), - ), - payload: '${event.channelType}:${event.channelId}', - ); -} - -Future cancelLocalNotifications() async { - final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); - await flutterLocalNotificationsPlugin.cancelAll(); -} diff --git a/sample_app/lib/widgets/channel_list.dart b/sample_app/lib/widgets/channel_list.dart deleted file mode 100644 index d17678c305..0000000000 --- a/sample_app/lib/widgets/channel_list.dart +++ /dev/null @@ -1,309 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter_slidable/flutter_slidable.dart'; -import 'package:go_router/go_router.dart'; -import 'package:sample_app/pages/chat_info_screen.dart'; -import 'package:sample_app/pages/group_info_screen.dart'; -import 'package:sample_app/routes/routes.dart'; -import 'package:sample_app/utils/localizations.dart'; -import 'package:sample_app/widgets/search_text_field.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -class ChannelList extends StatefulWidget { - const ChannelList({super.key}); - - @override - State createState() => _ChannelList(); -} - -class _ChannelList extends State { - final ScrollController _scrollController = ScrollController(); - - late final StreamMessageSearchListController _messageSearchListController = - StreamMessageSearchListController( - client: StreamChat.of(context).client, - filter: Filter.in_('members', [StreamChat.of(context).currentUser!.id]), - limit: 5, - searchQuery: '', - sort: [ - const SortOption( - 'created_at', - direction: SortOption.ASC, - ), - ], - ); - - late final TextEditingController _controller = TextEditingController() - ..addListener(_channelQueryListener); - - bool _isSearchActive = false; - - Timer? _debounce; - - void _channelQueryListener() { - if (_debounce?.isActive ?? false) _debounce!.cancel(); - _debounce = Timer(const Duration(milliseconds: 350), () { - if (mounted) { - _messageSearchListController.searchQuery = _controller.text; - setState(() { - _isSearchActive = _controller.text.isNotEmpty; - }); - if (_isSearchActive) _messageSearchListController.doInitialLoad(); - } - }); - } - - late final _channelListController = StreamChannelListController( - client: StreamChat.of(context).client, - filter: Filter.in_( - 'members', - [StreamChat.of(context).currentUser!.id], - ), - limit: 30, - ); - - @override - void dispose() { - _controller.removeListener(_channelQueryListener); - _controller.dispose(); - _scrollController.dispose(); - _channelListController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return PopScope( - canPop: _isSearchActive == false, - onPopInvokedWithResult: (didPop, _) { - if (didPop) return; - _controller.clear(); - setState(() => _isSearchActive = false); - }, - child: NotificationListener( - onNotification: (ScrollNotification scrollInfo) { - if (_scrollController.position.userScrollDirection == - ScrollDirection.reverse) { - FocusScope.of(context).unfocus(); - } - return true; - }, - child: NestedScrollView( - controller: _scrollController, - headerSliverBuilder: (_, __) => [ - SliverToBoxAdapter( - child: SearchTextField( - controller: _controller, - showCloseButton: _isSearchActive, - hintText: AppLocalizations.of(context).search, - ), - ), - ], - body: _isSearchActive - ? StreamMessageSearchListView( - controller: _messageSearchListController, - emptyBuilder: (_) { - return LayoutBuilder( - builder: (context, viewportConstraints) { - return SingleChildScrollView( - physics: const AlwaysScrollableScrollPhysics(), - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: viewportConstraints.maxHeight, - ), - child: Center( - child: Column( - children: [ - const Padding( - padding: EdgeInsets.all(24), - child: StreamSvgIcon( - icon: StreamSvgIcons.search, - size: 96, - color: Colors.grey, - ), - ), - Text( - AppLocalizations.of(context).noResults, - ), - ], - ), - ), - ), - ); - }, - ); - }, - itemBuilder: ( - context, - messageResponses, - index, - defaultWidget, - ) { - return defaultWidget.copyWith( - onTap: () async { - final messageResponse = messageResponses[index]; - FocusScope.of(context).requestFocus(FocusNode()); - final client = StreamChat.of(context).client; - final router = GoRouter.of(context); - final message = messageResponse.message; - final channel = client.channel( - messageResponse.channel!.type, - id: messageResponse.channel!.id, - ); - if (channel.state == null) { - await channel.watch(); - } - router.pushNamed( - Routes.CHANNEL_PAGE.name, - pathParameters: Routes.CHANNEL_PAGE.params(channel), - queryParameters: - Routes.CHANNEL_PAGE.queryParams(message), - ); - }, - ); - }, - ) - : SlidableAutoCloseBehavior( - child: RefreshIndicator( - onRefresh: _channelListController.refresh, - child: StreamChannelListView( - controller: _channelListController, - itemBuilder: (context, channels, index, defaultWidget) { - final chatTheme = StreamChatTheme.of(context); - final backgroundColor = chatTheme.colorTheme.inputBg; - final channel = channels[index]; - final canDeleteChannel = channel.ownCapabilities - .contains(PermissionType.deleteChannel); - return Slidable( - groupTag: 'channels-actions', - endActionPane: ActionPane( - extentRatio: canDeleteChannel ? 0.40 : 0.20, - motion: const BehindMotion(), - children: [ - CustomSlidableAction( - backgroundColor: backgroundColor, - onPressed: (_) { - showChannelInfoModalBottomSheet( - context: context, - channel: channel, - onViewInfoTap: () { - Navigator.pop(context); - Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - final isOneToOne = - channel.memberCount == 2 && - channel.isDistinct; - return StreamChannel( - channel: channel, - child: isOneToOne - ? ChatInfoScreen( - messageTheme: chatTheme - .ownMessageTheme, - user: channel - .state!.members - .where((m) => - m.userId != - channel - .client - .state - .currentUser! - .id) - .first - .user, - ) - : GroupInfoScreen( - messageTheme: chatTheme - .ownMessageTheme, - ), - ); - }, - ), - ); - }, - ); - }, - child: const Icon(Icons.more_horiz), - ), - if (canDeleteChannel) - CustomSlidableAction( - backgroundColor: backgroundColor, - child: StreamSvgIcon( - icon: StreamSvgIcons.delete, - color: chatTheme.colorTheme.accentError, - ), - onPressed: (_) async { - final res = - await showConfirmationBottomSheet( - context, - title: 'Delete Conversation', - question: - 'Are you sure you want to delete this conversation?', - okText: 'Delete', - cancelText: 'Cancel', - icon: StreamSvgIcon( - icon: StreamSvgIcons.delete, - color: chatTheme.colorTheme.accentError, - ), - ); - if (res == true) { - await _channelListController - .deleteChannel(channel); - } - }, - ), - ], - ), - child: defaultWidget, - ); - }, - onChannelTap: (channel) { - GoRouter.of(context).pushNamed( - Routes.CHANNEL_PAGE.name, - pathParameters: Routes.CHANNEL_PAGE.params(channel), - ); - }, - emptyBuilder: (_) { - return Center( - child: Padding( - padding: const EdgeInsets.all(8), - child: StreamScrollViewEmptyWidget( - emptyIcon: StreamSvgIcon( - icon: StreamSvgIcons.message, - size: 148, - color: StreamChatTheme.of(context) - .colorTheme - .disabled, - ), - emptyTitle: TextButton( - onPressed: () { - GoRouter.of(context) - .pushNamed(Routes.NEW_CHAT.name); - }, - child: Text( - 'Start a chat', - style: StreamChatTheme.of(context) - .textTheme - .bodyBold - .copyWith( - color: StreamChatTheme.of(context) - .colorTheme - .accentPrimary, - ), - ), - ), - ), - ), - ); - }, - ), - ), - ), - ), - ), - ); - } -} diff --git a/sample_app/lib/widgets/chips_input_text_field.dart b/sample_app/lib/widgets/chips_input_text_field.dart deleted file mode 100644 index 45368e0939..0000000000 --- a/sample_app/lib/widgets/chips_input_text_field.dart +++ /dev/null @@ -1,168 +0,0 @@ -// ignore_for_file: deprecated_member_use - -import 'package:flutter/material.dart'; -import 'package:sample_app/utils/localizations.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -typedef ChipBuilder = Widget Function(BuildContext context, T chip); -typedef OnChipAdded = void Function(T chip); -typedef OnChipRemoved = void Function(T chip); - -class ChipsInputTextField extends StatefulWidget { - const ChipsInputTextField({ - super.key, - required this.chipBuilder, - required this.controller, - this.onInputChanged, - this.focusNode, - this.onChipAdded, - this.onChipRemoved, - this.hint = 'Type a name', - }); - final TextEditingController? controller; - final FocusNode? focusNode; - final ValueChanged? onInputChanged; - final ChipBuilder chipBuilder; - final OnChipAdded? onChipAdded; - final OnChipRemoved? onChipRemoved; - final String hint; - - @override - ChipInputTextFieldState createState() => ChipInputTextFieldState(); -} - -class ChipInputTextFieldState extends State> { - final _chips = {}; - bool _pauseItemAddition = false; - - void addItem(T item) { - setState(() => _chips.add(item)); - widget.onChipAdded?.call(item); - } - - void removeItem(T item) { - setState(() { - _chips.remove(item); - if (_chips.isEmpty) resumeItemAddition(); - }); - widget.onChipRemoved?.call(item); - } - - void pauseItemAddition() { - if (!_pauseItemAddition) { - setState(() => _pauseItemAddition = true); - } - widget.focusNode?.unfocus(); - } - - void resumeItemAddition() { - if (_pauseItemAddition) { - setState(() => _pauseItemAddition = false); - } - widget.focusNode?.requestFocus(); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: _pauseItemAddition ? resumeItemAddition : null, - child: Material( - elevation: 1, - color: StreamChatTheme.of(context).colorTheme.barsBg, - child: Padding( - padding: const EdgeInsets.fromLTRB(16, 16, 16, 16), - child: Row( - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 4), - child: Text( - '${AppLocalizations.of(context).to.toUpperCase()}:', - style: StreamChatTheme.of(context) - .textTheme - .footnote - .copyWith( - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(.5)), - ), - ), - const SizedBox(width: 12), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Wrap( - spacing: 8, - runSpacing: 4, - children: _chips.map((item) { - return widget.chipBuilder(context, item); - }).toList(), - ), - if (!_pauseItemAddition) - TextField( - controller: widget.controller, - onChanged: widget.onInputChanged, - focusNode: widget.focusNode, - decoration: InputDecoration( - isDense: true, - border: InputBorder.none, - focusedBorder: InputBorder.none, - enabledBorder: InputBorder.none, - errorBorder: InputBorder.none, - disabledBorder: InputBorder.none, - contentPadding: const EdgeInsets.only(top: 4), - hintText: widget.hint, - hintStyle: StreamChatTheme.of(context) - .textTheme - .body - .copyWith( - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(.5)), - ), - ), - ], - ), - ), - const SizedBox(width: 12), - Align( - alignment: Alignment.bottomCenter, - child: IconButton( - icon: _chips.isEmpty - ? StreamSvgIcon( - icon: StreamSvgIcons.user, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5), - size: 24, - ) - : StreamSvgIcon( - icon: StreamSvgIcons.userAdd, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5), - size: 24, - ), - onPressed: resumeItemAddition, - alignment: Alignment.topRight, - visualDensity: VisualDensity.compact, - padding: EdgeInsets.zero, - splashRadius: 24, - constraints: const BoxConstraints.tightFor( - height: 24, - width: 24, - ), - ), - ), - ], - ), - ), - ), - ); - } -} diff --git a/sample_app/lib/widgets/search_text_field.dart b/sample_app/lib/widgets/search_text_field.dart deleted file mode 100644 index ec3084e47e..0000000000 --- a/sample_app/lib/widgets/search_text_field.dart +++ /dev/null @@ -1,96 +0,0 @@ -// ignore_for_file: deprecated_member_use - -import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -class SearchTextField extends StatelessWidget { - const SearchTextField({ - super.key, - required this.controller, - this.onChanged, - this.onTap, - this.hintText = 'Search', - this.showCloseButton = true, - }); - final TextEditingController? controller; - final ValueChanged? onChanged; - final String hintText; - final VoidCallback? onTap; - final bool showCloseButton; - - @override - Widget build(BuildContext context) { - return Container( - height: 36, - decoration: BoxDecoration( - color: StreamChatTheme.of(context).colorTheme.barsBg, - border: Border.all( - color: StreamChatTheme.of(context).colorTheme.borders, - ), - borderRadius: BorderRadius.circular(24), - ), - margin: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 8, - ), - child: Row( - children: [ - Expanded( - child: TextField( - onTap: onTap, - controller: controller, - onChanged: onChanged, - decoration: InputDecoration( - prefixText: ' ', - prefixIconConstraints: BoxConstraints.tight(const Size(40, 24)), - prefixIcon: Padding( - padding: const EdgeInsets.only( - left: 8, - right: 8, - ), - child: StreamSvgIcon( - icon: StreamSvgIcons.search, - color: - StreamChatTheme.of(context).colorTheme.textHighEmphasis, - size: 24, - ), - ), - hintText: hintText, - hintStyle: StreamChatTheme.of(context).textTheme.body.copyWith( - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(.5)), - contentPadding: EdgeInsets.zero, - border: OutlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(24), - ), - ), - ), - ), - if (showCloseButton) - Material( - color: Colors.transparent, - child: IconButton( - color: Colors.grey, - padding: EdgeInsets.zero, - icon: const StreamSvgIcon(icon: StreamSvgIcons.closeSmall), - splashRadius: 24, - onPressed: () { - if (controller!.text.isNotEmpty) { - Future.microtask( - () => [ - controller!.clear(), - if (onChanged != null) onChanged!(''), - ], - ); - } - }, - ), - ), - ], - ), - ); - } -} diff --git a/sample_app/lib/widgets/stream_version.dart b/sample_app/lib/widgets/stream_version.dart deleted file mode 100644 index dfafee4cd3..0000000000 --- a/sample_app/lib/widgets/stream_version.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:sample_app/utils/localizations.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:yaml/yaml.dart'; - -class StreamVersion extends StatelessWidget { - const StreamVersion({ - super.key, - }); - - @override - Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.symmetric(vertical: 16), - alignment: Alignment.bottomCenter, - child: FutureBuilder( - future: rootBundle.loadString('pubspec.lock'), - builder: (context, snapshot) { - if (!snapshot.hasData) { - return const SizedBox(); - } - - final pubspec = snapshot.data!; - final yaml = loadYaml(pubspec); - final streamChatDep = - yaml['packages']['stream_chat_flutter']['version']; - - return Text( - '${AppLocalizations.of(context).streamSDK} v $streamChatDep', - style: TextStyle( - fontSize: 14, - color: StreamChatTheme.of(context).colorTheme.disabled, - ), - ); - }, - ), - ); - } -} diff --git a/sample_app/macos/Flutter/Flutter-Debug.xcconfig b/sample_app/macos/Flutter/Flutter-Debug.xcconfig deleted file mode 100644 index 4b81f9b2d2..0000000000 --- a/sample_app/macos/Flutter/Flutter-Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/sample_app/macos/Flutter/Flutter-Release.xcconfig b/sample_app/macos/Flutter/Flutter-Release.xcconfig deleted file mode 100644 index 5caa9d1579..0000000000 --- a/sample_app/macos/Flutter/Flutter-Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/sample_app/macos/Podfile b/sample_app/macos/Podfile deleted file mode 100644 index 7005b3604d..0000000000 --- a/sample_app/macos/Podfile +++ /dev/null @@ -1,41 +0,0 @@ -# Uncomment this line to define a global platform for your project -# platform :osx, '11.5' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_macos_podfile_setup - -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_macos_build_settings(target) - end -end diff --git a/sample_app/macos/Runner.xcodeproj/project.pbxproj b/sample_app/macos/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index 0204e8aab2..0000000000 --- a/sample_app/macos/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,663 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXAggregateTarget section */ - 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { - isa = PBXAggregateTarget; - buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; - buildPhases = ( - 33CC111E2044C6BF0003C045 /* ShellScript */, - ); - dependencies = ( - ); - name = "Flutter Assemble"; - productName = FLX; - }; -/* End PBXAggregateTarget section */ - -/* Begin PBXBuildFile section */ - 0D4CBA18257CE68ED24F92E8 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B7C148DD697EB38DCDC5FC57 /* GoogleService-Info.plist */; }; - 1E52A3376056853520695B44 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 782D013032AAC6BDCC56A273 /* Pods_Runner.framework */; }; - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 33CC10E52044A3C60003C045 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 33CC111A2044C6BA0003C045; - remoteInfo = FLX; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 33CC110E2044A8840003C045 /* Bundle Framework */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Bundle Framework"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 1CCCE971B34B1EF0E9C23CAC /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* stream_chat_v1.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = stream_chat_v1.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; - 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; - 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; - 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; - 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; - 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 782D013032AAC6BDCC56A273 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 929AC42CE2CDA27FE9235234 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - B7C148DD697EB38DCDC5FC57 /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; }; - CDF4BDDD28F9A6920059DB87 /* RunnerDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerDebug.entitlements; sourceTree = ""; }; - D4F4A1B883B395E86A7B745A /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 33CC10EA2044A3C60003C045 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 1E52A3376056853520695B44 /* Pods_Runner.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 33BA886A226E78AF003329D5 /* Configs */ = { - isa = PBXGroup; - children = ( - 33E5194F232828860026EE4D /* AppInfo.xcconfig */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, - ); - path = Configs; - sourceTree = ""; - }; - 33CC10E42044A3C60003C045 = { - isa = PBXGroup; - children = ( - 33FAB671232836740065AC1E /* Runner */, - 33CEB47122A05771004F2AC0 /* Flutter */, - 33CC10EE2044A3C60003C045 /* Products */, - D73912EC22F37F3D000D13A0 /* Frameworks */, - 3A21EEB06B73EAB5B839B688 /* Pods */, - B7C148DD697EB38DCDC5FC57 /* GoogleService-Info.plist */, - ); - sourceTree = ""; - }; - 33CC10EE2044A3C60003C045 /* Products */ = { - isa = PBXGroup; - children = ( - 33CC10ED2044A3C60003C045 /* stream_chat_v1.app */, - ); - name = Products; - sourceTree = ""; - }; - 33CC11242044D66E0003C045 /* Resources */ = { - isa = PBXGroup; - children = ( - 33CC10F22044A3C60003C045 /* Assets.xcassets */, - 33CC10F42044A3C60003C045 /* MainMenu.xib */, - 33CC10F72044A3C60003C045 /* Info.plist */, - ); - name = Resources; - path = ..; - sourceTree = ""; - }; - 33CEB47122A05771004F2AC0 /* Flutter */ = { - isa = PBXGroup; - children = ( - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, - ); - path = Flutter; - sourceTree = ""; - }; - 33FAB671232836740065AC1E /* Runner */ = { - isa = PBXGroup; - children = ( - CDF4BDDD28F9A6920059DB87 /* RunnerDebug.entitlements */, - 33CC10F02044A3C60003C045 /* AppDelegate.swift */, - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, - 33E51913231747F40026EE4D /* DebugProfile.entitlements */, - 33E51914231749380026EE4D /* Release.entitlements */, - 33CC11242044D66E0003C045 /* Resources */, - 33BA886A226E78AF003329D5 /* Configs */, - ); - path = Runner; - sourceTree = ""; - }; - 3A21EEB06B73EAB5B839B688 /* Pods */ = { - isa = PBXGroup; - children = ( - 1CCCE971B34B1EF0E9C23CAC /* Pods-Runner.debug.xcconfig */, - 929AC42CE2CDA27FE9235234 /* Pods-Runner.release.xcconfig */, - D4F4A1B883B395E86A7B745A /* Pods-Runner.profile.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; - D73912EC22F37F3D000D13A0 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 782D013032AAC6BDCC56A273 /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 33CC10EC2044A3C60003C045 /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 1A517F7352EDBCB64556EE14 /* [CP] Check Pods Manifest.lock */, - 33CC10E92044A3C60003C045 /* Sources */, - 33CC10EA2044A3C60003C045 /* Frameworks */, - 33CC10EB2044A3C60003C045 /* Resources */, - 33CC110E2044A8840003C045 /* Bundle Framework */, - 3399D490228B24CF009A79C7 /* ShellScript */, - 5EA8C195DB09CB61CDB56B17 /* [CP] Embed Pods Frameworks */, - 61600C7AED8D72CB17EDB92D /* [CP] Copy Pods Resources */, - ); - buildRules = ( - ); - dependencies = ( - 33CC11202044C79F0003C045 /* PBXTargetDependency */, - ); - name = Runner; - productName = Runner; - productReference = 33CC10ED2044A3C60003C045 /* stream_chat_v1.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 33CC10E52044A3C60003C045 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1510; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 33CC10EC2044A3C60003C045 = { - CreatedOnToolsVersion = 9.2; - LastSwiftMigration = 1100; - SystemCapabilities = { - com.apple.Sandbox = { - enabled = 1; - }; - }; - }; - 33CC111A2044C6BA0003C045 = { - CreatedOnToolsVersion = 9.2; - ProvisioningStyle = Manual; - }; - }; - }; - buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 33CC10E42044A3C60003C045; - productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 33CC10EC2044A3C60003C045 /* Runner */, - 33CC111A2044C6BA0003C045 /* Flutter Assemble */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 33CC10EB2044A3C60003C045 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, - 0D4CBA18257CE68ED24F92E8 /* GoogleService-Info.plist in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 1A517F7352EDBCB64556EE14 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 3399D490228B24CF009A79C7 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; - }; - 33CC111E2044C6BF0003C045 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - Flutter/ephemeral/FlutterInputs.xcfilelist, - ); - inputPaths = ( - Flutter/ephemeral/tripwire, - ); - outputFileListPaths = ( - Flutter/ephemeral/FlutterOutputs.xcfilelist, - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; - }; - 5EA8C195DB09CB61CDB56B17 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 61600C7AED8D72CB17EDB92D /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 33CC10E92044A3C60003C045 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; - targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { - isa = PBXVariantGroup; - children = ( - 33CC10F52044A3C60003C045 /* Base */, - ); - name = MainMenu.xib; - path = Runner; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 338D0CE9231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 11.5; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Profile; - }; - 338D0CEA231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 11.5; - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Profile; - }; - 338D0CEB231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Profile; - }; - 33CC10F92044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 11.5; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 33CC10FA2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 11.5; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Release; - }; - 33CC10FC2044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/RunnerDebug.entitlements; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 11.5; - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 33CC10FD2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 11.5; - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - 33CC111C2044C6BA0003C045 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 33CC111D2044C6BA0003C045 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10F92044A3C60003C045 /* Debug */, - 33CC10FA2044A3C60003C045 /* Release */, - 338D0CE9231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10FC2044A3C60003C045 /* Debug */, - 33CC10FD2044A3C60003C045 /* Release */, - 338D0CEA231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC111C2044C6BA0003C045 /* Debug */, - 33CC111D2044C6BA0003C045 /* Release */, - 338D0CEB231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 33CC10E52044A3C60003C045 /* Project object */; -} diff --git a/sample_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/sample_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/sample_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/sample_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/sample_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index ce0fd11fd0..0000000000 --- a/sample_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/sample_app/macos/Runner.xcworkspace/contents.xcworkspacedata b/sample_app/macos/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 21a3cc14c7..0000000000 --- a/sample_app/macos/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/sample_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/sample_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/sample_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/sample_app/macos/Runner/AppDelegate.swift b/sample_app/macos/Runner/AppDelegate.swift deleted file mode 100644 index b3c1761412..0000000000 --- a/sample_app/macos/Runner/AppDelegate.swift +++ /dev/null @@ -1,13 +0,0 @@ -import Cocoa -import FlutterMacOS - -@main -class AppDelegate: FlutterAppDelegate { - override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { - return true - } - - override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { - return true - } -} diff --git a/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 96d3fee1a2..0000000000 --- a/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "info": { - "version": 1, - "author": "xcode" - }, - "images": [ - { - "size": "16x16", - "idiom": "mac", - "filename": "app_icon_16.png", - "scale": "1x" - }, - { - "size": "16x16", - "idiom": "mac", - "filename": "app_icon_32.png", - "scale": "2x" - }, - { - "size": "32x32", - "idiom": "mac", - "filename": "app_icon_32.png", - "scale": "1x" - }, - { - "size": "32x32", - "idiom": "mac", - "filename": "app_icon_64.png", - "scale": "2x" - }, - { - "size": "128x128", - "idiom": "mac", - "filename": "app_icon_128.png", - "scale": "1x" - }, - { - "size": "128x128", - "idiom": "mac", - "filename": "app_icon_256.png", - "scale": "2x" - }, - { - "size": "256x256", - "idiom": "mac", - "filename": "app_icon_256.png", - "scale": "1x" - }, - { - "size": "256x256", - "idiom": "mac", - "filename": "app_icon_512.png", - "scale": "2x" - }, - { - "size": "512x512", - "idiom": "mac", - "filename": "app_icon_512.png", - "scale": "1x" - }, - { - "size": "512x512", - "idiom": "mac", - "filename": "app_icon_1024.png", - "scale": "2x" - } - ] -} \ No newline at end of file diff --git a/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png deleted file mode 100644 index 6286cc9f76..0000000000 Binary files a/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png and /dev/null differ diff --git a/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png deleted file mode 100644 index c8b042eda5..0000000000 Binary files a/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png and /dev/null differ diff --git a/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png deleted file mode 100644 index bc3b6c5709..0000000000 Binary files a/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png and /dev/null differ diff --git a/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png deleted file mode 100644 index 1003e61bab..0000000000 Binary files a/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png and /dev/null differ diff --git a/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png deleted file mode 100644 index 1457317de4..0000000000 Binary files a/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png and /dev/null differ diff --git a/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png deleted file mode 100644 index e362036687..0000000000 Binary files a/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png and /dev/null differ diff --git a/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png deleted file mode 100644 index c4d5157eca..0000000000 Binary files a/sample_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png and /dev/null differ diff --git a/sample_app/macos/Runner/Base.lproj/MainMenu.xib b/sample_app/macos/Runner/Base.lproj/MainMenu.xib deleted file mode 100644 index 537341abf9..0000000000 --- a/sample_app/macos/Runner/Base.lproj/MainMenu.xib +++ /dev/null @@ -1,339 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/sample_app/macos/Runner/Configs/AppInfo.xcconfig b/sample_app/macos/Runner/Configs/AppInfo.xcconfig deleted file mode 100644 index 66cfb8165f..0000000000 --- a/sample_app/macos/Runner/Configs/AppInfo.xcconfig +++ /dev/null @@ -1,14 +0,0 @@ -// Application-level settings for the Runner target. -// -// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the -// future. If not, the values below would default to using the project name when this becomes a -// 'flutter create' template. - -// The application's name. By default this is also the title of the Flutter window. -PRODUCT_NAME = stream_chat_v1 - -// The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = io.getstream.streamChatV1 - -// The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright Ā© 2021 io.getstream. All rights reserved. diff --git a/sample_app/macos/Runner/Configs/Debug.xcconfig b/sample_app/macos/Runner/Configs/Debug.xcconfig deleted file mode 100644 index 36b0fd9464..0000000000 --- a/sample_app/macos/Runner/Configs/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Debug.xcconfig" -#include "Warnings.xcconfig" diff --git a/sample_app/macos/Runner/Configs/Release.xcconfig b/sample_app/macos/Runner/Configs/Release.xcconfig deleted file mode 100644 index dff4f49561..0000000000 --- a/sample_app/macos/Runner/Configs/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Release.xcconfig" -#include "Warnings.xcconfig" diff --git a/sample_app/macos/Runner/Configs/Warnings.xcconfig b/sample_app/macos/Runner/Configs/Warnings.xcconfig deleted file mode 100644 index 42bcbf4780..0000000000 --- a/sample_app/macos/Runner/Configs/Warnings.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings -GCC_WARN_UNDECLARED_SELECTOR = YES -CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES -CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE -CLANG_WARN__DUPLICATE_METHOD_MATCH = YES -CLANG_WARN_PRAGMA_PACK = YES -CLANG_WARN_STRICT_PROTOTYPES = YES -CLANG_WARN_COMMA = YES -GCC_WARN_STRICT_SELECTOR_MATCH = YES -CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES -CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES -GCC_WARN_SHADOW = YES -CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/sample_app/macos/Runner/DebugProfile.entitlements b/sample_app/macos/Runner/DebugProfile.entitlements deleted file mode 100644 index 0eaccf1418..0000000000 --- a/sample_app/macos/Runner/DebugProfile.entitlements +++ /dev/null @@ -1,16 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.cs.allow-jit - - com.apple.security.files.user-selected.read-write - - com.apple.security.network.client - - com.apple.security.network.server - - - diff --git a/sample_app/macos/Runner/GoogleService-Info.plist b/sample_app/macos/Runner/GoogleService-Info.plist deleted file mode 100644 index 0d4409dd1c..0000000000 --- a/sample_app/macos/Runner/GoogleService-Info.plist +++ /dev/null @@ -1,38 +0,0 @@ - - - - - CLIENT_ID - 674907137625-p3msks3snq0h22l7ekpqcf0frr0vt8mg.apps.googleusercontent.com - REVERSED_CLIENT_ID - com.googleusercontent.apps.674907137625-p3msks3snq0h22l7ekpqcf0frr0vt8mg - ANDROID_CLIENT_ID - 674907137625-2scfo9a5cs074dced5vhm712ej6hhtpm.apps.googleusercontent.com - API_KEY - AIzaSyBTAsaKFUPLAJqfsLiz0yPUVzwrgJkOwSE - GCM_SENDER_ID - 674907137625 - PLIST_VERSION - 1 - BUNDLE_ID - io.getstream.streamChatV1 - PROJECT_ID - stream-chat-internal - STORAGE_BUCKET - stream-chat-internal.appspot.com - IS_ADS_ENABLED - - IS_ANALYTICS_ENABLED - - IS_APPINVITE_ENABLED - - IS_GCM_ENABLED - - IS_SIGNIN_ENABLED - - GOOGLE_APP_ID - 1:674907137625:ios:c719c700198c28b1d7f348 - DATABASE_URL - https://stream-chat-internal.firebaseio.com - - \ No newline at end of file diff --git a/sample_app/macos/Runner/Info.plist b/sample_app/macos/Runner/Info.plist deleted file mode 100644 index 4789daa6a4..0000000000 --- a/sample_app/macos/Runner/Info.plist +++ /dev/null @@ -1,32 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIconFile - - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSMinimumSystemVersion - $(MACOSX_DEPLOYMENT_TARGET) - NSHumanReadableCopyright - $(PRODUCT_COPYRIGHT) - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - - diff --git a/sample_app/macos/Runner/MainFlutterWindow.swift b/sample_app/macos/Runner/MainFlutterWindow.swift deleted file mode 100644 index 2722837ec9..0000000000 --- a/sample_app/macos/Runner/MainFlutterWindow.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Cocoa -import FlutterMacOS - -class MainFlutterWindow: NSWindow { - override func awakeFromNib() { - let flutterViewController = FlutterViewController.init() - let windowFrame = self.frame - self.contentViewController = flutterViewController - self.setFrame(windowFrame, display: true) - - RegisterGeneratedPlugins(registry: flutterViewController) - - super.awakeFromNib() - } -} diff --git a/sample_app/macos/Runner/Release.entitlements b/sample_app/macos/Runner/Release.entitlements deleted file mode 100644 index a0463869a9..0000000000 --- a/sample_app/macos/Runner/Release.entitlements +++ /dev/null @@ -1,12 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.files.user-selected.read-write - - com.apple.security.network.client - - - diff --git a/sample_app/macos/Runner/RunnerDebug.entitlements b/sample_app/macos/Runner/RunnerDebug.entitlements deleted file mode 100644 index 0eaccf1418..0000000000 --- a/sample_app/macos/Runner/RunnerDebug.entitlements +++ /dev/null @@ -1,16 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.cs.allow-jit - - com.apple.security.files.user-selected.read-write - - com.apple.security.network.client - - com.apple.security.network.server - - - diff --git a/sample_app/macos/firebase_app_id_file.json b/sample_app/macos/firebase_app_id_file.json deleted file mode 100644 index 918fb78898..0000000000 --- a/sample_app/macos/firebase_app_id_file.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "file_generated_by": "FlutterFire CLI", - "purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory", - "GOOGLE_APP_ID": "1:674907137625:ios:c719c700198c28b1d7f348", - "FIREBASE_PROJECT_ID": "stream-chat-internal", - "GCM_SENDER_ID": "674907137625" -} \ No newline at end of file diff --git a/sample_app/pubspec.yaml b/sample_app/pubspec.yaml deleted file mode 100644 index e43a9b730d..0000000000 --- a/sample_app/pubspec.yaml +++ /dev/null @@ -1,66 +0,0 @@ -name: sample_app -description: A new Flutter project. -publish_to: "none" -version: 2.2.0 - -# Note: The environment configuration and dependency versions are managed by Melos. -# -# Do not edit them manually. -# -# Steps to update dependencies: -# 1. Modify the version in the melos.yaml file. -# 2. Run `melos bootstrap` to apply changes. -# -# Steps to add a new dependency: -# 1. Add the dependency to this list. -# 2. Add it to the melos.yaml file for future updates. - -environment: - sdk: ^3.6.2 - flutter: ">=3.27.4" - -dependencies: - collection: ^1.17.2 - firebase_core: ^3.0.0 - firebase_messaging: ^15.0.0 - flutter: - sdk: flutter - flutter_app_badger: ^1.5.0 - flutter_local_notifications: ^18.0.1 - flutter_secure_storage: ^9.2.2 - flutter_slidable: ^3.1.1 - flutter_svg: ^2.0.10+1 - go_router: ^14.6.2 - lottie: ^3.1.2 - provider: ^6.0.5 - sentry_flutter: ^8.3.0 - stream_chat_flutter: ^9.4.0 - stream_chat_localizations: ^9.4.0 - stream_chat_persistence: ^9.4.0 - streaming_shared_preferences: ^2.0.0 - uuid: ^4.4.0 - video_player: ^2.8.7 - yaml: ^3.1.2 - -dev_dependencies: - flutter_launcher_icons: ^0.14.2 - -flutter: - uses-material-design: true - assets: - - assets/ - - pubspec.lock - -flutter_icons: - image_path: "assets/ic_launcher.png" - - android: true - adaptive_icon_background: "assets/ic_launcher_background.png" - adaptive_icon_foreground: "assets/ic_launcher_foreground.png" - ios: true - web: - generate: true - windows: - generate: true - macos: - generate: true \ No newline at end of file diff --git a/sample_app/web/sql-wasm.js b/sql-wasm.js similarity index 100% rename from sample_app/web/sql-wasm.js rename to sql-wasm.js diff --git a/sample_app/web/sql-wasm.wasm b/sql-wasm.wasm similarity index 100% rename from sample_app/web/sql-wasm.wasm rename to sql-wasm.wasm diff --git a/tools/stream_svg_icons_generator.dart b/tools/stream_svg_icons_generator.dart deleted file mode 100644 index dc723e6f38..0000000000 --- a/tools/stream_svg_icons_generator.dart +++ /dev/null @@ -1,192 +0,0 @@ -import 'dart:io'; - -import 'package:code_builder/code_builder.dart'; -import 'package:dart_style/dart_style.dart'; -import 'package:path/path.dart' as path; -import 'package:recase/recase.dart'; - -const _streamSvgIconsClassDocs = ''' -/// Identifiers for the supported Stream SVG icons. -/// -/// Use with the [StreamSvgIcon] class to show specific icons. Icons are -/// identified by their name as listed below, e.g. [StreamSvgIcons.settings]. -/// -/// {@tool snippet} -/// This example shows how to create a [Row] of [StreamSvgIcon]s in different -/// colors and sizes. The first [StreamSvgIcon] uses a -/// [StreamSvgIcon.semanticLabel] to announce in accessibility modes like -/// TalkBack and VoiceOver. -/// -/// The following code snippet would generate a row of icons consisting of a -/// pink heart, a green musical note, and a blue umbrella, each progressively -/// bigger than the last. -/// -/// ```dart -/// const Row( -/// mainAxisAlignment: MainAxisAlignment.spaceAround, -/// children: [ -/// StreamSvgIcon( -/// icon: StreamSvgIcons.settings, -/// color: Colors.pink, -/// size: 24.0, -/// semanticLabel: 'Text to announce in accessibility modes', -/// ), -/// StreamSvgIcon( -/// icon: StreamSvgIcons.lock, -/// color: Colors.green, -/// size: 30.0, -/// ), -/// StreamSvgIcon( -/// icon: StreamSvgIcons.mic, -/// color: Colors.blue, -/// size: 36.0, -/// ), -/// ], -/// ) -/// ``` -/// {@end-tool} -/// -/// See also: -/// -/// * [StreamSvgIcon] -/// * [IconButton]'''; - -Future main() async { - final iconsPath = path.join('lib', 'assets', 'icons'); - final svgIconFiles = await _getSvgIconFiles(iconsPath); - - final coloredIconsPath = path.join(iconsPath, 'colored'); - final coloredSvgIconFiles = await _getSvgIconFiles(coloredIconsPath); - - if (svgIconFiles.isEmpty && coloredSvgIconFiles.isEmpty) { - print('No SVG icons found.'); - return; - } - - final outputFile = File('lib/src/icons/stream_svg_icon.g.dart'); - - final clazz = Class( - (it) => it - ..docs.add(_streamSvgIconsClassDocs) - ..abstract = true - ..modifier = ClassModifier.final$ - ..name = 'StreamSvgIcons' - ..fields.addAll( - [ - // The package that contains the Stream SVG icons. - Field( - (it) => it - ..docs.add('/// The package that contains the Stream SVG icons.') - ..static = true - ..modifier = FieldModifier.constant - ..type = refer('String') - ..name = 'package' - ..assignment = const Code("'stream_chat_flutter'"), - ), - // Icon data for each SVG file - ...svgIconFiles.map( - (file) { - final iconFullName = path.basenameWithoutExtension(file.path); - final iconName = iconFullName.replaceFirst('icon_', ''); - final camelCasedIconName = ReCase(iconName).camelCase; - return Field( - (it) => it - ..docs.add("/// Stream SVG icon named '$camelCasedIconName'.") - ..static = true - ..modifier = FieldModifier.constant - ..type = refer('StreamSvgIconData') - ..name = camelCasedIconName - ..assignment = Code( - ''' - StreamSvgIconData( - '${file.path}', - package: package, - )''', - ), - ); - }, - ), - // Icon data for each colored SVG file - ...coloredSvgIconFiles.map( - (file) { - final iconFullName = path.basenameWithoutExtension(file.path); - final iconName = iconFullName.replaceFirst('icon_', ''); - final camelCasedIconName = ReCase(iconName).camelCase; - return Field( - (it) => it - ..docs.add("/// Stream SVG icon named '$camelCasedIconName'.") - ..static = true - ..modifier = FieldModifier.constant - ..type = refer('StreamSvgIconData') - ..name = camelCasedIconName - ..assignment = Code( - ''' - StreamSvgIconData( - '${file.path}', - package: package, - preserveColors: true, - )''', - ), - ); - }, - ), - ], - ), - ); - - final library = Library((it) => it.body.add(clazz)); - final clazzSink = library.accept(DartEmitter.scoped()); - - final formatter = DartFormatter( - languageVersion: DartFormatter.latestShortStyleLanguageVersion, - ); - - try { - await outputFile.writeAsString( - formatter.format( - ''' - // GENERATED CODE - DO NOT MODIFY BY HAND - // To regenerate, run: tools/stream_svg_icons_generator.dart - - part of 'stream_svg_icon.dart'; - - // ************************************************************************** - // StreamSvgIconsGenerator - // ************************************************************************** - - $clazzSink - ''', - ), - ); - } catch (e, stk) { - print('Error generating Stream SVG icons: $e\n$stk'); - } - - print('Generated ${outputFile.path}'); -} - -// The pattern to match SVG icon file names. Example: icon_name.svg -// -// This pattern is as follows: -// - icon_ : The prefix of the file name -// - ([a-z0-9]+_)* : A sequence of lowercase letters or digits followed by an -// underscore, repeated zero or more times -// - [a-z0-9]+ : A sequence of lowercase letters or digits -// - \.svg : The file extension -const _svgIconFileNamePattern = r'^icon_([a-z0-9]+_)*[a-z0-9]+\.svg$'; - -Future> _getSvgIconFiles(String iconsPath) async { - final iconsDirectory = Directory(iconsPath); - - if (!iconsDirectory.existsSync()) return []; - - final files = iconsDirectory.listSync().whereType(); - final svgIconFiles = files.where( - (file) { - final split = file.path.split(Platform.pathSeparator); - return split.last.contains(RegExp(_svgIconFileNamePattern)); - }, - ).toList(); - - return svgIconFiles; -} diff --git a/tools/version.sh b/tools/version.sh deleted file mode 100755 index 4578328c09..0000000000 --- a/tools/version.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -if ! command -v melos &> /dev/null -then - echo "melos could not be found" - exit 1 -fi - -if ! command -v yq &> /dev/null -then - echo "yq could not be found" - exit 1 -fi - -if [ $# -eq 0 ] - then - echo "You must provide a version number" - echo "Usage: ./tools/version.sh 1.0.0" - exit 1 -fi - -VERSION=$1 - -echo "Checking melos.yaml for packages" - -PACKAGES_PATH=$(yq eval '.packages' melos.yaml | tr -d '-' | tr -d '*') -PACKAGES=$(cd $PACKAGES_PATH && ls -d */ | tr -d '/') - -COMMAND="melos version --no-git-tag-version --no-changelog" - -for PACKAGE in $PACKAGES -do - echo "Setting version $VERSION for $PACKAGE" - COMMAND+=" -V $PACKAGE:$VERSION" -done - -eval $COMMAND - -VERSION_FILE=$PWD/packages/stream_chat/lib/version.dart - -echo "Updating $VERSION_FILE" - -echo "$(cat $VERSION_FILE | sed -E "s/[0-9]+\.[0-9]+\.[0-9]+/$VERSION/g")" > $PWD/packages/stream_chat/lib/version.dart \ No newline at end of file diff --git a/version.json b/version.json new file mode 100644 index 0000000000..c5745fa852 --- /dev/null +++ b/version.json @@ -0,0 +1 @@ +{"app_name":"sample_app","version":"2.2.0","package_name":"sample_app"} \ No newline at end of file