diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..b4df0d91 --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +ANDROID_MAP_API_KEY= +IOS_MAP_API_KEY= +HTTP_ENDPOINT= +WEBSOCKET_ENDPOINT= \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..d65b91c5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,34 @@ +--- +name: "🐞 Bug report" +about: Create a report to help us improve +title: "" +labels: "🐞 bug" +assignees: "" +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: + +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Demonstration** +If applicable, add screenshots or video to help explain your problem. + +**Environment** +- Device/emulator being used: +- OS (iOS/ Android); If Android then android version: +- Flutter version: + +**Additional context** +Add any other context about the problem here. + +**Are you working on this issue? (Yes/No)** \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 00000000..d24be768 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,9 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..0cf4fd4a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,21 @@ +--- +name: "⭐️ Feature request" +about: Suggest an idea for this project +title: "" +labels: "🌟 feature" +assignees: "" +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. + +**Are you working on this? (Yes/No)** \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..b4820f63 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,7 @@ +Fixes # + +Describe the changes you have made in this PR - + +Screenshots of the changes (If any) - + +Note: Please check Allow edits from maintainers. if you would like us to assist in the PR. \ No newline at end of file diff --git a/.github/workflows/flutter-ci.yml b/.github/workflows/flutter-ci.yml new file mode 100644 index 00000000..5c94e182 --- /dev/null +++ b/.github/workflows/flutter-ci.yml @@ -0,0 +1,53 @@ +name: Flutter CI + +on: + push: + branches: [main, development] + pull_request: + branches: [main, development] + +jobs: + test: + strategy: + matrix: + platform: [windows-latest, ubuntu-latest, macos-latest] + runs-on: ${{matrix.platform}} + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-java@v1 + with: + java-version: "12.x" + - uses: subosito/flutter-action@v2 + with: + # Temporary fix to failing CI + flutter-version: '3.22.2' + architecture: x64 + # channel: "stable" + - run: flutter pub get + - run: dart format . --set-exit-if-changed + - run: flutter analyze . + + build: + strategy: + matrix: + platform: [windows-latest, macos-latest, ubuntu-latest] + runs-on: ${{matrix.platform}} + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-java@v1 + with: + java-version: "12.x" + - uses: subosito/flutter-action@v2 + with: + # Temporary fix to failing CI + flutter-version: '3.22.2' + architecture: x64 + # channel: "stable" + - run: flutter pub get + - run: dart format . --set-exit-if-changed + - run: flutter analyze . + # - run: flutter build apk + # - uses: actions/upload-artifact@v1 + # with: + # name: release-apk + # path: build/app/outputs/apk/release/app-release.apk diff --git a/.gitignore b/.gitignore index 0fa6b675..45ed5324 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ .buildlog/ .history .svn/ +*.env # IntelliJ related *.iml @@ -44,3 +45,10 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release + +# Testing +/coverage/ + +# FVM Version Cache +.fvm/ +.gitignore \ No newline at end of file diff --git a/.metadata b/.metadata index 80206eb5..3e6e02af 100644 --- a/.metadata +++ b/.metadata @@ -4,7 +4,42 @@ # This file should be version controlled and should not be manually edited. version: - revision: c5a4b4029c0798f37c4a39b479d7cb75daa7b05c - channel: stable + revision: "78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9" + channel: "stable" project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + - platform: android + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + - platform: ios + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + - platform: linux + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + - platform: macos + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + - platform: web + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + - platform: windows + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + + # 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/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..a67b4a09 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,25 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "beacon", + "request": "launch", + "type": "dart" + }, + { + "name": "beacon (profile mode)", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "beacon (release mode)", + "request": "launch", + "type": "dart", + "flutterMode": "release" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 2fd7c0ad..e31227e5 --- a/README.md +++ b/README.md @@ -1,31 +1,106 @@ # Beacon +[![Flutter CI](https://github.com/CCExtractor/beacon/actions/workflows/flutter-ci.yml/badge.svg)](https://github.com/CCExtractor/beacon/actions/workflows/flutter-ci.yml) -This project is a flutter build native interface to ease the group travelling (or hiking). By using this, the group leader would be able to share his location with the entire crew, and in case if someone loses contact with the group, he can quickly get in the right place by following the beacon. +## About the Project + +This project is a flutter build native interface to ease the group travelling (or hiking). By using this, the group leader would be able to share his location with the entire crew, and in case if someone loses contact with the group, he can quickly get in the right place by following the beacon. ## Getting Started -For setting up the development environment, follow the steps given below. +You can test Beacon in your own development environment. This section shows you how: -1. For making changes to the project, fork this repository using the green button on this page. +### Prerequisites -2. Clone your fork or this repository, as applicable. +You'll need to set up the IDE and mobile device emulator, or any mobile testing device on your local system. -```bash -git clone https://github.com//beacon.git -cd beacon -``` +**Flutter Environment**: You'll need to have the following installed: +1. [Flutter SDK](https://flutter.dev/docs/get-started/install) +2. [Android Studio](https://developer.android.com/studio) -3. Check for flutter setup and connected devices +***Ensure you are testing the app using Flutter version [3.0.1](https://docs.flutter.dev/development/tools/sdk/releases?tab=windows) and above.*** -```bash -flutter doctor -``` +For checking flutter version: +- Run `flutter --version` in terminal -4. Run the app using -```bash -flutter run --no-sound-null-safety -``` +If your version is not upto date, follow these steps to upgrade: +- `flutter channel stable` to switch to the channel having stable version of flutter updates +- `flutter upgrade` to get the latest flutter version + +**Development Environment**: For setting up the development environment, follow the steps given below. + +- Clone this repository after forking using `git clone https://github.com//beacon.git` +- `cd` into `beacon` +- Check for flutter setup and connected devices using `flutter doctor` +- Get all the dependencies using `flutter pub get` +- Create a .env file in the root folder, copy the content from .env.example and add respected key's value +- Run the app using `flutter run` 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. \ No newline at end of file +samples, guidance on mobile development, and a full API reference. + +## Project Structure + +This project follows Clean architecture with following structure: + +```bash +beacon/ +├── lib/ +│ ├── config/ # Configuration files +│ ├── core/ # Core application logic +│ ├── data/ +│ │ ├── datasources/ +│ │ │ ├── local/ # Local data sources +│ │ │ └── remote/ # Remote data sources +│ │ ├── models/ # Data models +│ │ └── repositories/ # Data repositories +│ ├── domain/ +│ │ ├── entities/ # Domain entities +│ │ ├── repositories/ # Domain repositories +│ │ └── usecases/ # Domain use cases +│ ├── presentation/ +│ │ ├── auth/ +│ │ │ ├── cubit/ # Authentication Cubit +│ │ │ ├── widget/ # Authentication widgets +│ │ │ └── screen/ # Authentication screens +│ │ ├── home/ +│ │ │ ├── cubit/ # Home Cubit +│ │ │ ├── widget/ # Home widgets +│ │ │ └── screen/ # Home screens +│ │ ├── group/ +│ │ │ ├── cubit/ # Group Cubit +│ │ │ ├── widget/ # Group widgets +│ │ │ └── screen/ # Group screens +│ │ ├── hike/ +│ │ │ ├── cubit/ # Hike Cubit +│ │ │ ├── widget/ # Hike widgets +│ │ │ └── screen/ # Hike screens +│ │ ├── widgets/ # Shared widgets used across all presentation folders +│ │ └── splash_screen.dart # Initial screen displayed while loading +├── main.dart # App entry point +├── theme/ # Theme configurations +├── locator.dart # Dependency injection setup (using get_it) +├── router.dart # App navigation routes +``` + +## Screenshots + + + + + + + +## Contributing + +Whether you have some feauture requests/ideas, code improvements, refactoring, performance improvements, help is always Welcome. The more is done, better it gets. + +If you found any bugs, consider opening an [issue](https://github.com/CCExtractor/beacon/issues/new). + +**To know the details about features implemented till Google Summer of Code'21 and future todo's please visit [this blog](https://blog.nishthab.tech/gsoc-2021-ccextractor-beacon)** + +## Community + +We would love to hear from you! You may join gsoc-beacon channel of CCExtractor community through slack: + +[![Slack](https://img.shields.io/badge/chat-on_slack-purple.svg?style=for-the-badge&logo=slack)](https://ccextractor.org/public/general/support/) \ No newline at end of file diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 00000000..27dba309 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,27 @@ +# 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.dev/lints. + # + # 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/android/.settings/org.eclipse.buildship.core.prefs b/android/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 00000000..a50efddb --- /dev/null +++ b/android/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,13 @@ +arguments=--init-script /var/folders/87/sjnhgg2s1dn1b0l2r0jrgxx80000gn/T/d146c9752a26f79b52047fb6dc6ed385d064e120494f96f08ca63a317c41f94c.gradle --init-script /var/folders/87/sjnhgg2s1dn1b0l2r0jrgxx80000gn/T/52cde0cfcf3e28b8b7510e992210d9614505e0911af0c190bd590d7158574963.gradle +auto.sync=false +build.scans.enabled=false +connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) +connection.project.dir= +eclipse.preferences.version=1 +gradle.user.home= +java.home=/opt/homebrew/Cellar/openjdk/21.0.2/libexec/openjdk.jdk/Contents/Home +jvm.arguments= +offline.mode=false +override.workspace.settings=true +show.console.view=true +show.executions.view=true diff --git a/android/app/build.gradle b/android/app/build.gradle index 3c13d761..e1d57237 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -5,7 +5,6 @@ if (localPropertiesFile.exists()) { 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.") @@ -24,10 +23,10 @@ if (flutterVersionName == null) { apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - +apply from: project(':flutter_config').projectDir.getPath() + "/dotenv.gradle" android { - compileSdkVersion 30 + compileSdkVersion 34 buildToolsVersion '29.0.0' sourceSets { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index c40b9f6e..480c3165 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,46 +1,45 @@ + + - + + + - + android:usesCleartextTraffic="true" + android:requestLegacyExternalStorage="true"> + + + - - - + + + @@ -49,8 +48,7 @@ - + diff --git a/android/app/src/main/kotlin/com/example/beacon/MainActivity.kt b/android/app/src/main/kotlin/com/example/beacon/MainActivity.kt index 7e21f388..0553af8d 100644 --- a/android/app/src/main/kotlin/com/example/beacon/MainActivity.kt +++ b/android/app/src/main/kotlin/com/example/beacon/MainActivity.kt @@ -1,6 +1,21 @@ package com.example.beacon +import android.content.res.Configuration +import androidx.annotation.NonNull +import cl.puntito.simple_pip_mode.PipCallbackHelper import io.flutter.embedding.android.FlutterActivity +import io.flutter.embedding.engine.FlutterEngine -class MainActivity: FlutterActivity() { +class MainActivity : FlutterActivity() { + private var callbackHelper = PipCallbackHelper() + + override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + callbackHelper.configureFlutterEngine(flutterEngine) + } + + override fun onPictureInPictureModeChanged(active: Boolean, newConfig: Configuration?) { + super.onPictureInPictureModeChanged(active, newConfig) + callbackHelper.onPictureInPictureModeChanged(active) + } } diff --git a/android/app/src/main/res/drawable/app_icon.png b/android/app/src/main/res/drawable/app_icon.png new file mode 100644 index 00000000..a1f87e14 Binary files /dev/null and b/android/app/src/main/res/drawable/app_icon.png differ diff --git a/android/build.gradle b/android/build.gradle index c505a863..9b6b7376 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.3.50' + ext.kotlin_version = '1.9.22' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:7.1.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -16,6 +16,11 @@ allprojects { google() jcenter() } + configurations.all { + resolutionStrategy { + force "com.google.android.gms:play-services-location:21.0.1" + } + } } rootProject.buildDir = '../build' @@ -26,6 +31,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/Flutter.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/Flutter.build/dgph new file mode 100644 index 00000000..ffd418e3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/Flutter.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/GoogleMaps.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/GoogleMaps.build/dgph new file mode 100644 index 00000000..ffd418e3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/GoogleMaps.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/Pods-Runner.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/Pods-Runner.build/dgph new file mode 100644 index 00000000..ffd418e3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/Pods-Runner.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/ReachabilitySwift.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/ReachabilitySwift.build/dgph new file mode 100644 index 00000000..ffd418e3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/ReachabilitySwift.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/Toast.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/Toast.build/dgph new file mode 100644 index 00000000..ffd418e3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/Toast.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/connectivity_plus.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/connectivity_plus.build/dgph new file mode 100644 index 00000000..ffd418e3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/connectivity_plus.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/flutter_config.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/flutter_config.build/dgph new file mode 100644 index 00000000..ffd418e3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/flutter_config.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/flutter_local_notifications.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/flutter_local_notifications.build/dgph new file mode 100644 index 00000000..ffd418e3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/flutter_local_notifications.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/fluttertoast.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/fluttertoast.build/dgph new file mode 100644 index 00000000..ffd418e3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/fluttertoast.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/geolocator_apple.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/geolocator_apple.build/dgph new file mode 100644 index 00000000..ffd418e3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/geolocator_apple.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/google_maps_flutter_ios.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/google_maps_flutter_ios.build/dgph new file mode 100644 index 00000000..ffd418e3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/google_maps_flutter_ios.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/location.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/location.build/dgph new file mode 100644 index 00000000..ffd418e3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/location.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/modal_progress_hud_nsn.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/modal_progress_hud_nsn.build/dgph new file mode 100644 index 00000000..ffd418e3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/modal_progress_hud_nsn.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/path_provider_foundation.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/path_provider_foundation.build/dgph new file mode 100644 index 00000000..ffd418e3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/path_provider_foundation.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/share_plus.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/share_plus.build/dgph new file mode 100644 index 00000000..ffd418e3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/share_plus.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/shared_preferences_foundation.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/shared_preferences_foundation.build/dgph new file mode 100644 index 00000000..ffd418e3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/shared_preferences_foundation.build/dgph differ diff --git a/android/build/ios/Pods.build/Release-iphonesimulator/uni_links.build/dgph b/android/build/ios/Pods.build/Release-iphonesimulator/uni_links.build/dgph new file mode 100644 index 00000000..ffd418e3 Binary files /dev/null and b/android/build/ios/Pods.build/Release-iphonesimulator/uni_links.build/dgph differ diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 939efa29..302da314 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip \ No newline at end of file diff --git a/images/filter_icon.png b/images/filter_icon.png new file mode 100644 index 00000000..c0d1b31f Binary files /dev/null and b/images/filter_icon.png differ diff --git a/images/google.png b/images/google.png new file mode 100644 index 00000000..0ea3fd5b Binary files /dev/null and b/images/google.png differ diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index 9367d483..7c569640 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 8.0 + 12.0 diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig index 592ceee8..ec97fc6f 100644 --- a/ios/Flutter/Debug.xcconfig +++ b/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig index 592ceee8..c4855bfe 100644 --- a/ios/Flutter/Release.xcconfig +++ b/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 00000000..279576f3 --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,41 @@ +# 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/ios/Podfile.lock b/ios/Podfile.lock new file mode 100644 index 00000000..d5fe87e7 --- /dev/null +++ b/ios/Podfile.lock @@ -0,0 +1,109 @@ +PODS: + - connectivity_plus (0.0.1): + - Flutter + - ReachabilitySwift + - Flutter (1.0.0) + - flutter_config (0.0.1): + - Flutter + - flutter_local_notifications (0.0.1): + - Flutter + - fluttertoast (0.0.2): + - Flutter + - Toast + - geolocator_apple (1.2.0): + - Flutter + - google_maps_flutter_ios (0.0.1): + - Flutter + - GoogleMaps (< 9.0) + - GoogleMaps (5.2.0): + - GoogleMaps/Maps (= 5.2.0) + - GoogleMaps/Base (5.2.0) + - GoogleMaps/Maps (5.2.0): + - GoogleMaps/Base + - location (0.0.1): + - Flutter + - modal_progress_hud_nsn (0.0.1): + - Flutter + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - ReachabilitySwift (5.0.0) + - share_plus (0.0.1): + - Flutter + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS + - Toast (4.0.0) + - uni_links (0.0.1): + - Flutter + +DEPENDENCIES: + - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) + - Flutter (from `Flutter`) + - flutter_config (from `.symlinks/plugins/flutter_config/ios`) + - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) + - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) + - geolocator_apple (from `.symlinks/plugins/geolocator_apple/ios`) + - google_maps_flutter_ios (from `.symlinks/plugins/google_maps_flutter_ios/ios`) + - location (from `.symlinks/plugins/location/ios`) + - modal_progress_hud_nsn (from `.symlinks/plugins/modal_progress_hud_nsn/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) + - share_plus (from `.symlinks/plugins/share_plus/ios`) + - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) + - uni_links (from `.symlinks/plugins/uni_links/ios`) + +SPEC REPOS: + trunk: + - GoogleMaps + - ReachabilitySwift + - Toast + +EXTERNAL SOURCES: + connectivity_plus: + :path: ".symlinks/plugins/connectivity_plus/ios" + Flutter: + :path: Flutter + flutter_config: + :path: ".symlinks/plugins/flutter_config/ios" + flutter_local_notifications: + :path: ".symlinks/plugins/flutter_local_notifications/ios" + fluttertoast: + :path: ".symlinks/plugins/fluttertoast/ios" + geolocator_apple: + :path: ".symlinks/plugins/geolocator_apple/ios" + google_maps_flutter_ios: + :path: ".symlinks/plugins/google_maps_flutter_ios/ios" + location: + :path: ".symlinks/plugins/location/ios" + modal_progress_hud_nsn: + :path: ".symlinks/plugins/modal_progress_hud_nsn/ios" + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/darwin" + share_plus: + :path: ".symlinks/plugins/share_plus/ios" + shared_preferences_foundation: + :path: ".symlinks/plugins/shared_preferences_foundation/darwin" + uni_links: + :path: ".symlinks/plugins/uni_links/ios" + +SPEC CHECKSUMS: + connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + flutter_config: f48f0d47a284f1791aacce2687eabb3309ba7a41 + flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086 + fluttertoast: 31b00dabfa7fb7bacd9e7dbee580d7a2ff4bf265 + geolocator_apple: 9157311f654584b9bb72686c55fc02a97b73f461 + google_maps_flutter_ios: d1318b4ff711612cab16862d7a87e31a7403d458 + GoogleMaps: 025272d5876d3b32604e5c080dc25eaf68764693 + location: d5cf8598915965547c3f36761ae9cc4f4e87d22e + modal_progress_hud_nsn: f6fb744cd060653d66ed8f325360ef3650eb2fde + path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c + ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 + share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5 + shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 + Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 + uni_links: d97da20c7701486ba192624d99bffaaffcfc298a + +PODFILE CHECKSUM: c4c93c5f6502fe2754f48404d3594bf779584011 + +COCOAPODS: 1.15.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index d7b80c99..b4a0eff2 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -3,10 +3,11 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ + 1349DDD394F8B9121047E3AB /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A2C007CD47EB9021F4E926F /* 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 */; }; @@ -29,11 +30,13 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 038A92027DADC4EF9EBE927B /* 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 = ""; }; 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 = ""; }; + 78DB3DCCFD7099F03F9B4EAF /* 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 = ""; }; 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 = ""; }; @@ -42,6 +45,8 @@ 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 = ""; }; + 9A2C007CD47EB9021F4E926F /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CE30273B859892FC6E2E900E /* 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 */ @@ -49,12 +54,32 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 1349DDD394F8B9121047E3AB /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 30DF07BA73E75D70B602401E /* Pods */ = { + isa = PBXGroup; + children = ( + 78DB3DCCFD7099F03F9B4EAF /* Pods-Runner.debug.xcconfig */, + CE30273B859892FC6E2E900E /* Pods-Runner.release.xcconfig */, + 038A92027DADC4EF9EBE927B /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + 888D013527335CE2AB1C5009 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 9A2C007CD47EB9021F4E926F /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -72,6 +97,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, + 30DF07BA73E75D70B602401E /* Pods */, + 888D013527335CE2AB1C5009 /* Frameworks */, ); sourceTree = ""; }; @@ -105,12 +132,15 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 3A08507AD20C04E82854BAC1 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 5E3CB5F5DA464959BB57EDC1 /* [CP] Embed Pods Frameworks */, + 514063E86C8A8173E0053EFC /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -127,7 +157,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -169,12 +199,36 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 3A08507AD20C04E82854BAC1 /* [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; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( @@ -183,8 +237,43 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 514063E86C8A8173E0053EFC /* [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; + }; + 5E3CB5F5DA464959BB57EDC1 /* [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 = ( ); @@ -272,7 +361,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -290,7 +379,10 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.example.beacon; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -346,7 +438,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -395,7 +487,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -414,7 +506,10 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.example.beacon; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -433,7 +528,10 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.example.beacon; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -468,4 +566,4 @@ /* End XCConfigurationList section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; -} \ No newline at end of file +} diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a28140cf..e67b2808 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ + + diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 70693e4a..5f7cadae 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -1,5 +1,6 @@ import UIKit import Flutter +import GoogleMaps @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { @@ -7,7 +8,8 @@ import Flutter _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { + GMSServices.provideAPIKey("IOS_MAP_API_KEY") GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } -} +} \ No newline at end of file diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 6d16928c..11c9e17b 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -41,5 +41,13 @@ UIViewControllerBasedStatusBarAppearance + NSLocationWhenInUseUsageDescription + Your location is needed to guide you during the hike and show your location to the group + NSLocationAlwaysUsageDescription + Your location is needed to guide you during the hike and show your location to the group + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + diff --git a/ios/RunnerTests/RunnerTests.swift b/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 00000000..86a7c3b1 --- /dev/null +++ b/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/lib/components/create_join_dialog.dart b/lib/components/create_join_dialog.dart deleted file mode 100644 index 58b657f2..00000000 --- a/lib/components/create_join_dialog.dart +++ /dev/null @@ -1,239 +0,0 @@ -import 'package:beacon/locator.dart'; -import 'package:beacon/models/landmarks/landmark.dart'; -import 'package:beacon/services/validators.dart'; -import 'package:date_time_picker/date_time_picker.dart'; -import 'package:beacon/components/hike_button.dart'; -import 'package:beacon/utilities/constants.dart'; -import 'package:beacon/view_model/home_view_model.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_duration_picker/flutter_duration_picker.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; - -class CreateJoinBeaconDialog { - static Future createHikeDialog(BuildContext context, HomeViewModel model) { - model.resultingDuration = Duration(minutes: 30); - model.durationController = new TextEditingController(); - return showDialog( - context: context, - builder: (context) => Dialog( - child: Form( - key: model.formKeyCreate, - child: Container( - height: 325, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 32, vertical: 16), - child: Column( - children: [ - Container( - child: Padding( - padding: const EdgeInsets.all(4.0), - child: TextFormField( - validator: (value) => - Validator.validateBeaconTitle(value), - onChanged: (name) { - model.title = name; - }, - decoration: InputDecoration( - hintText: 'Title Here', - labelStyle: - TextStyle(fontSize: 18, color: kBlack), - labelText: 'Title', - alignLabelWithHint: true, - floatingLabelBehavior: - FloatingLabelBehavior.always, - focusedBorder: InputBorder.none, - enabledBorder: InputBorder.none), - ), - ), - color: kLightBlue, - ), - SizedBox( - height: 30, - ), - Container( - child: Padding( - padding: const EdgeInsets.all(4.0), - child: InkWell( - onTap: () async { - model.resultingDuration = - await showDurationPicker( - context: context, - initialTime: model.resultingDuration != null - ? model.resultingDuration - : Duration(minutes: 30), - ); - model.durationController.text = model - .resultingDuration - .toString() - .substring(0, 8); - }, - child: TextFormField( - enabled: false, - controller: model.durationController, - onChanged: (value) { - model.durationController.text = model - .resultingDuration - .toString() - .substring(0, 8); - }, - validator: (value) { - if (value.startsWith("0:00:00")) - return "Enter valid duration"; - return null; - }, - decoration: InputDecoration( - alignLabelWithHint: true, - floatingLabelBehavior: - FloatingLabelBehavior.always, - labelText: 'Duration', - labelStyle: - TextStyle(fontSize: 18, color: kBlack), - hintText: - 'How long should beacon last for?', - focusedBorder: InputBorder.none, - enabledBorder: InputBorder.none), - ), - ), - ), - color: kLightBlue, - ), - SizedBox( - height: 30, - ), - Flexible( - flex: 2, - child: HikeButton( - buttonWidth: 20, - buttonHeight: 20, - text: 'Create', - textColor: Colors.white, - buttonColor: kYellow, - onTap: () { - // navigationService.pop(); - model.createHikeRoom(); - }), - ), - ], - ), - ), - ), - ), - )); - } - - static Future joinBeaconDialog(BuildContext context, HomeViewModel model) { - return showDialog( - context: context, - builder: (context) => Dialog( - child: Form( - key: model.formKeyJoin, - child: Container( - height: 250, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 32, vertical: 16), - child: Column( - children: [ - Container( - child: Padding( - padding: const EdgeInsets.all(4.0), - child: TextFormField( - validator: (value) => - Validator.validatePasskey(value), - onChanged: (key) { - model.enteredPasskey = key; - }, - decoration: InputDecoration( - hintText: 'Passkey Here', - hintStyle: - TextStyle(fontSize: 20, color: kBlack), - labelText: 'Passkey', - labelStyle: - TextStyle(fontSize: 14, color: kYellow), - ), - ), - ), - color: kLightBlue, - ), - SizedBox( - height: 30, - ), - Flexible( - child: HikeButton( - buttonWidth: 48, - text: 'Validate', - textColor: Colors.white, - buttonColor: kYellow, - onTap: () { - navigationService.pop(); - model.joinHikeRoom(); - }), - ), - ], - ), - ), - ), - ), - )); - } - - static Future addLandmarkDialog( - BuildContext context, LatLng loc, String id) { - String title; - return showDialog( - context: context, - builder: (context) => Dialog( - child: Container( - height: 250, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), - child: Column( - children: [ - Container( - height: 100, - child: Padding( - padding: const EdgeInsets.all(4.0), - child: TextField( - onChanged: (key) { - title = key; - }, - decoration: InputDecoration( - alignLabelWithHint: true, - floatingLabelBehavior: FloatingLabelBehavior.always, - hintText: 'Add title for the landmark', - hintStyle: TextStyle(fontSize: 15, color: kBlack), - labelText: 'Title', - labelStyle: TextStyle(fontSize: 20, color: kYellow), - ), - ), - ), - color: kLightBlue, - ), - SizedBox( - height: 30, - ), - Flexible( - child: HikeButton( - buttonWidth: 25, - text: 'Create Landmark', - textColor: Colors.white, - buttonColor: kYellow, - onTap: () async { - navigationService.pop(); - await databaseFunctions.init(); - await databaseFunctions - .createLandmark(title, loc, id) - .then((value) { - return value; - }); - }), - ), - ], - ), - ), - ), - ), - ); - } -} diff --git a/lib/components/dialog_boxes.dart b/lib/components/dialog_boxes.dart deleted file mode 100644 index febf33b4..00000000 --- a/lib/components/dialog_boxes.dart +++ /dev/null @@ -1,85 +0,0 @@ -import 'package:beacon/components/hike_button.dart'; -import 'package:beacon/locator.dart'; -import 'package:beacon/utilities/constants.dart'; -import 'package:flutter/material.dart'; - -class DialogBoxes { - static AlertDialog showExitDialog( - BuildContext context, bool isLeader, int X) { - return AlertDialog( - title: Text( - 'This will terminate the hike, Confirm?', - style: TextStyle(fontSize: 25, color: kYellow), - ), - content: Text( - isLeader && (X - 1 > 0) - ? 'There are ${X - 1} followers and you are carrying the beacon. Do you want to terminate the hike?' - : 'Are you sure you want to terminate the hike?', - style: TextStyle(fontSize: 16, color: kBlack), - ), - actions: [ - HikeButton( - buttonHeight: 20, - buttonWidth: 40, - onTap: () => Navigator.of(context).pop(false), - text: 'No', - ), - HikeButton( - buttonHeight: 20, - buttonWidth: 40, - onTap: () { - navigationService.removeAllAndPush('/main', '/'); - }, - text: 'Yes', - ), - ], - ); - } - - static Future changeDurationDialog(BuildContext context) { - return showDialog( - context: context, - builder: (context) => Dialog( - child: Container( - height: 500, - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 32, vertical: 16), - child: Column( - children: [ - Flexible( - child: Container( - color: kLightBlue, - child: Column( - children: [ - Text( - 'Change Beacon Duration', - style: TextStyle(color: kYellow, fontSize: 12), - ), - ], - ), - ), - ), - SizedBox( - height: 30, - ), - Flexible( - child: HikeButton( - buttonWidth: 48, - text: 'Done', - textColor: Colors.white, - buttonColor: kYellow, - onTap: () { - // DateTime newTime = - // DateTime.now().add(newDuration); - // update time - Navigator.pop(context); - }), - ), - ], - ), - ), - ), - )); - } -} diff --git a/lib/components/hike_screen_widget.dart b/lib/components/hike_screen_widget.dart deleted file mode 100644 index c382d0b6..00000000 --- a/lib/components/hike_screen_widget.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:beacon/components/dialog_boxes.dart'; -import 'package:beacon/components/hike_button.dart'; -import 'package:beacon/locator.dart'; -import 'package:beacon/utilities/constants.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:fluttertoast/fluttertoast.dart'; -import 'package:share/share.dart'; - -class HikeScreenWidget { - static copyPasskey(String passkey) { - Clipboard.setData(ClipboardData(text: passkey)); - Fluttertoast.showToast(msg: 'PASSKEY: $passkey COPIED'); - } - - static generateUrl(String shortcode) async { - Uri url = Uri.parse('https://beacon.aadibajpai.com/?shortcode=$shortcode'); - Share.share('To join beacon follow this link: $url'); - } - - static Widget shareButton(BuildContext context, String passkey) { - return FloatingActionButton( - onPressed: () { - showDialog( - context: context, - builder: (context) => Dialog( - child: Container( - height: 400, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 32, vertical: 16), - child: Column( - children: [ - Container( - child: Padding( - padding: const EdgeInsets.all(4.0), - child: Text( - 'Invite Friends', - style: TextStyle(fontSize: 24), - ), - ), - ), - SizedBox( - height: 30, - ), - Flexible( - child: HikeButton( - textSize: 20, - text: 'Generate URL', - textColor: Colors.white, - buttonColor: kYellow, - onTap: () async { - generateUrl(passkey); - navigationService.pop(); - }), - ), - SizedBox( - height: 10, - ), - Flexible( - child: HikeButton( - textSize: 20, - text: 'Copy Passkey', - textColor: Colors.white, - buttonColor: kYellow, - onTap: () { - copyPasskey(passkey); - navigationService.pop(); - }, - ), - ) - ], - ), - ), - ), - )); - }, - backgroundColor: kYellow, - child: Icon(Icons.person_add), - ); - } -} diff --git a/lib/config/enviornment_config.dart b/lib/config/enviornment_config.dart new file mode 100644 index 00000000..0b8b8a40 --- /dev/null +++ b/lib/config/enviornment_config.dart @@ -0,0 +1,21 @@ +import 'dart:io'; + +import 'package:flutter_config/flutter_config.dart'; + +class EnvironmentConfig { + static String? get httpEndpoint => FlutterConfig.get('HTTP_ENDPOINT'); + + static String? get websocketEndpoint => + FlutterConfig.get('WEBSOCKET_ENDPOINT'); + + static String? get googleMapApi { + if (Platform.isAndroid) { + return FlutterConfig.get('ANDROID_MAP_API_KEY'); + } + return FlutterConfig.get('IOS_MAP_API_KEY'); + } + + static Future loadEnvVariables() async { + await FlutterConfig.loadEnvVariables(); + } +} diff --git a/lib/config/graphql_config.dart b/lib/config/graphql_config.dart new file mode 100644 index 00000000..924b1087 --- /dev/null +++ b/lib/config/graphql_config.dart @@ -0,0 +1,75 @@ +// import 'dart:async'; +// import 'package:beacon/config/enviornment_config.dart'; +// import 'package:beacon/locator.dart'; +// import 'package:graphql_flutter/graphql_flutter.dart'; + +import 'package:beacon/config/enviornment_config.dart'; +import 'package:beacon/locator.dart'; +import 'package:graphql_flutter/graphql_flutter.dart'; + +class GraphQLConfig { + static String? token; + WebSocketLink? _webSocketLink; + static final HttpLink httpLink = HttpLink( + EnvironmentConfig.httpEndpoint!, + ); + + Future _loadAuthLink() async { + // no need to load token, it will get load when _loadWebSocketLink + return AuthLink(getToken: () async => token); + } + + Future _loadWebSocketLink() async { + await _getToken(); + _webSocketLink = WebSocketLink(EnvironmentConfig.websocketEndpoint!, + config: SocketClientConfig( + autoReconnect: true, + initialPayload: {"Authorization": token}, + )); + + return _webSocketLink!; + } + + _getToken() async { + await localApi.init(); + await localApi.userloggedIn(); + token = localApi.userModel.authToken; + return true; + } + +// // for non auth clients + GraphQLClient clientToQuery() { + return GraphQLClient( + cache: GraphQLCache(), + link: httpLink, + ); + } + +// // for auth clients + Future authClient() async { + await _getToken(); + final AuthLink authLink = AuthLink(getToken: () async => '$token'); + final Link finalAuthLink = authLink.concat(httpLink); + return GraphQLClient( + cache: GraphQLCache(partialDataPolicy: PartialDataCachePolicy.accept), + link: finalAuthLink, + ); + } + + // for subscription + Future graphQlClient() async { + final websocketLink = await _loadWebSocketLink(); + final authLink = await _loadAuthLink(); + + return GraphQLClient( + cache: GraphQLCache( + partialDataPolicy: PartialDataCachePolicy.acceptForOptimisticData, + ), + link: Link.split( + (request) => request.isSubscription, + websocketLink, + authLink.concat(httpLink), + ), + ); + } +} diff --git a/lib/config/local_notification.dart b/lib/config/local_notification.dart new file mode 100644 index 00000000..99bf4cb3 --- /dev/null +++ b/lib/config/local_notification.dart @@ -0,0 +1,158 @@ +import 'dart:developer'; + +import 'package:beacon/config/router/router.dart'; +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/domain/usecase/hike_usecase.dart'; +import 'package:beacon/locator.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:timezone/timezone.dart' as tz; +import 'package:timezone/data/latest.dart' as tz; + +class LocalNotification { + static FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = + FlutterLocalNotificationsPlugin(); + +// initialise the plugin. app_icon needs to be a added as a drawable resource to the Android head project + Future initialize() async { + const AndroidInitializationSettings initializationSettingsAndroid = + AndroidInitializationSettings('app_icon'); + final DarwinInitializationSettings initializationSettingsIOS = + DarwinInitializationSettings( + onDidReceiveLocalNotification: (_, __, ___, ____) {} + // as Future Function(int, String?, String?, String?)? + ); + final InitializationSettings initializationSettings = + InitializationSettings( + android: initializationSettingsAndroid, + iOS: initializationSettingsIOS, + ); + tz.initializeTimeZones(); // <---- + + await flutterLocalNotificationsPlugin.initialize( + initializationSettings, + onDidReceiveNotificationResponse: (notificationResponse) => + onSelectNotification(notificationResponse), + ); + } + + Future onSelectNotification( + NotificationResponse notificationResponse) async { + String beaconId = notificationResponse.payload!; + + var hikeUseCase = locator(); + + // Fetch beacon details + final dataState = await hikeUseCase.fetchBeaconDetails(beaconId); + if (dataState is DataSuccess) { + var beacon = dataState.data; + bool isLeader = beacon!.leader!.id == localApi.userModel.id; + appRouter.push(HikeScreenRoute(beacon: beacon, isLeader: isLeader)); + } + } + + Future deleteNotification() async { + await flutterLocalNotificationsPlugin.cancelAll(); + } + + Future scheduleNotification(BeaconEntity beacon) async { + var scheduledDate1 = + tz.TZDateTime.fromMillisecondsSinceEpoch(tz.local, beacon.startsAt!); + + // Check if scheduledDate1 is in the future + if (scheduledDate1.isBefore(tz.TZDateTime.now(tz.local))) { + log('Scheduled date is not in the future.'); + return; + } + + // Schedule the notification for the beacon start time + await flutterLocalNotificationsPlugin.zonedSchedule( + beacon.id.hashCode, + 'Hike ' + beacon.title! + ' has started', + 'Click here to join!', + scheduledDate1, + NotificationDetails( + android: AndroidNotificationDetails( + 'channel id', + 'channel name', + playSound: true, + priority: Priority.high, + importance: Importance.high, + ), + iOS: DarwinNotificationDetails( + presentAlert: true, + presentBadge: true, + presentSound: true, + badgeNumber: 1, + ), + ), + uiLocalNotificationDateInterpretation: + UILocalNotificationDateInterpretation.absoluteTime, + androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle, + payload: beacon.id, + ); + + var scheduledDate2 = scheduledDate1.subtract(Duration(hours: 1)); + + if (scheduledDate2.isBefore(tz.TZDateTime.now(tz.local))) { + log('Reminder date is not in the future.'); + return; + } + + await flutterLocalNotificationsPlugin.zonedSchedule( + beacon.id.hashCode, + 'Reminder: ' + beacon.title! + ' will start in an hour', + 'Get Ready!', + scheduledDate2, + NotificationDetails( + android: AndroidNotificationDetails( + 'channel id', + 'channel name', + playSound: true, + priority: Priority.high, + importance: Importance.high, + ), + iOS: DarwinNotificationDetails( + presentAlert: true, + presentBadge: true, + presentSound: true, + badgeNumber: 1, + ), + ), + uiLocalNotificationDateInterpretation: + UILocalNotificationDateInterpretation.absoluteTime, + androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle, + payload: beacon.id, + ); + } + + Future> getPendingNotifications() async { + final pendingNotifications = + await flutterLocalNotificationsPlugin.pendingNotificationRequests(); + return pendingNotifications; + } + + Future showInstantNotification(String title, String body) async { + await flutterLocalNotificationsPlugin.show( + 0, + title, + body, + NotificationDetails( + android: AndroidNotificationDetails( + 'channel id', + 'channel name', + // 'this is description', + playSound: true, + priority: Priority.high, + importance: Importance.high, + ), + iOS: DarwinNotificationDetails( + presentAlert: true, + presentBadge: true, + presentSound: true, + badgeNumber: 1, + ), + ), + ); + } +} diff --git a/lib/config/pip_manager.dart b/lib/config/pip_manager.dart new file mode 100644 index 00000000..4c0e8e8f --- /dev/null +++ b/lib/config/pip_manager.dart @@ -0,0 +1,29 @@ +import 'package:flutter/services.dart'; + +class PIPMode { + static const platform = MethodChannel("com.example.beacon/pip"); + + // static Future enterPIPMode() async { + // try { + // await platform.invokeMethod('enablePIPMode'); + // } on PlatformException catch (e) { + // print("Failed to enter PIP mode: '${e.message}'."); + // } + // } + + static Future disablePIPMode() async { + try { + await platform.invokeMethod('disablePIPMode'); + } on PlatformException catch (e) { + print("Failed to enter PIP mode: '${e.message}'."); + } + } + + static Future switchPIPMode() async { + try { + await platform.invokeMethod('switchPIPMode'); + } on PlatformException catch (e) { + print("Failed to enter PIP mode: '${e.message}'."); + } + } +} diff --git a/lib/config/router/router.dart b/lib/config/router/router.dart new file mode 100644 index 00000000..7ff17774 --- /dev/null +++ b/lib/config/router/router.dart @@ -0,0 +1,29 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:beacon/domain/entities/group/group_entity.dart'; +import 'package:beacon/presentation/auth/verfication_screen.dart'; +import 'package:beacon/presentation/splash/splash_screen.dart'; +import 'package:beacon/presentation/home/home_screen.dart'; +import 'package:flutter/material.dart'; +import 'package:beacon/presentation/auth/auth_screen.dart'; +import 'package:beacon/presentation/group/group_screen.dart'; +import 'package:beacon/presentation/hike/hike_screen.dart'; +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +part 'router.gr.dart'; + +@AutoRouterConfig(replaceInRouteName: 'Route') +class AppRouter extends _$AppRouter { + @override + List get routes => [ + AutoRoute(page: SplashScreenRoute.page, initial: true, path: '/'), + AutoRoute(page: AuthScreenRoute.page, path: '/auth'), + AutoRoute(page: HomeScreenRoute.page, path: '/home'), + AutoRoute( + page: HikeScreenRoute.page, + path: '/hike/:hikeDetails', + ), + AutoRoute(page: GroupScreenRoute.page), + AutoRoute( + page: VerificationScreenRoute.page, + ), + ]; +} diff --git a/lib/config/router/router.gr.dart b/lib/config/router/router.gr.dart new file mode 100644 index 00000000..994646b0 --- /dev/null +++ b/lib/config/router/router.gr.dart @@ -0,0 +1,189 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ************************************************************************** +// AutoRouterGenerator +// ************************************************************************** + +// ignore_for_file: type=lint +// coverage:ignore-file + +part of 'router.dart'; + +abstract class _$AppRouter extends RootStackRouter { + // ignore: unused_element + _$AppRouter({super.navigatorKey}); + + @override + final Map pagesMap = { + AuthScreenRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const AuthScreen(), + ); + }, + GroupScreenRoute.name: (routeData) { + final args = routeData.argsAs(); + return AutoRoutePage( + routeData: routeData, + child: GroupScreen(args.group), + ); + }, + HikeScreenRoute.name: (routeData) { + final args = routeData.argsAs(); + return AutoRoutePage( + routeData: routeData, + child: HikeScreen( + key: args.key, + beacon: args.beacon, + isLeader: args.isLeader, + ), + ); + }, + HomeScreenRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const HomeScreen(), + ); + }, + SplashScreenRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const SplashScreen(), + ); + }, + VerificationScreenRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const VerificationScreen(), + ); + }, + }; +} + +/// generated route for +/// [AuthScreen] +class AuthScreenRoute extends PageRouteInfo { + const AuthScreenRoute({List? children}) + : super( + AuthScreenRoute.name, + initialChildren: children, + ); + + static const String name = 'AuthScreenRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [GroupScreen] +class GroupScreenRoute extends PageRouteInfo { + GroupScreenRoute({ + required GroupEntity group, + List? children, + }) : super( + GroupScreenRoute.name, + args: GroupScreenRouteArgs(group: group), + initialChildren: children, + ); + + static const String name = 'GroupScreenRoute'; + + static const PageInfo page = + PageInfo(name); +} + +class GroupScreenRouteArgs { + const GroupScreenRouteArgs({required this.group}); + + final GroupEntity group; + + @override + String toString() { + return 'GroupScreenRouteArgs{group: $group}'; + } +} + +/// generated route for +/// [HikeScreen] +class HikeScreenRoute extends PageRouteInfo { + HikeScreenRoute({ + Key? key, + required BeaconEntity beacon, + required bool? isLeader, + List? children, + }) : super( + HikeScreenRoute.name, + args: HikeScreenRouteArgs( + key: key, + beacon: beacon, + isLeader: isLeader, + ), + initialChildren: children, + ); + + static const String name = 'HikeScreenRoute'; + + static const PageInfo page = + PageInfo(name); +} + +class HikeScreenRouteArgs { + const HikeScreenRouteArgs({ + this.key, + required this.beacon, + required this.isLeader, + }); + + final Key? key; + + final BeaconEntity beacon; + + final bool? isLeader; + + @override + String toString() { + return 'HikeScreenRouteArgs{key: $key, beacon: $beacon, isLeader: $isLeader}'; + } +} + +/// generated route for +/// [HomeScreen] +class HomeScreenRoute extends PageRouteInfo { + const HomeScreenRoute({List? children}) + : super( + HomeScreenRoute.name, + initialChildren: children, + ); + + static const String name = 'HomeScreenRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [SplashScreen] +class SplashScreenRoute extends PageRouteInfo { + const SplashScreenRoute({List? children}) + : super( + SplashScreenRoute.name, + initialChildren: children, + ); + + static const String name = 'SplashScreenRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [VerificationScreen] +class VerificationScreenRoute extends PageRouteInfo { + const VerificationScreenRoute({List? children}) + : super( + VerificationScreenRoute.name, + initialChildren: children, + ); + + static const String name = 'VerificationScreenRoute'; + + static const PageInfo page = PageInfo(name); +} diff --git a/lib/config/service_location.dart b/lib/config/service_location.dart new file mode 100644 index 00000000..8e0f6393 --- /dev/null +++ b/lib/config/service_location.dart @@ -0,0 +1,29 @@ +import 'package:location/location.dart'; + +class ServiceLocation { + static LocationData? locationData; + Future getLocation() async { + Location location = new Location(); + + bool _serviceEnabled; + PermissionStatus _permissionGranted; + + _serviceEnabled = await location.serviceEnabled(); + if (!_serviceEnabled) { + _serviceEnabled = await location.requestService(); + if (!_serviceEnabled) { + return; + } + } + + _permissionGranted = await location.hasPermission(); + if (_permissionGranted == PermissionStatus.denied) { + _permissionGranted = await location.requestPermission(); + if (_permissionGranted != PermissionStatus.granted) { + return; + } + } + + locationData = await location.getLocation(); + } +} diff --git a/lib/core/queries/auth.dart b/lib/core/queries/auth.dart new file mode 100644 index 00000000..ce67feeb --- /dev/null +++ b/lib/core/queries/auth.dart @@ -0,0 +1,96 @@ +class AuthQueries { + String registerUser(String? name, String email, String? password) { + return ''' + mutation{ + register(user: {name: "$name", credentials: {email: "$email", password: "$password"}}) + { + _id + name + email + } + } + '''; + } + + String gAuth(String? name, String email) { + return ''' + mutation{ + oAuth(userInput: {email: "$email", name: "$name"}) + } + '''; + } + + String loginAsGuest(String? name) { + return ''' + mutation{ + register(user: {name: "$name"}) + { + _id + name + } + } + '''; + } + + String sendVerficationCode() { + return ''' + mutation{ + sendVerificationCode + } + '''; + } + + String completeVerificationCode() { + return ''' + mutation{ + completeVerification{ + _id + email + name + groups{ + _id + } + isVerified + beacons{ + _id + } + } + } + '''; + } + + String loginUser(String email, String? password) { + return ''' + mutation{ + login(credentials: {email: "$email", password: "$password"}) + } + '''; + } + + String loginUsingID(String? id) { + return ''' + mutation{ + login(id: "$id") + } + '''; + } + + String fetchUserInfo() { + return ''' + query{ + me{ + _id + email + name + isVerified + groups{ + _id + } + beacons{ + _id + } + } + } + '''; + } +} diff --git a/lib/core/queries/beacon.dart b/lib/core/queries/beacon.dart new file mode 100644 index 00000000..ea2735f5 --- /dev/null +++ b/lib/core/queries/beacon.dart @@ -0,0 +1,474 @@ +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:graphql_flutter/graphql_flutter.dart'; + +class BeaconQueries { + String filterBeacons(String groupId, String type) { + return '''query{ + filterBeacons(id:"$groupId", type: "$type"){ + _id + title + leader{ + _id + name + } + startsAt + expiresAt + shortcode + } + } +'''; + } + + String rescheduleHike(int newExpirestAt, int newStartsAt, String beaconId) { + return '''mutation{ rescheduleHike(newExpiresAt: $newExpirestAt, newStartsAt: $newStartsAt, beaconID: "$beaconId"){ + _id + title + shortcode + leader { + _id + name + } + group{ + _id + title + } + location{ + lat + lon + } + followers { + _id + name + } + startsAt + expiresAt + }}'''; + } + + String deleteBeacon(String? id) { + return ''' + +mutation{ +deleteBeacon(id: "$id") +} +'''; + } + + String changeLeader(String? beaconID, String? newLeaderID) { + return ''' + mutation{ + changeLeader (beaconID:"$beaconID" ,newLeaderID: "$newLeaderID") + { + _id + title + shortcode + leader { + _id + name + } + group{ + _id + title + } + location{ + lat + lon + } + followers { + _id + name + } + startsAt + expiresAt + landmarks { + title + location { + lat + lon + } + } + } + } + } + '''; + } + + String createBeacon(String? title, int startsAt, int expiresAt, String lat, + String lon, String? groupID) { + return ''' + mutation{ + createBeacon(beacon: { + title: "$title", + startsAt: $startsAt, + expiresAt: $expiresAt, + startLocation: { + lat: "$lat", lon: "$lon" + } + }, + groupID:"$groupID") + { + _id + title + shortcode + leader { + _id + name + email + beacons{ + _id + } + } + group { + _id + title + } + location{ + lat + lon + } + followers { + _id + name + } + startsAt + expiresAt + landmarks { + title + location { + lat + lon + } + } + } + } + '''; + } + + String updateBeaconLocation(String? id, String lat, String lon) { + return ''' + mutation { + updateBeaconLocation(id: "$id", location: {lat: "$lat", lon:"$lon"}){ + location{ + lat + lon + } + } + } + '''; + } + + String changeUserLocation(String? id, String lat, String lon) { + return ''' + mutation { + updateUserLocation(id: "$id", location: {lat: "$lat", lon:"$lon"}){ + location{ + lat + lon + } + } + } + '''; + } + + String addLandmark(String title, String lat, String lon, String id) { + return ''' + mutation{ + createLandmark( + landmark: { + title: "$title", + location: { + lat:"$lat", + lon:"$lon" + } + } + beaconID:"$id" + ){ + location{ + lat + lon + } + } + } + '''; + } + + String joinBeacon(String? shortcode) { + return ''' + mutation { + joinBeacon(shortcode: "$shortcode"){ + _id + title + shortcode + group{ + _id + title + } + leader { + _id + name + } + location { + lat + lon + } + followers { + _id + name + } + startsAt + expiresAt + } + } + '''; + } + + String fetchBeaconDetail(String? id) { + return ''' + query{ + beacon(id:"$id"){ + _id + title + leader{ + _id + name + location{ + lat + lon + } + } + group { + _id + title + } + followers { + _id + name + location{ + lat + lon + } + } + landmarks{ + _id + title + location{ + lat + lon + } + createdBy{ + _id + name + } + } + location{ + lat + lon + } + route{ + lat + lon + } + startsAt + expiresAt + shortcode + } + } + '''; + } + + String addRoute(String id, LatLng latlng) { + return ''' + mutation{ + addRoute( + id: "$id" + location:{ + lat: "${latlng.latitude}", + lon: "${latlng.longitude}" + } + ) + } + '''; + } + + String fetchNearbyBeacons(String id, String lat, String lon, double radius) { + return ''' + query { + nearbyBeacons( + id: "$id", + location:{ + lat: "$lat", + lon: "$lon" + }, + radius: $radius){ + _id + title + shortcode + group { + _id + title + } + leader { + _id + name + location { + lat + lon + } + } + location { + lat + lon + } + followers { + _id + name + } + startsAt + expiresAt + } + } + '''; + } + + final beaconLocationSubGql = gql(r''' + subscription StreamBeaconLocation($id: ID!){ + beaconLocation(id: $id){ + lat + lon + } + } + '''); + + // Gql for order updated subscription. + final beaconJoinedSubGql = gql(r''' + subscription StreamNewlyJoinedBeacons($id: ID!){ + beaconJoined(id: $id){ + name + location{ + lat + lon + } + } + } + '''); + + final joinleaveBeaconSubGql = gql(r''' + subscription StreamNewlyJoinedBeacons($id: ID!){ + JoinLeaveBeacon(id: $id){ + newfollower{ + _id + name + email + } + inactiveuser{ + _id + name + email + } + } + } + '''); + + final beaconUpdateSubGql = gql(r''' + subscription StreamBeaconUpdate($id: ID!){ + updateBeacon(id: $id){ + user{ + _id + name + email + location{ + lat + lon + } + } + + landmark{ + _id + title + location{ + lat + lon + } + } + + } + } + '''); + + String createLandmark(String? id, String lat, String lon, String? title) { + return ''' + mutation{ + createLandmark( + landmark:{ + location:{lat:"$lat", lon:"$lon"}, + title:"$title" + }, + beaconID:"$id") + { + _id + title + location{ + lat + lon + } + createdBy{ + _id + name + } + } + } + '''; + } + + String sos(String id) { + return ''' + mutation{ + sos( id:"$id"){ + _id + name + email + location{ + lat + lon + } + } + } + '''; + } + + final locationUpdateGQL = gql(r''' + subscription StreamLocationUpdate($id: ID!){ + beaconLocations(id: $id){ + + route{ + lat + lon + } + + updatedUser{ + _id + name + location{ + lat + lon + } + } + + landmark{ + _id + title + location{ + lat + lon + } + createdBy{ + _id + name + email + } + } + + } + } + '''); +} diff --git a/lib/core/queries/group.dart b/lib/core/queries/group.dart new file mode 100644 index 00000000..06cea688 --- /dev/null +++ b/lib/core/queries/group.dart @@ -0,0 +1,303 @@ +import 'package:graphql_flutter/graphql_flutter.dart'; + +class GroupQueries { + String fetchUserGroups(int page, int pageSize) { + return ''' + query { + groups(page: $page, pageSize: $pageSize) { + _id + title + beacons{ + _id + } + leader{ + _id + name + } + members{ + _id + name + } + shortcode + __typename + } + } + '''; + } + + String createGroup(String? title) { + return ''' + mutation{ + createGroup(group: { + title: "$title" + } + ) + { + _id + title + shortcode + leader { + _id + name + } + members { + _id + name + } + beacons + { + _id + title + shortcode + leader { + _id + name + } + location{ + lat + lon + } + followers { + _id + name + } + startsAt + expiresAt + } + } + } + '''; + } + + String joinGroup(String? shortcode) { + return ''' + mutation{ + joinGroup( + shortcode: "$shortcode" + ) + { + _id + title + shortcode + leader { + _id + name + } + members { + _id + name + } + beacons{ + _id + title + shortcode + leader { + _id + name + } + location{ + lat + lon + } + followers { + _id + name + } + startsAt + expiresAt + } + } + } + '''; + } + + String groupDetail(String? id) { + return ''' + query{ + group(id:"$id") + { + _id + title + shortcode + leader { + _id + name + } + members { + _id + name + } + beacons + { + _id + } + } + } + '''; + } + + String fetchHikes(String groupID, int page, int pageSize) { + return ''' +query{ + beacons(groupId: "$groupID", page: $page, pageSize: $pageSize){ + _id + title + shortcode + leader { + _id + name + } + location{ + lat + lon + } + followers { + _id + name + } + group{ + _id + } + startsAt + expiresAt + + } +} +'''; + } + + String changeShortCode(String groupId) { + return ''' + mutation{ + changeShortcode(groupId: "$groupId"){ + _id + title + beacons{ + _id + } + leader{ + _id + name + } + members{ + _id + name + } + shortcode + __typename + } + } + +'''; + } + + final groupUpdateSubGql = gql(r''' + subscription groupUpdate($groupIds: [ID!]!) { + groupUpdate(groupIds: $groupIds) { + groupId + + newUser{ + _id + name + email + } + + newBeacon{ + _id + title + leader { + _id + name + email + } + followers { + _id + name + } + group{ + _id + } + location { + lat + lon + } + shortcode + startsAt + expiresAt + } + + deletedBeacon{ + _id + title + leader { + _id + name + email + } + followers { + _id + name + } + group{ + _id + } + location { + lat + lon + } + shortcode + startsAt + expiresAt + } + + updatedBeacon{ + _id + title + leader { + _id + name + email + } + followers { + _id + name + } + group{ + _id + } + location { + lat + lon + } + shortcode + startsAt + expiresAt + } + } + } +'''); + + String removeMember(String groupId, String memberId) { + return ''' + mutation{ + removeMember(groupId: "$groupId", memberId: "$memberId"){ + _id + name + email + } + } + '''; + } + + final groupJoinedSubGql = gql(r''' + subscription StreamNewlyJoinedGroups($id: ID!){ + groupJoined(id: $id){ + name + location{ + lat + lon + } + } + } + '''); +} diff --git a/lib/core/resources/data_state.dart b/lib/core/resources/data_state.dart new file mode 100644 index 00000000..a6bdca91 --- /dev/null +++ b/lib/core/resources/data_state.dart @@ -0,0 +1,14 @@ +abstract class DataState { + final T? data; + final String? error; + + const DataState({this.data, this.error}); +} + +class DataSuccess extends DataState { + const DataSuccess(T data) : super(data: data); +} + +class DataFailed extends DataState { + const DataFailed(String error) : super(error: error); +} diff --git a/lib/core/services/location_services.dart b/lib/core/services/location_services.dart new file mode 100644 index 00000000..e8d1a8aa --- /dev/null +++ b/lib/core/services/location_services.dart @@ -0,0 +1,65 @@ +import 'package:geolocator/geolocator.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; + +class LocationService { + static final LocationService _instance = LocationService._internal(); + + LocationService._internal(); + + factory LocationService() { + return _instance; + } + + Position? _currentPosition; + Position? get currentPosition => _currentPosition; + + Future getCurrentLocation() async { + bool serviceEnabled; + LocationPermission permission; + + serviceEnabled = await Geolocator.isLocationServiceEnabled(); + + if (!serviceEnabled) { + return Future.error('Location service is disabled.'); + } + + permission = await Geolocator.checkPermission(); + + if (permission == LocationPermission.denied) { + permission = await Geolocator.requestPermission(); + if (permission == LocationPermission.denied) { + return Future.error('Location permission is denied'); + } + } + + if (permission == LocationPermission.deniedForever) { + return Future.error('Location permission is permanently denied.'); + } + + try { + Position location = await Geolocator.getCurrentPosition( + desiredAccuracy: LocationAccuracy.high); + + _currentPosition = location; + + return location; + } catch (e) { + return Future.error('Failed to get location: $e'); + } + } + + Future openSettings() async { + await Geolocator.openAppSettings(); + } + + Future calculateDistance(LatLng first, LatLng second) async { + double distanceInMeters = await Geolocator.distanceBetween( + first.latitude, + first.longitude, + second.latitude, + second.longitude, + ); + + return distanceInMeters; + } +} diff --git a/lib/core/services/shared_prefrence_service.dart b/lib/core/services/shared_prefrence_service.dart new file mode 100644 index 00000000..af083a85 --- /dev/null +++ b/lib/core/services/shared_prefrence_service.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class SharedPreferenceService { + static SharedPreferenceService _instance = + SharedPreferenceService._internal(); + + SharedPreferenceService._internal(); + + factory SharedPreferenceService() { + return _instance; + } + + late SharedPreferences _prefs; + + init() async { + _prefs = await SharedPreferences.getInstance(); + } + + Future saveData(String key, dynamic value) async { + try { + if (value is String) { + await _prefs.setString(key, value); + } else if (value is int) { + await _prefs.setInt(key, value); + } else if (value is bool) { + await _prefs.setBool(key, value); + } else if (value is double) { + await _prefs.setDouble(key, value); + } else if (value is List) { + await _prefs.setStringList(key, value); + } + } catch (e) { + debugPrint(e.toString()); + } + } + + Future loadData(String key) async { + return _prefs.get(key); + } + + Future deleteData(String key) async { + return await _prefs.remove(key); + } +} diff --git a/lib/utilities/constants.dart b/lib/core/utils/constants.dart similarity index 59% rename from lib/utilities/constants.dart rename to lib/core/utils/constants.dart index 68eed073..35de8fd7 100644 --- a/lib/utilities/constants.dart +++ b/lib/core/utils/constants.dart @@ -1,39 +1,33 @@ import 'package:flutter/material.dart'; -import 'package:geolocator/geolocator.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; const Color kYellow = Color(0xFFFDBB2C); const Color kBlue = Color(0xFF222375); +const Color lightkBlue = Color(0xFF535393); const Color kLightBlue = Color(0xFFE8F1F8); const Color kBlack = Color(0xFF343434); const Color shimmerSkeletonColor = Color(0xff4e4f91); +const Color hintColor = Colors.black54; // Hint Text Color +const double CAMERA_ZOOM = 15; +const double CAMERA_TILT = 80; +const double CAMERA_BEARING = 30; -class AppConstants { - static Future getLocation() async { - bool serviceEnabled; - LocationPermission permission; - - serviceEnabled = await Geolocator.isLocationServiceEnabled(); - if (!serviceEnabled) { - return Future.error('Location services are disabled.'); - } +// Button Sizings +const double labelsize = 24; +const double hintsize = 16; +final double homebheight = 4.5.h; +final double homebwidth = 3.5.w; +const double optbheight = 18.0; +const double optbwidth = 30.0; - permission = await Geolocator.checkPermission(); - if (permission == LocationPermission.denied) { - permission = await Geolocator.requestPermission(); - if (permission == LocationPermission.denied) { - return Future.error('Location permissions are denied'); - } - } +// GraphQL Registering Errors +const String exceptionError = "Exception Errors"; +const String otherError = "Other Errors"; +const String logSuccess = "Successful Login"; - if (permission == LocationPermission.deniedForever) { - return Future.error( - 'Location permissions are permanently denied, we cannot request permissions.'); - } - - Position position = await Geolocator.getCurrentPosition(); - return LatLng(position.latitude, position.longitude); - } +class AppConstants { + static const _imagePath = 'images'; + static const filterIconPath = '$_imagePath/filter_icon.png'; } //routes @@ -44,6 +38,7 @@ class Routes { static const String authScreen = "/auth"; static const String mainScreen = "/main"; static const String hikeScreen = "/hikeScreen"; + static const String groupScreen = "/groupScreen"; } class Style { diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart new file mode 100644 index 00000000..6b9021c8 --- /dev/null +++ b/lib/core/utils/utils.dart @@ -0,0 +1,71 @@ +import 'dart:developer'; +import 'package:beacon/core/utils/constants.dart'; +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:graphql/client.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; + +class Utils { + void showSnackBar(String message, BuildContext context, + {Duration duration = const Duration(seconds: 2), + bool icon = false, + bool top = false}) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + duration: duration, + content: Row( + children: [ + icon + ? Image.asset( + 'images/male_avatar.png', + height: 35, + ) + : Container(), + icon ? Gap(20) : Container(), + Text( + message, + style: TextStyle(color: Colors.black, fontSize: 12), + ) + ], + ), + backgroundColor: kLightBlue.withOpacity(0.8), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(10), + ), + ), + margin: top + ? EdgeInsets.only(top: 0, right: 10, left: 10, bottom: 85.h) + : null, + behavior: SnackBarBehavior.floating, + elevation: 5, + ), + ); + } + + String filterException(OperationException exception) { + // checking grapqhl exceptions + if (exception.graphqlErrors.isNotEmpty) { + return exception.graphqlErrors.first.message; + } + // checking link exception + else if (exception.linkException != null) { + log('Link Exception: ${exception.linkException!.originalStackTrace}'); + return 'Server exception'; + } else { + return 'Network Error: The request could not be completed.'; + } + } + + Future checkInternetConnectivity() async { + final connectivityResult = await Connectivity().checkConnectivity(); + + if (connectivityResult == ConnectivityResult.mobile || + connectivityResult == ConnectivityResult.wifi || + connectivityResult == ConnectivityResult.ethernet) { + return true; + } + return false; + } +} diff --git a/lib/core/utils/validators.dart b/lib/core/utils/validators.dart new file mode 100644 index 00000000..4d62cbe7 --- /dev/null +++ b/lib/core/utils/validators.dart @@ -0,0 +1,166 @@ +import 'package:flutter/material.dart'; + +class Validator { + static String? validateName(String? name) { + if (name != null && name.isEmpty) { + return "Name must not be left blank"; + } + return null; + } + + static String? validateRadius(String? radius) { + if (radius == null) { + return 'Radius can\'t be blank'; + } + try { + double.parse(radius); + + return null; + } catch (e) { + return 'Radius should be number'; + } + } + + static String? validateDate(String? date) { + if (date == null || date.isEmpty || date == '') { + return 'Date can\'t be empty'; + } + + DateTime parsedDate = DateTime.parse(date); + DateTime now = DateTime.now(); + + DateTime currentDate = DateTime(now.year, now.month, now.day); + DateTime onlyDate = + DateTime(parsedDate.year, parsedDate.month, parsedDate.day); + + if (onlyDate.isBefore(currentDate)) { + return 'Please enter a valid date'; + } + + return null; + } + + static String? validateStartTime(String? time, String date) { + DateTime parsedDate = DateTime.parse(date); + DateTime selectedDate = + DateTime(parsedDate.year, parsedDate.month, parsedDate.day); + DateTime now = DateTime.now(); + DateTime currentDate = DateTime(now.year, now.month, now.day); + + if (selectedDate.isAfter(currentDate)) { + return null; + } + + if (time == null || time.isEmpty) { + return 'Please chose a start time'; + } + + RegExp timeRegex = RegExp(r'^([0-1]?[0-9]|2[0-3]):[0-5][0-9] (AM|PM)$'); + + if (!timeRegex.hasMatch(time)) { + return 'Invalid time format. Please enter time in hh:mm AM/PM format'; + } + + TimeOfDay enteredTime = _stringToTimeOfDay(time); + + TimeOfDay currentTime = TimeOfDay.now(); + + int isValid = compareTimeOfDay(enteredTime, currentTime); + + if (isValid == -1) { + return 'Please chose a valid time'; + } + + return null; + } + + static int compareTimeOfDay(TimeOfDay time1, TimeOfDay time2) { + if (time1.hour < time2.hour) { + return -1; + } else if (time1.hour > time2.hour) { + return 1; + } else { + // If hours are the same, compare minutes + if (time1.minute < time2.minute) { + return -1; + } else if (time1.minute > time2.minute) { + return 1; + } else { + return 0; + } + } + } + + static TimeOfDay _stringToTimeOfDay(String time) { + final numbers = time.split(' '); + final format = numbers[0].split(":"); + final hour = int.parse(format[0]); + final minute = int.parse(format[1]); + + return TimeOfDay(hour: hour, minute: minute); + } + + static String? validateEmail(String? email) { + // If email is empty return. + if (email != null && email.isEmpty) { + return "Email must not be left blank"; + } + const String pattern = + r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?)*$"; + final RegExp regex = RegExp(pattern); + if (email != null && !regex.hasMatch(email)) { + return 'Please enter a valid Email Address'; + } + return null; + } + + static String? validatePassword(String? password) { + // If password is empty return. + if (password != null && password.isEmpty) { + return "Password must not be left blank"; + } + // const String pattern = r'^(?=.*?[0-9])(?=.*?[!@#\$&*%^~.]).{8,}$'; + // final RegExp regExp = RegExp(pattern); + + //Regex for no spaces allowed + const String noSpaces = r'^\S+$'; + final RegExp noSpaceRegex = RegExp(noSpaces); + + if (password!.length < 8) { + return "Must be of atleast 8 characters"; + } + // if (!regExp.hasMatch(password)) { + // return "At least 1 number and symbol required"; + // } + if (!noSpaceRegex.hasMatch(password)) { + return "Password must not contain spaces"; + } + return null; + } + + static String? validateBeaconTitle(String? title) { + if (title != null && title.isEmpty) { + return "Title must not be left blank"; + } + return null; + } + + static String? validatePasskey(String? passkey) { + if (passkey != null && passkey.isEmpty) { + return "Passkey must not be left blank"; + } + const String pattern = r'[A-Z]+'; + final RegExp regExp = RegExp(pattern); + if (!regExp.hasMatch(passkey!) || passkey.length != 6) { + return "Invalid passkey"; + } + return null; + } + + static String? validateDuration(String? duration) { + if (duration == null || duration.isEmpty || duration == '') { + return "Please enter duration"; + } + return null; + } +} diff --git a/lib/data/datasource/local/local_api.dart b/lib/data/datasource/local/local_api.dart new file mode 100644 index 00000000..13ace147 --- /dev/null +++ b/lib/data/datasource/local/local_api.dart @@ -0,0 +1,195 @@ +import 'dart:async'; +import 'dart:developer'; +import 'dart:io'; +import 'package:beacon/data/models/beacon/beacon_model.dart'; +import 'package:beacon/data/models/group/group_model.dart'; +import 'package:beacon/data/models/landmark/landmark_model.dart'; +import 'package:beacon/data/models/location/location_model.dart'; +import 'package:beacon/data/models/user/user_model.dart'; +import 'package:hive/hive.dart'; +import 'package:path_provider/path_provider.dart' as path_provider; + +class LocalApi { + String userModelbox = 'userBox'; + String groupModelBox = 'groupBox'; + String beaconModelBox = 'beaconBox'; + String locationModelBox = 'locationBox'; + String landMarkModelBox = 'landMarkBox'; + String nearbyBeaconModelBox = 'nearbybeaconBox'; + + late Box userBox; + late Box groupBox; + late Box beaconBox; + late Box nearbyBeaconBox; + late Box locationBox; + late Box landMarkbox; + + UserModel _userModel = UserModel(authToken: null); + UserModel get userModel => _userModel; + + init() async { + Directory directory = + await path_provider.getApplicationDocumentsDirectory(); + + Hive.init(directory.path); + + !Hive.isAdapterRegistered(10) + ? Hive.registerAdapter(UserModelAdapter()) + : null; + !Hive.isAdapterRegistered(20) + ? Hive.registerAdapter(BeaconModelAdapter()) + : null; + !Hive.isAdapterRegistered(30) + ? Hive.registerAdapter(GroupModelAdapter()) + : null; + + !Hive.isAdapterRegistered(40) + ? Hive.registerAdapter(LocationModelAdapter()) + : null; + !Hive.isAdapterRegistered(50) + ? Hive.registerAdapter(LandMarkModelAdapter()) + : null; + + try { + userBox = await Hive.openBox(userModelbox); + groupBox = await Hive.openBox(groupModelBox); + beaconBox = await Hive.openBox(beaconModelBox); + nearbyBeaconBox = await Hive.openBox(nearbyBeaconModelBox); + locationBox = await Hive.openBox(locationModelBox); + landMarkbox = await Hive.openBox(landMarkModelBox); + } catch (e) { + log('error: ${e.toString()}'); + } + } + + Future saveUser(UserModel user) async { + try { + _userModel = user; + await userBox.put('currentUser', user); + return true; + } catch (e) { + log(e.toString()); + return false; + } + } + + Future deleteUser() async { + // clearing the info + + try { + _userModel.copyWithModel( + authToken: null, + beacons: null, + email: null, + groups: null, + id: null, + isGuest: null, + location: null, + name: null); + await userBox.clear(); + await groupBox.clear(); + await beaconBox.clear(); + } catch (e) { + log(e.toString()); + } + } + + Future fetchUser() async { + try { + final user = await userBox.get('currentUser'); + return user; + } catch (e) { + log(e.toString()); + return null; + } + } + + Future userloggedIn() async { + try { + UserModel? user = await userBox.get('currentUser'); + + if (user == null) { + return false; + } + _userModel = user; + return true; + } catch (e) { + log(e.toString()); + return false; + } + } + + Future saveGroup(GroupModel group) async { + await deleteGroup(group.id); + try { + await groupBox.put(group.id, group); + return true; + } catch (e) { + log(e.toString()); + return false; + } + } + + Future deleteGroup(String? groupId) async { + try { + bool doesExist = await groupBox.containsKey(groupId); + doesExist ? await groupBox.delete(groupId) : null; + return true; + } catch (e) { + log(e.toString()); + return false; + } + } + + Future getGroup(String? groupId) async { + try { + final group = await groupBox.get(groupId); + return group; + } catch (e) { + log(e.toString()); + return null; + } + } + + Future saveBeacon(BeaconModel beacon) async { + try { + await deleteBeacon(beacon.id); + await beaconBox.put(beacon.id, beacon); + return true; + } catch (e) { + log(e.toString()); + return false; + } + } + + Future savenearbyBeacons(BeaconModel beacon) async { + try { + await deleteBeacon(beacon.id); + await nearbyBeaconBox.put(beacon.id, beacon); + return true; + } catch (e) { + return false; + } + } + + Future deleteBeacon(String? beaconId) async { + try { + bool doesExist = await beaconBox.containsKey(beaconId); + doesExist ? await beaconBox.delete(beaconId) : null; + return true; + } catch (e) { + log(e.toString()); + return false; + } + } + + Future getBeacon(String? beaconId) async { + try { + final beacon = await beaconBox.get(beaconId); + return beacon; + } catch (e) { + log(e.toString()); + return null; + } + } +} diff --git a/lib/data/datasource/remote/remote_auth_api.dart b/lib/data/datasource/remote/remote_auth_api.dart new file mode 100644 index 00000000..d2f8cb43 --- /dev/null +++ b/lib/data/datasource/remote/remote_auth_api.dart @@ -0,0 +1,245 @@ +import 'dart:async'; +import 'dart:developer'; + +import 'package:beacon/core/queries/auth.dart'; +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/data/datasource/remote/remote_group_api.dart'; +import 'package:beacon/data/datasource/remote/remote_hike_api.dart'; +import 'package:beacon/data/datasource/remote/remote_home_api.dart'; +import 'package:beacon/data/models/user/user_model.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:beacon/locator.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:graphql_flutter/graphql_flutter.dart'; + +class RemoteAuthApi { + GraphQLClient clientNonAuth; + late GraphQLClient _authClient; + + RemoteAuthApi(this._authClient, this.clientNonAuth); + + void loadClient(GraphQLClient newClient) { + _authClient = newClient; + } + + AuthQueries _authQueries = AuthQueries(); + + Future> fetchUserInfo() async { + final isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) + return DataFailed('Beacon is trying to connect with internet...'); + + var _authClient = await graphqlConfig.authClient(); + // api call + final result = await _authClient + .mutate(MutationOptions(document: gql(_authQueries.fetchUserInfo()))); + + if (result.data != null && result.isConcrete) { + final json = result.data!['me']; + final user = UserModel.fromJson(json); + + final currentUser = await localApi.fetchUser(); + + // checking if user is login + if (currentUser == null) return DataFailed('Please login first'); + final newUser = user.copyWithModel( + authToken: currentUser.authToken, + isGuest: user.email == '' ? true : false); + + // saving user details locally + await localApi.saveUser(newUser); + + // returning + return DataSuccess(newUser); + } + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + + Future> register( + String name, String email, String password) async { + try { + final isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) + return DataFailed('Beacon is trying to connect with internet...'); + + final result = await clientNonAuth.mutate( + MutationOptions( + document: gql(_authQueries.registerUser(name, email, password)), + ), + ); + + if (result.data != null && result.isConcrete) { + // LOGIN API CALL + final dataState = await login(email, password); + return dataState; + } else if (result.hasException) { + final message = encounteredExceptionOrError(result.exception!); + return DataFailed(message); + } + + return DataFailed('An unexpected error occurred during registration.'); + } catch (e) { + return DataFailed(e.toString()); + } + } + + Future> gAuth(String name, String email) async { + log('name: $name'); + log('email: $email'); + + final isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + return DataFailed('Beacon is trying to connect with internet...'); + } + + final QueryResult result = await clientNonAuth.mutate( + MutationOptions(document: gql(_authQueries.gAuth(name, email)))); + + log(result.toString()); + + if (result.data != null && result.isConcrete) { + final token = "Bearer ${result.data!['oAuth']}"; + + UserModel? user; + + user = UserModel(authToken: token, isGuest: false); + + // storing auth token in hive + await localApi.saveUser(user); + + // loading clients + final authClient = await graphqlConfig.authClient(); + final subscriptionClient = await graphqlConfig.graphQlClient(); + locator().loadClient(authClient); + locator().loadClient(authClient, subscriptionClient); + locator().loadClient(authClient, subscriptionClient); + locator().loadClient(authClient, subscriptionClient); + + // fetching User Info + + final dataState = await fetchUserInfo(); + + if (dataState is DataSuccess) { + final updatedUser = dataState.data! + .copyWithModel(authToken: user.authToken, isGuest: user.isGuest); + + // saving locally + await localApi.saveUser(updatedUser); + + return DataSuccess(updatedUser); + } + } else if (result.hasException) { + final message = encounteredExceptionOrError(result.exception!); + + return DataFailed(message); + } + + return DataFailed('An unexpected error occured.'); + } + + Future> login(String email, String password) async { + log('calling login function $email'); + final isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + return DataFailed('Beacon is trying to connect with internet...'); + } + + final QueryResult result = await clientNonAuth.mutate(MutationOptions( + document: gql(_authQueries.loginUser(email, password)))); + + if (result.data != null && result.isConcrete) { + final token = "Bearer ${result.data!['login']}"; + + UserModel? user; + + // if (email.isEmpty) { + // user = UserModel(authToken: token, isGuest: true); + // } else { + user = UserModel(authToken: token, isGuest: false); + // } + + // storing auth token in hive + await localApi.saveUser(user); + + // loading clients + final authClient = await graphqlConfig.authClient(); + final subscriptionClient = await graphqlConfig.graphQlClient(); + locator().loadClient(authClient); + locator().loadClient(authClient, subscriptionClient); + locator().loadClient(authClient, subscriptionClient); + locator().loadClient(authClient, subscriptionClient); + + // fetching User Info + + final dataState = await fetchUserInfo(); + + if (dataState is DataSuccess) { + final updatedUser = dataState.data! + .copyWithModel(authToken: user.authToken, isGuest: user.isGuest); + + // saving locally + await localApi.saveUser(updatedUser); + + return DataSuccess(updatedUser); + } + } else if (result.hasException) { + final message = encounteredExceptionOrError(result.exception!); + + return DataFailed(message); + } + + return DataFailed('An unexpected error occured.'); + } + + Future> sendVerificationCode() async { + final isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + return DataFailed('Beacon is trying to connect with internet...'); + } + + final QueryResult result = await _authClient.mutate( + MutationOptions(document: gql(_authQueries.sendVerficationCode()))); + + if (result.data != null && result.isConcrete) { + return DataSuccess(result.data!['sendVerificationCode'] as String); + } + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + + Future> completeVerification() async { + final isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + return DataFailed('Beacon is trying to connect with internet...'); + } + + var authClient = await graphqlConfig.authClient(); + + final QueryResult result = await authClient.mutate(MutationOptions( + document: gql(_authQueries.completeVerificationCode()))); + + if (result.data != null && result.isConcrete) { + var user = UserModel.fromJson(result.data!['completeVerification']); + var currentUser = await localApi.fetchUser(); + currentUser = currentUser!.copyWithModel(isVerified: user.isVerified); + await localApi.saveUser(currentUser); + return DataSuccess(user); + } + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + + String encounteredExceptionOrError(OperationException exception) { + if (exception.linkException != null) { + debugPrint(exception.linkException.toString()); + return 'Something went wrong'; + } else { + return exception.graphqlErrors[0].message.toString(); + } + } +} diff --git a/lib/data/datasource/remote/remote_group_api.dart b/lib/data/datasource/remote/remote_group_api.dart new file mode 100644 index 00000000..f1d3a8d8 --- /dev/null +++ b/lib/data/datasource/remote/remote_group_api.dart @@ -0,0 +1,243 @@ +import 'dart:async'; + +import 'package:beacon/core/queries/beacon.dart'; +import 'package:beacon/core/queries/group.dart'; +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/data/models/beacon/beacon_model.dart'; +import 'package:beacon/data/models/group/group_model.dart'; +import 'package:beacon/data/models/user/user_model.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:beacon/locator.dart'; +import 'package:graphql_flutter/graphql_flutter.dart'; + +class RemoteGroupApi { + late GraphQLClient _authClient; + + RemoteGroupApi(this._authClient); + + void loadClient(GraphQLClient authClient, GraphQLClient subscriptionClient) { + this._authClient = authClient; + } + + final _groupqueries = GroupQueries(); + + final _beaconQueries = BeaconQueries(); + + Future>> fetchHikes( + String groupId, int page, int pageSize) async { + bool isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + GroupModel? group = await localApi.getGroup(groupId); + + if (group != null && group.beacons != null) { + int condition = (page - 1) * pageSize + pageSize; + int beaconLen = group.beacons!.length; + + if (condition > beaconLen) { + condition = beaconLen; + } + + List beacons = []; + + for (int i = (page - 1) * pageSize; i < condition; i++) { + BeaconModel? beaconModel = + await localApi.getBeacon(group.beacons![i]!.id); + + beaconModel != null ? beacons.add(beaconModel) : null; + } + + return DataSuccess(beacons); + } + + return DataFailed('Please check your internet connection!'); + } + + final result = await _authClient.query(QueryOptions( + document: gql(_groupqueries.fetchHikes(groupId, page, pageSize)), + fetchPolicy: FetchPolicy.networkOnly)); + + if (result.data != null && result.isConcrete) { + List hikesJson = result.data!['beacons']; + + List hikes = []; + + for (var hikeJson in hikesJson) { + BeaconModel hike = BeaconModel.fromJson(hikeJson); + hikes.add(hike); + // storing beacon + await localApi.saveBeacon(hike); + } + + return DataSuccess(hikes); + } + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + + Future> createHike(String title, int startsAt, + int expiresAt, String lat, String lon, String groupID) async { + bool isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + return DataFailed('Please check your internet connection!'); + } + final result = await _authClient.mutate(MutationOptions( + document: gql(_beaconQueries.createBeacon( + title, startsAt, expiresAt, lat, lon, groupID)))); + + if (result.data != null && result.isConcrete) { + final hikeJson = result.data!['createBeacon']; + + final beacon = BeaconModel.fromJson(hikeJson); + + // storing beacon + await localApi.saveBeacon(beacon); + return DataSuccess(beacon); + } + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + + Future> joinHike(String shortcode) async { + bool isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + return DataFailed('Please check your internet connection!'); + } + final result = await _authClient.mutate( + MutationOptions(document: gql(_beaconQueries.joinBeacon(shortcode)))); + + if (result.data != null && result.isConcrete) { + final hikeJosn = result.data!['joinBeacon']; + + final beacon = BeaconModel.fromJson(hikeJosn); + + // storing beacon + await localApi.saveBeacon(beacon); + + return DataSuccess(beacon); + } + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + + Future>> nearbyBeacons( + String id, String lat, String lon, double radius) async { + bool isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + return DataFailed('Please check your internet connection!'); + } + final result = await _authClient.mutate(MutationOptions( + document: + gql(_beaconQueries.fetchNearbyBeacons(id, lat, lon, radius)))); + + if (result.data != null && + result.isConcrete && + result.data!['nearbyBeacons'] != null) { + List nearbyBeaconJson = result.data!['nearbyBeacons']; + + List nearbyBeacons = nearbyBeaconJson + .map((beaconJson) => BeaconModel.fromJson(beaconJson)) + .toList(); + + // storing beacons + for (var beacon in nearbyBeacons) { + await localApi.savenearbyBeacons(beacon); + } + + return DataSuccess(nearbyBeacons); + } + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + + Future>> filterBeacons( + String groupId, String type) async { + bool isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + return DataFailed('Please check your internet connection!'); + } + final result = await _authClient.mutate(MutationOptions( + document: gql(_beaconQueries.filterBeacons(groupId, type)))); + + if (result.data != null && + result.isConcrete && + result.data!['filterBeacons'] != null) { + List beaconsJson = result.data!['filterBeacons']; + + List beacons = beaconsJson + .map((beaconJson) => BeaconModel.fromJson(beaconJson)) + .toList(); + + return DataSuccess(beacons); + } else { + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + } + + Future> deleteBeacon(String? beaconId) async { + bool isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + return DataFailed('Please check your internet connection!'); + } + final result = await _authClient.mutate( + MutationOptions(document: gql(_beaconQueries.deleteBeacon(beaconId)))); + + if (result.data != null && + result.isConcrete && + result.data!['deleteBeacon'] != null) { + bool isDeleted = result.data!['deleteBeacon']; + + return DataSuccess(isDeleted); + } else { + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + } + + Future> rescheduleBeacon( + int newExpiresAt, int newStartsAt, String beaconId) async { + bool isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + return DataFailed('Please check your internet connection!'); + } + final result = await _authClient.mutate(MutationOptions( + document: gql(_beaconQueries.rescheduleHike( + newExpiresAt, newStartsAt, beaconId)))); + + if (result.data != null && + result.isConcrete && + result.data!['rescheduleHike'] != null) { + return DataSuccess(BeaconModel.fromJson(result.data!['rescheduleHike'])); + } else { + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + } + + Future> removeMember( + String groupId, String memberId) async { + bool isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + return DataFailed('Please check your internet connection!'); + } + final result = await _authClient.mutate(MutationOptions( + document: gql(_groupqueries.removeMember(groupId, memberId)))); + + if (result.data != null && + result.isConcrete && + result.data!['removeMember'] != null) { + return DataSuccess(UserModel.fromJson(result.data!['removeMember'])); + } else { + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + } + + String encounteredExceptionOrError(OperationException exception) { + if (exception.linkException != null) { + return 'Server not running'; + } else { + return exception.graphqlErrors[0].message.toString(); + } + } +} diff --git a/lib/data/datasource/remote/remote_hike_api.dart b/lib/data/datasource/remote/remote_hike_api.dart new file mode 100644 index 00000000..1317f018 --- /dev/null +++ b/lib/data/datasource/remote/remote_hike_api.dart @@ -0,0 +1,187 @@ +import 'dart:async'; +import 'dart:developer'; +import 'package:beacon/core/queries/beacon.dart'; +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/data/models/beacon/beacon_model.dart'; +import 'package:beacon/data/models/landmark/landmark_model.dart'; +import 'package:beacon/data/models/location/location_model.dart'; +import 'package:beacon/data/models/subscriptions/beacon_locations_model/beacon_locations_model.dart'; +import 'package:beacon/data/models/subscriptions/join_leave_beacon_model/join_leave_beacon_model.dart'; +import 'package:beacon/data/models/user/user_model.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:beacon/locator.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:graphql_flutter/graphql_flutter.dart'; + +class RemoteHikeApi { + late GraphQLClient _authClient; + late GraphQLClient _subscriptionClient; + + RemoteHikeApi(this._authClient, this._subscriptionClient); + + final beaconQueries = BeaconQueries(); + + void loadClient(GraphQLClient authClient, GraphQLClient subscriptionClient) { + this._authClient = authClient; + this._subscriptionClient = subscriptionClient; + } + + Future> fetchBeaconDetails(String beaconId) async { + bool isConnected = await utils.checkInternetConnectivity(); + if (!isConnected) {} + + final result = await _authClient.mutate(MutationOptions( + document: gql(beaconQueries.fetchBeaconDetail(beaconId)))); + + if (result.isConcrete && result.data != null) { + final beaconJson = result.data!['beacon']; + + final beacon = BeaconModel.fromJson(beaconJson); + return DataSuccess(beacon); + } + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + + Future> updateBeaconLocation( + String? beaconId, String lat, String lon) async { + bool isConnected = await utils.checkInternetConnectivity(); + if (!isConnected) {} + + final result = await _authClient.mutate(MutationOptions( + document: gql(beaconQueries.updateBeaconLocation(beaconId, lat, lon)))); + + if (result.isConcrete && result.data != null) { + final beaconJson = result.data!['updateBeaconLocation']; + + final location = LocationModel.fromJson(beaconJson); + return DataSuccess(location); + } + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + + Future> changeUserLocation( + String beaconId, LatLng latlng) async { + bool isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + return DataFailed('No internet connection'); + } + + final result = await _authClient.mutate(MutationOptions( + document: gql(beaconQueries.changeUserLocation(beaconId, + latlng.latitude.toString(), latlng.longitude.toString())))); + + if (result.isConcrete && + result.data != null && + result.data!['updateUserLocation'] != null) { + final user = UserModel.fromJson(result.data!['updateUserLocation']); + return DataSuccess(user); + } else { + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + } + + Future> createLandMark( + String id, String lat, String lon, String title) async { + bool isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + return DataFailed('No internet connection'); + } + + final result = await _authClient.mutate(MutationOptions( + document: gql(beaconQueries.createLandmark(id, lat, lon, title)))); + + if (result.isConcrete && + result.data != null && + result.data!['createLandmark'] != null) { + final newLandMark = + LandMarkModel.fromJson(result.data!['createLandmark']); + return DataSuccess(newLandMark); + } else { + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + } + + Stream> locationUpdateSubscription( + String beaconId) async* { + bool isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + yield DataFailed('No internet connection'); + } + + final subscriptionOptions = SubscriptionOptions( + document: beaconQueries.locationUpdateGQL, variables: {'id': beaconId}); + + final resultStream = + await _subscriptionClient.subscribe(subscriptionOptions); + + try { + await for (var stream in resultStream) { + if (stream.hasException) { + yield DataFailed('Something went wrong'); + } else { + var locations = + BeaconLocationsModel.fromJson(stream.data!['beaconLocations']); + yield DataSuccess(locations); + } + } + } catch (e) { + log(e.toString()); + } + } + + Stream> LeaveJoinBeaconSubscription( + String beaconId) async* { + bool isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + yield DataFailed('No internet connection'); + } + + final subscriptionOptions = SubscriptionOptions( + document: beaconQueries.joinleaveBeaconSubGql, + variables: {'id': beaconId}); + + final resultStream = + await _subscriptionClient.subscribe(subscriptionOptions); + + await for (var stream in resultStream) { + if (stream.hasException) { + yield DataFailed('Something went wrong'); + } else { + var locations = + JoinLeaveBeaconModel.fromJson(stream.data!['JoinLeaveBeacon']); + yield DataSuccess(locations); + } + } + } + + Future> sos(String id) async { + bool isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + return DataFailed('No internet connection'); + } + + final result = await _authClient + .mutate(MutationOptions(document: gql(beaconQueries.sos(id)))); + + if (result.isConcrete && + result.data != null && + result.data!['sos'] != null) { + return DataSuccess(UserModel.fromJson(result.data!['sos'])); + } else { + return DataFailed(utils.filterException(result.exception!)); + } + } + + String encounteredExceptionOrError(OperationException exception) { + if (exception.linkException != null) { + return 'Server not running'; + } else { + return exception.graphqlErrors[0].message.toString(); + } + } +} diff --git a/lib/data/datasource/remote/remote_home_api.dart b/lib/data/datasource/remote/remote_home_api.dart new file mode 100644 index 00000000..e183eced --- /dev/null +++ b/lib/data/datasource/remote/remote_home_api.dart @@ -0,0 +1,207 @@ +import 'package:beacon/core/queries/group.dart'; +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/data/models/group/group_model.dart'; +import 'package:beacon/data/models/subscriptions/updated_group_model/updated_group_model.dart'; +import 'package:beacon/data/models/user/user_model.dart'; +import 'package:beacon/domain/entities/group/group_entity.dart'; +import 'package:beacon/locator.dart'; +import 'package:flutter/material.dart'; +import 'package:graphql_flutter/graphql_flutter.dart'; + +class RemoteHomeApi { + late GraphQLClient _authClient; + late GraphQLClient _subscriptionClient; + RemoteHomeApi(this._authClient, this._subscriptionClient); + + final _groupQueries = GroupQueries(); + + void loadClient(GraphQLClient authClient, GraphQLClient subscriptionClient) { + this._authClient = authClient; + this._subscriptionClient = subscriptionClient; + } + + Future>> fetchUserGroups( + int page, int pageSize) async { + final isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + // fetching the previous data stored + // here taking all the ids of group from the user model and then fetching the groups locally from the ids + // returning all groups in one go + UserModel? usermodel = await localApi.fetchUser(); + + if (usermodel != null && usermodel.groups != null) { + // taking user groups + + int condition = (page - 1) * pageSize + pageSize; + int groupLen = usermodel.groups!.length; + + if (condition > groupLen) { + condition = groupLen; + } + + List groups = []; + + for (int i = (page - 1) * pageSize; i < condition; i++) { + GroupModel? groupModel = + await localApi.getGroup(usermodel.groups![i]!.id); + groupModel != null ? groups.add(groupModel) : null; + } + + return DataSuccess(groups); + } + } + + final result = await _authClient.query(QueryOptions( + document: gql(_groupQueries.fetchUserGroups(page, pageSize)))); + + if (result.data != null && result.isConcrete) { + List groups = []; + List groupsData = result.data!['groups']; + for (var groupData in groupsData) { + final group = GroupModel.fromJson(groupData); + + // saving locally + await localApi.saveGroup(group); + + groups.add(group); + } + return DataSuccess(groups); + } + + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + + Future> fetchGroup(String id) async { + final isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) + return DataFailed('Beacon is trying to connect with internet...'); + final result = await _authClient + .mutate(MutationOptions(document: gql(_groupQueries.groupDetail(id)))); + + if (result.data != null && result.isConcrete) { + GroupModel group = GroupModel.fromJson(result.data!['group']); + + // storing group + await localApi.saveGroup(group); + + return DataSuccess(group); + } + + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + + Future> createGroup(String title) async { + final isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) + return DataFailed('Beacon is trying to connect with internet...'); + final result = await _authClient.mutate( + MutationOptions(document: gql(_groupQueries.createGroup(title)))); + + if (result.data != null && result.isConcrete) { + GroupModel group = GroupModel.fromJson( + result.data!['createGroup'] as Map); + + // storing group + await localApi.saveGroup(group); + + return DataSuccess(group); + } + + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + + Future> joinGroup(String shortCode) async { + final isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) + return DataFailed('Beacon is trying to connect with internet...'); + + final result = await _authClient.mutate( + MutationOptions(document: gql(_groupQueries.joinGroup(shortCode)))); + + if (result.data != null && result.isConcrete) { + GroupModel group = GroupModel.fromJson( + result.data!['joinGroup'] as Map); + + // storing group + await localApi.saveGroup(group); + + return DataSuccess(group); + } + + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + + SubscriptionOptions? groupsSubscription; + + Stream> groupUpdateSubscription( + List groupIds) async* { + // Check for internet connectivity + final isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) { + yield DataFailed('Beacon is trying to connect with internet...'); + return; + } + + // Initialize GraphQL client + final subscriptionOptions = SubscriptionOptions( + document: _groupQueries.groupUpdateSubGql, + variables: {'groupIds': groupIds}, + ); + final resultStream = + await _subscriptionClient.subscribe(subscriptionOptions); + + // Listen to the subscription stream + await for (var result in resultStream) { + if (result.hasException) { + yield DataFailed(result.exception.toString()); + continue; + } + + if (result.data == null || + !result.isConcrete || + result.data!['groupUpdate'] == null) { + continue; + } + + final groupUpdateJson = + result.data!['groupUpdate'] as Map; + + UpdatedGroupModel updatedGroup = + UpdatedGroupModel.fromJson(groupUpdateJson); + + yield DataSuccess(updatedGroup); + } + } + + Future> changeShortCode(String groupId) async { + final isConnected = await utils.checkInternetConnectivity(); + + if (!isConnected) + return DataFailed('Beacon is trying to connect with internet...'); + + final result = await _authClient.mutate( + MutationOptions(document: gql(_groupQueries.changeShortCode(groupId)))); + + if (result.data != null && + result.isConcrete && + result.data!['changeShortcode'] != null) { + return DataSuccess(GroupModel.fromJson(result.data!['changeShortcode'])); + } else { + return DataFailed(encounteredExceptionOrError(result.exception!)); + } + } + + String encounteredExceptionOrError(OperationException exception) { + if (exception.linkException != null) { + debugPrint(exception.linkException.toString()); + return 'Something went wrong'; + } else { + return exception.graphqlErrors[0].message.toString(); + } + } +} diff --git a/lib/data/models/beacon/beacon_model.dart b/lib/data/models/beacon/beacon_model.dart new file mode 100644 index 00000000..0c1fa867 --- /dev/null +++ b/lib/data/models/beacon/beacon_model.dart @@ -0,0 +1,85 @@ +import 'package:beacon/data/models/group/group_model.dart'; +import 'package:beacon/data/models/landmark/landmark_model.dart'; +import 'package:beacon/data/models/location/location_model.dart'; +import 'package:beacon/data/models/user/user_model.dart'; +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +import 'package:hive/hive.dart'; +import 'package:json_annotation/json_annotation.dart'; +part 'beacon_model.g.dart'; + +@HiveType(typeId: 20) +@JsonSerializable() +class BeaconModel implements BeaconEntity { + @JsonKey(name: '_id') + @HiveField(0) + String? id; + @HiveField(1) + String? title; + @HiveField(2) + UserModel? leader; + @HiveField(3) + GroupModel? group; + @HiveField(4) + String? shortcode; + @HiveField(5) + List? followers; + @HiveField(6) + List? landmarks; + @HiveField(7) + LocationModel? location; + @HiveField(8) + List? route; + @HiveField(9) + int? startsAt; + @HiveField(10) + int? expiresAt; + + BeaconModel( + {this.id, + this.title, + this.leader, + this.group, + this.shortcode, + this.followers, + this.landmarks, + this.location, + this.route, + this.startsAt, + this.expiresAt}); + + @override + $BeaconEntityCopyWith get copyWith => + throw UnimplementedError(); + + factory BeaconModel.fromJson(Map json) => + _$BeaconModelFromJson(json); + + Map toJson() => _$BeaconModelToJson(this); + + BeaconModel copyWithModel({ + String? id, + String? title, + UserModel? leader, + GroupModel? group, + String? shortcode, + List? followers, + List? landmarks, + LocationModel? location, + List? route, + int? startsAt, + int? expiresAt, + }) { + return BeaconModel( + id: id ?? this.id, + title: title ?? this.title, + leader: leader ?? this.leader, + group: group ?? this.group, + shortcode: shortcode ?? this.shortcode, + followers: followers ?? this.followers, + landmarks: landmarks ?? this.landmarks, + location: location ?? this.location, + route: route ?? this.route, + startsAt: startsAt ?? this.startsAt, + expiresAt: expiresAt ?? this.expiresAt); + } +} diff --git a/lib/data/models/beacon/beacon_model.g.dart b/lib/data/models/beacon/beacon_model.g.dart new file mode 100644 index 00000000..e94a13e9 --- /dev/null +++ b/lib/data/models/beacon/beacon_model.g.dart @@ -0,0 +1,121 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'beacon_model.dart'; + +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class BeaconModelAdapter extends TypeAdapter { + @override + final int typeId = 20; + + @override + BeaconModel read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return BeaconModel( + id: fields[0] as String?, + title: fields[1] as String?, + leader: fields[2] as UserModel?, + group: fields[3] as GroupModel?, + shortcode: fields[4] as String?, + followers: (fields[5] as List?)?.cast(), + landmarks: (fields[6] as List?)?.cast(), + location: fields[7] as LocationModel?, + route: (fields[8] as List?)?.cast(), + startsAt: fields[9] as int?, + expiresAt: fields[10] as int?, + ); + } + + @override + void write(BinaryWriter writer, BeaconModel obj) { + writer + ..writeByte(11) + ..writeByte(0) + ..write(obj.id) + ..writeByte(1) + ..write(obj.title) + ..writeByte(2) + ..write(obj.leader) + ..writeByte(3) + ..write(obj.group) + ..writeByte(4) + ..write(obj.shortcode) + ..writeByte(5) + ..write(obj.followers) + ..writeByte(6) + ..write(obj.landmarks) + ..writeByte(7) + ..write(obj.location) + ..writeByte(8) + ..write(obj.route) + ..writeByte(9) + ..write(obj.startsAt) + ..writeByte(10) + ..write(obj.expiresAt); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is BeaconModelAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +BeaconModel _$BeaconModelFromJson(Map json) => BeaconModel( + id: json['_id'] as String?, + title: json['title'] as String?, + leader: json['leader'] == null + ? null + : UserModel.fromJson(json['leader'] as Map), + group: json['group'] == null + ? null + : GroupModel.fromJson(json['group'] as Map), + shortcode: json['shortcode'] as String?, + followers: (json['followers'] as List?) + ?.map((e) => + e == null ? null : UserModel.fromJson(e as Map)) + .toList(), + landmarks: (json['landmarks'] as List?) + ?.map((e) => e == null + ? null + : LandMarkModel.fromJson(e as Map)) + .toList(), + location: json['location'] == null + ? null + : LocationModel.fromJson(json['location'] as Map), + route: (json['route'] as List?) + ?.map((e) => e == null + ? null + : LocationModel.fromJson(e as Map)) + .toList(), + startsAt: (json['startsAt'] as num?)?.toInt(), + expiresAt: (json['expiresAt'] as num?)?.toInt(), + ); + +Map _$BeaconModelToJson(BeaconModel instance) => + { + '_id': instance.id, + 'title': instance.title, + 'leader': instance.leader, + 'group': instance.group, + 'shortcode': instance.shortcode, + 'followers': instance.followers, + 'landmarks': instance.landmarks, + 'location': instance.location, + 'route': instance.route, + 'startsAt': instance.startsAt, + 'expiresAt': instance.expiresAt, + }; diff --git a/lib/data/models/group/group_model.dart b/lib/data/models/group/group_model.dart new file mode 100644 index 00000000..84e60329 --- /dev/null +++ b/lib/data/models/group/group_model.dart @@ -0,0 +1,65 @@ +import 'package:beacon/data/models/beacon/beacon_model.dart'; +import 'package:beacon/data/models/user/user_model.dart'; +import 'package:beacon/domain/entities/group/group_entity.dart'; +import 'package:hive/hive.dart'; +import 'package:json_annotation/json_annotation.dart'; +part 'group_model.g.dart'; + +@HiveType(typeId: 30) +@JsonSerializable() +class GroupModel implements GroupEntity { + @JsonKey(name: '_id') + @HiveField(0) + String? id; + @HiveField(1) + String? title; + @HiveField(2) + UserModel? leader; + @HiveField(3) + List? members; + @HiveField(4) + String? shortcode; + @HiveField(5) + List? beacons; + + GroupModel({ + this.id, + this.title, + this.leader, + this.members, + this.shortcode, + this.beacons, + }); + + @override + $GroupEntityCopyWith get copyWith => throw UnimplementedError(); + + factory GroupModel.fromJson(Map json) => + _$GroupModelFromJson(json); + + Map toJson() => _$GroupModelToJson(this); + + GroupModel copyWithModel({ + String? id, + String? title, + UserModel? leader, + List? members, + String? shortcode, + List? beacons, + }) { + return GroupModel( + id: id ?? this.id, + title: title ?? this.title, + leader: leader ?? this.leader, + members: members ?? this.members, + shortcode: shortcode ?? this.shortcode, + beacons: beacons ?? this.beacons, + ); + } + + @override + bool get hasBeaconActivity => false; + + @override + bool get hasMemberActivity => false; +} diff --git a/lib/data/models/group/group_model.g.dart b/lib/data/models/group/group_model.g.dart new file mode 100644 index 00000000..64d110e5 --- /dev/null +++ b/lib/data/models/group/group_model.g.dart @@ -0,0 +1,88 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'group_model.dart'; + +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class GroupModelAdapter extends TypeAdapter { + @override + final int typeId = 30; + + @override + GroupModel read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return GroupModel( + id: fields[0] as String?, + title: fields[1] as String?, + leader: fields[2] as UserModel?, + members: (fields[3] as List?)?.cast(), + shortcode: fields[4] as String?, + beacons: (fields[5] as List?)?.cast(), + ); + } + + @override + void write(BinaryWriter writer, GroupModel obj) { + writer + ..writeByte(6) + ..writeByte(0) + ..write(obj.id) + ..writeByte(1) + ..write(obj.title) + ..writeByte(2) + ..write(obj.leader) + ..writeByte(3) + ..write(obj.members) + ..writeByte(4) + ..write(obj.shortcode) + ..writeByte(5) + ..write(obj.beacons); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is GroupModelAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +GroupModel _$GroupModelFromJson(Map json) => GroupModel( + id: json['_id'] as String?, + title: json['title'] as String?, + leader: json['leader'] == null + ? null + : UserModel.fromJson(json['leader'] as Map), + members: (json['members'] as List?) + ?.map((e) => + e == null ? null : UserModel.fromJson(e as Map)) + .toList(), + shortcode: json['shortcode'] as String?, + beacons: (json['beacons'] as List?) + ?.map((e) => e == null + ? null + : BeaconModel.fromJson(e as Map)) + .toList(), + ); + +Map _$GroupModelToJson(GroupModel instance) => + { + '_id': instance.id, + 'title': instance.title, + 'leader': instance.leader, + 'members': instance.members, + 'shortcode': instance.shortcode, + 'beacons': instance.beacons, + }; diff --git a/lib/data/models/landmark/landmark_model.dart b/lib/data/models/landmark/landmark_model.dart new file mode 100644 index 00000000..1a16319f --- /dev/null +++ b/lib/data/models/landmark/landmark_model.dart @@ -0,0 +1,45 @@ +import 'package:beacon/data/models/location/location_model.dart'; +import 'package:beacon/data/models/user/user_model.dart'; +import 'package:beacon/domain/entities/landmark/landmark_entity.dart'; + +import 'package:hive/hive.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'landmark_model.g.dart'; + +@HiveType(typeId: 50) +@JsonSerializable() +class LandMarkModel implements LandMarkEntity { + @HiveField(0) + String? title; + @HiveField(1) + LocationModel? location; + @JsonKey(name: '_id') + @HiveField(2) + String? id; + @HiveField(3) + UserModel? createdBy; + + LandMarkModel({this.title, this.location, this.id, this.createdBy}); + + @override + $LandMarkEntityCopyWith get copyWith => + throw UnimplementedError(); + + factory LandMarkModel.fromJson(Map json) => + _$LandMarkModelFromJson(json); + + Map toJson() => _$LandMarkModelToJson(this); + + LandMarkModel copyWithModel( + {String? id, + String? title, + LocationModel? location, + UserModel? createdBy}) { + return LandMarkModel( + id: id, + title: title ?? this.title, + location: location ?? this.location, + createdBy: createdBy ?? this.createdBy); + } +} diff --git a/lib/data/models/landmark/landmark_model.g.dart b/lib/data/models/landmark/landmark_model.g.dart new file mode 100644 index 00000000..96a59958 --- /dev/null +++ b/lib/data/models/landmark/landmark_model.g.dart @@ -0,0 +1,74 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'landmark_model.dart'; + +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class LandMarkModelAdapter extends TypeAdapter { + @override + final int typeId = 50; + + @override + LandMarkModel read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return LandMarkModel( + title: fields[0] as String?, + location: fields[1] as LocationModel?, + id: fields[2] as String?, + createdBy: fields[3] as UserModel?, + ); + } + + @override + void write(BinaryWriter writer, LandMarkModel obj) { + writer + ..writeByte(4) + ..writeByte(0) + ..write(obj.title) + ..writeByte(1) + ..write(obj.location) + ..writeByte(2) + ..write(obj.id) + ..writeByte(3) + ..write(obj.createdBy); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is LandMarkModelAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +LandMarkModel _$LandMarkModelFromJson(Map json) => + LandMarkModel( + title: json['title'] as String?, + location: json['location'] == null + ? null + : LocationModel.fromJson(json['location'] as Map), + id: json['_id'] as String?, + createdBy: json['createdBy'] == null + ? null + : UserModel.fromJson(json['createdBy'] as Map), + ); + +Map _$LandMarkModelToJson(LandMarkModel instance) => + { + 'title': instance.title, + 'location': instance.location, + '_id': instance.id, + 'createdBy': instance.createdBy, + }; diff --git a/lib/data/models/location/location_model.dart b/lib/data/models/location/location_model.dart new file mode 100644 index 00000000..53ebaf98 --- /dev/null +++ b/lib/data/models/location/location_model.dart @@ -0,0 +1,44 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:hive/hive.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:beacon/domain/entities/location/location_entity.dart'; +part 'location_model.g.dart'; + +@HiveType(typeId: 40) +@JsonSerializable() +class LocationModel implements LocationEntity { + @HiveField(0) + final String? lat; + @HiveField(1) + final String? lon; + @JsonKey(name: '_id') + @HiveField(2) + final String? id; + + LocationModel({ + this.id, + this.lat, + this.lon, + }); + + factory LocationModel.fromJson(Map json) => + _$LocationModelFromJson(json); + + Map toJson() => _$LocationModelToJson(this); + + LocationModel copyWithModel({ + String? id, + String? lat, + String? long, + }) { + return LocationModel( + id: id ?? this.id, + lat: lat ?? this.lat, + lon: lon ?? this.lon, + ); + } + + @override + $LocationEntityCopyWith get copyWith => + throw UnimplementedError(); +} diff --git a/lib/data/models/location/location_model.g.dart b/lib/data/models/location/location_model.g.dart new file mode 100644 index 00000000..adec4317 --- /dev/null +++ b/lib/data/models/location/location_model.g.dart @@ -0,0 +1,65 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'location_model.dart'; + +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class LocationModelAdapter extends TypeAdapter { + @override + final int typeId = 40; + + @override + LocationModel read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return LocationModel( + id: fields[2] as String?, + lat: fields[0] as String?, + lon: fields[1] as String?, + ); + } + + @override + void write(BinaryWriter writer, LocationModel obj) { + writer + ..writeByte(3) + ..writeByte(0) + ..write(obj.lat) + ..writeByte(1) + ..write(obj.lon) + ..writeByte(2) + ..write(obj.id); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is LocationModelAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +LocationModel _$LocationModelFromJson(Map json) => + LocationModel( + id: json['_id'] as String?, + lat: json['lat'] as String?, + lon: json['lon'] as String?, + ); + +Map _$LocationModelToJson(LocationModel instance) => + { + 'lat': instance.lat, + 'lon': instance.lon, + '_id': instance.id, + }; diff --git a/lib/data/models/subscriptions/beacon_locations_model/beacon_locations_model.dart b/lib/data/models/subscriptions/beacon_locations_model/beacon_locations_model.dart new file mode 100644 index 00000000..15a611dd --- /dev/null +++ b/lib/data/models/subscriptions/beacon_locations_model/beacon_locations_model.dart @@ -0,0 +1,26 @@ +import 'package:beacon/data/models/landmark/landmark_model.dart'; +import 'package:beacon/data/models/location/location_model.dart'; +import 'package:beacon/data/models/user/user_model.dart'; +import 'package:beacon/domain/entities/subscriptions/beacon_locations_entity/beacon_locations_entity.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +part 'beacon_locations_model.g.dart'; + +@JsonSerializable() +class BeaconLocationsModel implements BeaconLocationsEntity { + UserModel? userSOS; + List? route; + LandMarkModel? landmark; + @JsonKey(name: 'updatedUser') + UserModel? user; + + BeaconLocationsModel({this.userSOS, this.route, this.landmark, this.user}); + + factory BeaconLocationsModel.fromJson(Map json) => + _$BeaconLocationsModelFromJson(json); + + Map toJson() => _$BeaconLocationsModelToJson(this); + + @override + $BeaconLocationsEntityCopyWith get copyWith => + throw UnimplementedError(); +} diff --git a/lib/data/models/subscriptions/beacon_locations_model/beacon_locations_model.g.dart b/lib/data/models/subscriptions/beacon_locations_model/beacon_locations_model.g.dart new file mode 100644 index 00000000..40cd1e95 --- /dev/null +++ b/lib/data/models/subscriptions/beacon_locations_model/beacon_locations_model.g.dart @@ -0,0 +1,35 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'beacon_locations_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +BeaconLocationsModel _$BeaconLocationsModelFromJson( + Map json) => + BeaconLocationsModel( + userSOS: json['userSOS'] == null + ? null + : UserModel.fromJson(json['userSOS'] as Map), + route: (json['route'] as List?) + ?.map((e) => e == null + ? null + : LocationModel.fromJson(e as Map)) + .toList(), + landmark: json['landmark'] == null + ? null + : LandMarkModel.fromJson(json['landmark'] as Map), + user: json['updatedUser'] == null + ? null + : UserModel.fromJson(json['updatedUser'] as Map), + ); + +Map _$BeaconLocationsModelToJson( + BeaconLocationsModel instance) => + { + 'userSOS': instance.userSOS, + 'route': instance.route, + 'landmark': instance.landmark, + 'updatedUser': instance.user, + }; diff --git a/lib/data/models/subscriptions/join_leave_beacon_model/join_leave_beacon_model.dart b/lib/data/models/subscriptions/join_leave_beacon_model/join_leave_beacon_model.dart new file mode 100644 index 00000000..23c1513d --- /dev/null +++ b/lib/data/models/subscriptions/join_leave_beacon_model/join_leave_beacon_model.dart @@ -0,0 +1,21 @@ +import 'package:beacon/data/models/user/user_model.dart'; +import 'package:beacon/domain/entities/subscriptions/join_leave_beacon_entity/join_leave_beacon_entity.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +part 'join_leave_beacon_model.g.dart'; + +@JsonSerializable() +class JoinLeaveBeaconModel implements JoinLeaveBeaconEntity { + UserModel? inactiveuser; + UserModel? newfollower; + + JoinLeaveBeaconModel({this.inactiveuser, this.newfollower}); + + @override + $JoinLeaveBeaconEntityCopyWith get copyWith => + throw UnimplementedError(); + + factory JoinLeaveBeaconModel.fromJson(Map json) => + _$JoinLeaveBeaconModelFromJson(json); + + Map toJson() => _$JoinLeaveBeaconModelToJson(this); +} diff --git a/lib/data/models/subscriptions/join_leave_beacon_model/join_leave_beacon_model.g.dart b/lib/data/models/subscriptions/join_leave_beacon_model/join_leave_beacon_model.g.dart new file mode 100644 index 00000000..43b93503 --- /dev/null +++ b/lib/data/models/subscriptions/join_leave_beacon_model/join_leave_beacon_model.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'join_leave_beacon_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +JoinLeaveBeaconModel _$JoinLeaveBeaconModelFromJson( + Map json) => + JoinLeaveBeaconModel( + inactiveuser: json['inactiveuser'] == null + ? null + : UserModel.fromJson(json['inactiveuser'] as Map), + newfollower: json['newfollower'] == null + ? null + : UserModel.fromJson(json['newfollower'] as Map), + ); + +Map _$JoinLeaveBeaconModelToJson( + JoinLeaveBeaconModel instance) => + { + 'inactiveuser': instance.inactiveuser, + 'newfollower': instance.newfollower, + }; diff --git a/lib/data/models/subscriptions/updated_group_model/updated_group_model.dart b/lib/data/models/subscriptions/updated_group_model/updated_group_model.dart new file mode 100644 index 00000000..2a4315e9 --- /dev/null +++ b/lib/data/models/subscriptions/updated_group_model/updated_group_model.dart @@ -0,0 +1,32 @@ +import 'package:beacon/data/models/beacon/beacon_model.dart'; +import 'package:beacon/data/models/user/user_model.dart'; +import 'package:beacon/domain/entities/subscriptions/updated_group_entity/updated_group_entity.dart'; +import 'package:json_annotation/json_annotation.dart'; +part 'updated_group_model.g.dart'; + +@JsonSerializable() +class UpdatedGroupModel implements UpdatedGroupEntity { + @JsonKey(name: 'groupId') + String? id; + UserModel? newUser; + BeaconModel? newBeacon; + BeaconModel? updatedBeacon; + BeaconModel? deletedBeacon; + + UpdatedGroupModel({ + this.id, + this.newUser, + this.newBeacon, + this.updatedBeacon, + this.deletedBeacon, + }); + + factory UpdatedGroupModel.fromJson(Map json) => + _$UpdatedGroupModelFromJson(json); + + Map toJson() => _$UpdatedGroupModelToJson(this); + + @override + $UpdatedGroupEntityCopyWith get copyWith => + throw UnimplementedError(); +} diff --git a/lib/data/models/subscriptions/updated_group_model/updated_group_model.g.dart b/lib/data/models/subscriptions/updated_group_model/updated_group_model.g.dart new file mode 100644 index 00000000..aafb3d59 --- /dev/null +++ b/lib/data/models/subscriptions/updated_group_model/updated_group_model.g.dart @@ -0,0 +1,33 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'updated_group_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +UpdatedGroupModel _$UpdatedGroupModelFromJson(Map json) => + UpdatedGroupModel( + id: json['groupId'] as String?, + newUser: json['newUser'] == null + ? null + : UserModel.fromJson(json['newUser'] as Map), + newBeacon: json['newBeacon'] == null + ? null + : BeaconModel.fromJson(json['newBeacon'] as Map), + updatedBeacon: json['updatedBeacon'] == null + ? null + : BeaconModel.fromJson(json['updatedBeacon'] as Map), + deletedBeacon: json['deletedBeacon'] == null + ? null + : BeaconModel.fromJson(json['deletedBeacon'] as Map), + ); + +Map _$UpdatedGroupModelToJson(UpdatedGroupModel instance) => + { + 'groupId': instance.id, + 'newUser': instance.newUser, + 'newBeacon': instance.newBeacon, + 'updatedBeacon': instance.updatedBeacon, + 'deletedBeacon': instance.deletedBeacon, + }; diff --git a/lib/data/models/user/user_model.dart b/lib/data/models/user/user_model.dart new file mode 100644 index 00000000..1c2b61d6 --- /dev/null +++ b/lib/data/models/user/user_model.dart @@ -0,0 +1,82 @@ +import 'package:beacon/data/models/beacon/beacon_model.dart'; +import 'package:beacon/data/models/group/group_model.dart'; +import 'package:beacon/data/models/location/location_model.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:hive/hive.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'user_model.g.dart'; + +@HiveType(typeId: 10) +@JsonSerializable() +class UserModel implements UserEntity { + @JsonKey(name: '_id') + @HiveField(0) + String? id; + + @HiveField(1) + String? name; + + @HiveField(2) + String? email; + + @HiveField(3) + String? authToken; + + @HiveField(4) + bool? isGuest; + + @HiveField(5) + List? groups; + + @HiveField(6) + List? beacons; + + @HiveField(7) + LocationModel? location; + + @HiveField(8) + bool? isVerified; + + UserModel( + {this.authToken, + this.beacons, + this.email, + this.groups, + this.id, + this.isGuest, + this.location, + this.name, + this.isVerified}); + + @override + $UserEntityCopyWith get copyWith => throw UnimplementedError(); + + factory UserModel.fromJson(Map json) => + _$UserModelFromJson(json); + + Map toJson() => _$UserModelToJson(this); + + UserModel copyWithModel({ + String? id, + String? name, + String? authToken, + String? email, + bool? isGuest, + bool? isVerified, + List? groups, + List? beacons, + LocationModel? location, + }) { + return UserModel( + id: id ?? this.id, + name: name ?? this.name, + authToken: authToken ?? this.authToken, + email: email ?? this.email, + isGuest: isGuest ?? this.isGuest, + groups: groups ?? this.groups, + beacons: beacons ?? this.beacons, + location: location ?? this.location, + isVerified: isVerified ?? this.isVerified); + } +} diff --git a/lib/data/models/user/user_model.g.dart b/lib/data/models/user/user_model.g.dart new file mode 100644 index 00000000..45bb58a0 --- /dev/null +++ b/lib/data/models/user/user_model.g.dart @@ -0,0 +1,102 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'user_model.dart'; + +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class UserModelAdapter extends TypeAdapter { + @override + final int typeId = 10; + + @override + UserModel read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return UserModel( + authToken: fields[3] as String?, + beacons: (fields[6] as List?)?.cast(), + email: fields[2] as String?, + groups: (fields[5] as List?)?.cast(), + id: fields[0] as String?, + isGuest: fields[4] as bool?, + location: fields[7] as LocationModel?, + name: fields[1] as String?, + isVerified: fields[8] as bool?, + ); + } + + @override + void write(BinaryWriter writer, UserModel obj) { + writer + ..writeByte(9) + ..writeByte(0) + ..write(obj.id) + ..writeByte(1) + ..write(obj.name) + ..writeByte(2) + ..write(obj.email) + ..writeByte(3) + ..write(obj.authToken) + ..writeByte(4) + ..write(obj.isGuest) + ..writeByte(5) + ..write(obj.groups) + ..writeByte(6) + ..write(obj.beacons) + ..writeByte(7) + ..write(obj.location) + ..writeByte(8) + ..write(obj.isVerified); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is UserModelAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +UserModel _$UserModelFromJson(Map json) => UserModel( + authToken: json['authToken'] as String?, + beacons: (json['beacons'] as List?) + ?.map((e) => e == null + ? null + : BeaconModel.fromJson(e as Map)) + .toList(), + email: json['email'] as String?, + groups: (json['groups'] as List?) + ?.map((e) => + e == null ? null : GroupModel.fromJson(e as Map)) + .toList(), + id: json['_id'] as String?, + isGuest: json['isGuest'] as bool?, + location: json['location'] == null + ? null + : LocationModel.fromJson(json['location'] as Map), + name: json['name'] as String?, + isVerified: json['isVerified'] as bool?, + ); + +Map _$UserModelToJson(UserModel instance) => { + '_id': instance.id, + 'name': instance.name, + 'email': instance.email, + 'authToken': instance.authToken, + 'isGuest': instance.isGuest, + 'groups': instance.groups, + 'beacons': instance.beacons, + 'location': instance.location, + 'isVerified': instance.isVerified, + }; diff --git a/lib/data/repositories/auth_repository_implementation.dart b/lib/data/repositories/auth_repository_implementation.dart new file mode 100644 index 00000000..a6a63111 --- /dev/null +++ b/lib/data/repositories/auth_repository_implementation.dart @@ -0,0 +1,41 @@ +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/data/datasource/remote/remote_auth_api.dart'; +import 'package:beacon/data/models/user/user_model.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:beacon/domain/repositories/auth_repository.dart'; + +class AuthRepositoryImplementation implements AuthRepository { + final RemoteAuthApi remoteAuthApi; + + AuthRepositoryImplementation({required this.remoteAuthApi}); + + @override + Future> getUser() { + return remoteAuthApi.fetchUserInfo(); + } + + @override + Future> login(String email, String password) { + return remoteAuthApi.login(email, password); + } + + @override + Future> oAuth(String name, String email) { + return remoteAuthApi.gAuth(name, email); + } + + @override + Future> register( + String name, String email, String password) { + return remoteAuthApi.register(name, email, password); + } + + Future> sendVerificationCode() { + return remoteAuthApi.sendVerificationCode(); + } + + @override + Future> completeVerification() { + return remoteAuthApi.completeVerification(); + } +} diff --git a/lib/data/repositories/group_repository_implementation.dart b/lib/data/repositories/group_repository_implementation.dart new file mode 100644 index 00000000..ad4f213e --- /dev/null +++ b/lib/data/repositories/group_repository_implementation.dart @@ -0,0 +1,56 @@ +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/data/datasource/remote/remote_group_api.dart'; +import 'package:beacon/data/models/beacon/beacon_model.dart'; +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:beacon/domain/repositories/group_repository.dart'; + +class GroupRepostioryImplementation implements GroupRepository { + final RemoteGroupApi remoteGroupApi; + GroupRepostioryImplementation({required this.remoteGroupApi}); + @override + Future> createHike(String title, int startsAt, + int expiresAt, String lat, String lon, String groupID) async { + return remoteGroupApi.createHike( + title, startsAt, expiresAt, lat, lon, groupID); + } + + @override + Future>> fetchHikes( + String groupID, int page, int pageSize) { + return remoteGroupApi.fetchHikes(groupID, page, pageSize); + } + + @override + Future> joinHike(String shortcode) { + return remoteGroupApi.joinHike(shortcode); + } + + @override + Future>> nearbyHikes( + String groupId, String lat, String lon, double radius) { + return remoteGroupApi.nearbyBeacons(groupId, lat, lon, radius); + } + + @override + Future>> filterHikes( + String groupId, String type) { + return remoteGroupApi.filterBeacons(groupId, type); + } + + @override + Future> deleteBeacon(String? beaconId) { + return remoteGroupApi.deleteBeacon(beaconId); + } + + @override + Future> rescheduleHike( + int expiresAt, int startsAt, String beaconId) { + return remoteGroupApi.rescheduleBeacon(expiresAt, startsAt, beaconId); + } + + @override + Future> removeMember(String groupId, String memberId) { + return remoteGroupApi.removeMember(groupId, memberId); + } +} diff --git a/lib/data/repositories/hike_repository_implementation.dart b/lib/data/repositories/hike_repository_implementation.dart new file mode 100644 index 00000000..93c0d562 --- /dev/null +++ b/lib/data/repositories/hike_repository_implementation.dart @@ -0,0 +1,56 @@ +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/data/datasource/remote/remote_hike_api.dart'; +import 'package:beacon/data/models/beacon/beacon_model.dart'; +import 'package:beacon/data/models/landmark/landmark_model.dart'; +import 'package:beacon/data/models/location/location_model.dart'; +import 'package:beacon/domain/entities/subscriptions/beacon_locations_entity/beacon_locations_entity.dart'; +import 'package:beacon/domain/entities/subscriptions/join_leave_beacon_entity/join_leave_beacon_entity.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:beacon/domain/repositories/hike_repository.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; + +class HikeRepositoryImplementatioin implements HikeRepository { + final RemoteHikeApi remoteHikeApi; + + HikeRepositoryImplementatioin({required this.remoteHikeApi}); + + @override + Future> fetchBeaconDetails(String beaconId) { + return remoteHikeApi.fetchBeaconDetails(beaconId); + } + + @override + Future> updateBeaconLocation( + String beaconId, LatLng position) { + return remoteHikeApi.updateBeaconLocation( + beaconId, position.latitude.toString(), position.longitude.toString()); + } + + @override + Future> createLandMark( + String id, String title, String lat, String lon) { + return remoteHikeApi.createLandMark(id, lat, lon, title); + } + + @override + Stream> beaconLocationsSubscription( + String beaconId) { + return remoteHikeApi.locationUpdateSubscription(beaconId); + } + + @override + Stream> joinLeaveBeaconSubscription( + String beaconId) { + return remoteHikeApi.LeaveJoinBeaconSubscription(beaconId); + } + + @override + Future> changeUserLocation(String id, LatLng latLng) { + return remoteHikeApi.changeUserLocation(id, latLng); + } + + @override + Future> sos(String beaconId) { + return remoteHikeApi.sos(beaconId); + } +} diff --git a/lib/data/repositories/home_repository_implementation.dart b/lib/data/repositories/home_repository_implementation.dart new file mode 100644 index 00000000..3804d64e --- /dev/null +++ b/lib/data/repositories/home_repository_implementation.dart @@ -0,0 +1,43 @@ +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/data/datasource/remote/remote_home_api.dart'; +import 'package:beacon/data/models/group/group_model.dart'; +import 'package:beacon/data/models/subscriptions/updated_group_model/updated_group_model.dart'; +import 'package:beacon/domain/entities/group/group_entity.dart'; +import 'package:beacon/domain/repositories/home_repository.dart'; + +class HomeRepostitoryImplementation implements HomeRepository { + final RemoteHomeApi remoteHomeApi; + + HomeRepostitoryImplementation({required this.remoteHomeApi}); + + @override + Future> createGroup(String title) { + return remoteHomeApi.createGroup(title); + } + + @override + Future>> fetchGroups(int page, int pageSize) { + return remoteHomeApi.fetchUserGroups(page, pageSize); + } + + @override + Future> joinGroup(String shortCode) { + return remoteHomeApi.joinGroup(shortCode); + } + + @override + Stream> groupUpdateSubscription( + List groupIds) { + return remoteHomeApi.groupUpdateSubscription(groupIds); + } + + @override + Future> fetchGroup(String groupId) { + return remoteHomeApi.fetchGroup(groupId); + } + + @override + Future> changeShortcode(String groupId) { + return remoteHomeApi.changeShortCode(groupId); + } +} diff --git a/lib/domain/entities/beacon/beacon_entity.dart b/lib/domain/entities/beacon/beacon_entity.dart new file mode 100644 index 00000000..27ad61fa --- /dev/null +++ b/lib/domain/entities/beacon/beacon_entity.dart @@ -0,0 +1,52 @@ +import 'package:beacon/domain/entities/group/group_entity.dart'; +import 'package:beacon/domain/entities/landmark/landmark_entity.dart'; +import 'package:beacon/domain/entities/location/location_entity.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +part 'beacon_entity.freezed.dart'; + +@freezed +class BeaconEntity with _$BeaconEntity { + const factory BeaconEntity({ + String? id, + String? shortcode, + int? startsAt, + int? expiresAt, + String? title, + UserEntity? leader, + List? followers, + List? route, + List? landmarks, + LocationEntity? location, + GroupEntity? group, + }) = _BeaconEntity; +} + +extension BeaconEntityCopyWithExtension on BeaconEntity { + BeaconEntity copywith( + {String? id, + String? shortcode, + int? startsAt, + int? expiresAt, + String? title, + UserEntity? leader, + List? followers, + List? route, + List? landmarks, + LocationEntity? location, + GroupEntity? group}) { + return BeaconEntity( + id: id ?? this.id, + shortcode: shortcode ?? this.shortcode, + startsAt: startsAt ?? this.startsAt, + expiresAt: expiresAt ?? this.expiresAt, + title: title ?? this.title, + leader: leader ?? this.leader, + followers: followers ?? this.followers, + route: route ?? this.route, + landmarks: landmarks ?? this.landmarks, + location: location ?? this.location, + group: group ?? this.group, + ); + } +} diff --git a/lib/domain/entities/beacon/beacon_entity.freezed.dart b/lib/domain/entities/beacon/beacon_entity.freezed.dart new file mode 100644 index 00000000..a623a54b --- /dev/null +++ b/lib/domain/entities/beacon/beacon_entity.freezed.dart @@ -0,0 +1,428 @@ +// 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 'beacon_entity.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 _$BeaconEntity { + String? get id => throw _privateConstructorUsedError; + String? get shortcode => throw _privateConstructorUsedError; + int? get startsAt => throw _privateConstructorUsedError; + int? get expiresAt => throw _privateConstructorUsedError; + String? get title => throw _privateConstructorUsedError; + UserEntity? get leader => throw _privateConstructorUsedError; + List? get followers => throw _privateConstructorUsedError; + List? get route => throw _privateConstructorUsedError; + List? get landmarks => throw _privateConstructorUsedError; + LocationEntity? get location => throw _privateConstructorUsedError; + GroupEntity? get group => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $BeaconEntityCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $BeaconEntityCopyWith<$Res> { + factory $BeaconEntityCopyWith( + BeaconEntity value, $Res Function(BeaconEntity) then) = + _$BeaconEntityCopyWithImpl<$Res, BeaconEntity>; + @useResult + $Res call( + {String? id, + String? shortcode, + int? startsAt, + int? expiresAt, + String? title, + UserEntity? leader, + List? followers, + List? route, + List? landmarks, + LocationEntity? location, + GroupEntity? group}); + + $UserEntityCopyWith<$Res>? get leader; + $LocationEntityCopyWith<$Res>? get location; + $GroupEntityCopyWith<$Res>? get group; +} + +/// @nodoc +class _$BeaconEntityCopyWithImpl<$Res, $Val extends BeaconEntity> + implements $BeaconEntityCopyWith<$Res> { + _$BeaconEntityCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + Object? shortcode = freezed, + Object? startsAt = freezed, + Object? expiresAt = freezed, + Object? title = freezed, + Object? leader = freezed, + Object? followers = freezed, + Object? route = freezed, + Object? landmarks = freezed, + Object? location = freezed, + Object? group = freezed, + }) { + return _then(_value.copyWith( + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + shortcode: freezed == shortcode + ? _value.shortcode + : shortcode // ignore: cast_nullable_to_non_nullable + as String?, + startsAt: freezed == startsAt + ? _value.startsAt + : startsAt // ignore: cast_nullable_to_non_nullable + as int?, + expiresAt: freezed == expiresAt + ? _value.expiresAt + : expiresAt // ignore: cast_nullable_to_non_nullable + as int?, + title: freezed == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String?, + leader: freezed == leader + ? _value.leader + : leader // ignore: cast_nullable_to_non_nullable + as UserEntity?, + followers: freezed == followers + ? _value.followers + : followers // ignore: cast_nullable_to_non_nullable + as List?, + route: freezed == route + ? _value.route + : route // ignore: cast_nullable_to_non_nullable + as List?, + landmarks: freezed == landmarks + ? _value.landmarks + : landmarks // ignore: cast_nullable_to_non_nullable + as List?, + location: freezed == location + ? _value.location + : location // ignore: cast_nullable_to_non_nullable + as LocationEntity?, + group: freezed == group + ? _value.group + : group // ignore: cast_nullable_to_non_nullable + as GroupEntity?, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $UserEntityCopyWith<$Res>? get leader { + if (_value.leader == null) { + return null; + } + + return $UserEntityCopyWith<$Res>(_value.leader!, (value) { + return _then(_value.copyWith(leader: value) as $Val); + }); + } + + @override + @pragma('vm:prefer-inline') + $LocationEntityCopyWith<$Res>? get location { + if (_value.location == null) { + return null; + } + + return $LocationEntityCopyWith<$Res>(_value.location!, (value) { + return _then(_value.copyWith(location: value) as $Val); + }); + } + + @override + @pragma('vm:prefer-inline') + $GroupEntityCopyWith<$Res>? get group { + if (_value.group == null) { + return null; + } + + return $GroupEntityCopyWith<$Res>(_value.group!, (value) { + return _then(_value.copyWith(group: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$BeaconEntityImplCopyWith<$Res> + implements $BeaconEntityCopyWith<$Res> { + factory _$$BeaconEntityImplCopyWith( + _$BeaconEntityImpl value, $Res Function(_$BeaconEntityImpl) then) = + __$$BeaconEntityImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String? id, + String? shortcode, + int? startsAt, + int? expiresAt, + String? title, + UserEntity? leader, + List? followers, + List? route, + List? landmarks, + LocationEntity? location, + GroupEntity? group}); + + @override + $UserEntityCopyWith<$Res>? get leader; + @override + $LocationEntityCopyWith<$Res>? get location; + @override + $GroupEntityCopyWith<$Res>? get group; +} + +/// @nodoc +class __$$BeaconEntityImplCopyWithImpl<$Res> + extends _$BeaconEntityCopyWithImpl<$Res, _$BeaconEntityImpl> + implements _$$BeaconEntityImplCopyWith<$Res> { + __$$BeaconEntityImplCopyWithImpl( + _$BeaconEntityImpl _value, $Res Function(_$BeaconEntityImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + Object? shortcode = freezed, + Object? startsAt = freezed, + Object? expiresAt = freezed, + Object? title = freezed, + Object? leader = freezed, + Object? followers = freezed, + Object? route = freezed, + Object? landmarks = freezed, + Object? location = freezed, + Object? group = freezed, + }) { + return _then(_$BeaconEntityImpl( + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + shortcode: freezed == shortcode + ? _value.shortcode + : shortcode // ignore: cast_nullable_to_non_nullable + as String?, + startsAt: freezed == startsAt + ? _value.startsAt + : startsAt // ignore: cast_nullable_to_non_nullable + as int?, + expiresAt: freezed == expiresAt + ? _value.expiresAt + : expiresAt // ignore: cast_nullable_to_non_nullable + as int?, + title: freezed == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String?, + leader: freezed == leader + ? _value.leader + : leader // ignore: cast_nullable_to_non_nullable + as UserEntity?, + followers: freezed == followers + ? _value._followers + : followers // ignore: cast_nullable_to_non_nullable + as List?, + route: freezed == route + ? _value._route + : route // ignore: cast_nullable_to_non_nullable + as List?, + landmarks: freezed == landmarks + ? _value._landmarks + : landmarks // ignore: cast_nullable_to_non_nullable + as List?, + location: freezed == location + ? _value.location + : location // ignore: cast_nullable_to_non_nullable + as LocationEntity?, + group: freezed == group + ? _value.group + : group // ignore: cast_nullable_to_non_nullable + as GroupEntity?, + )); + } +} + +/// @nodoc + +class _$BeaconEntityImpl implements _BeaconEntity { + const _$BeaconEntityImpl( + {this.id, + this.shortcode, + this.startsAt, + this.expiresAt, + this.title, + this.leader, + final List? followers, + final List? route, + final List? landmarks, + this.location, + this.group}) + : _followers = followers, + _route = route, + _landmarks = landmarks; + + @override + final String? id; + @override + final String? shortcode; + @override + final int? startsAt; + @override + final int? expiresAt; + @override + final String? title; + @override + final UserEntity? leader; + final List? _followers; + @override + List? get followers { + final value = _followers; + if (value == null) return null; + if (_followers is EqualUnmodifiableListView) return _followers; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + final List? _route; + @override + List? get route { + final value = _route; + if (value == null) return null; + if (_route is EqualUnmodifiableListView) return _route; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + final List? _landmarks; + @override + List? get landmarks { + final value = _landmarks; + if (value == null) return null; + if (_landmarks is EqualUnmodifiableListView) return _landmarks; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + @override + final LocationEntity? location; + @override + final GroupEntity? group; + + @override + String toString() { + return 'BeaconEntity(id: $id, shortcode: $shortcode, startsAt: $startsAt, expiresAt: $expiresAt, title: $title, leader: $leader, followers: $followers, route: $route, landmarks: $landmarks, location: $location, group: $group)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$BeaconEntityImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.shortcode, shortcode) || + other.shortcode == shortcode) && + (identical(other.startsAt, startsAt) || + other.startsAt == startsAt) && + (identical(other.expiresAt, expiresAt) || + other.expiresAt == expiresAt) && + (identical(other.title, title) || other.title == title) && + (identical(other.leader, leader) || other.leader == leader) && + const DeepCollectionEquality() + .equals(other._followers, _followers) && + const DeepCollectionEquality().equals(other._route, _route) && + const DeepCollectionEquality() + .equals(other._landmarks, _landmarks) && + (identical(other.location, location) || + other.location == location) && + (identical(other.group, group) || other.group == group)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + id, + shortcode, + startsAt, + expiresAt, + title, + leader, + const DeepCollectionEquality().hash(_followers), + const DeepCollectionEquality().hash(_route), + const DeepCollectionEquality().hash(_landmarks), + location, + group); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$BeaconEntityImplCopyWith<_$BeaconEntityImpl> get copyWith => + __$$BeaconEntityImplCopyWithImpl<_$BeaconEntityImpl>(this, _$identity); +} + +abstract class _BeaconEntity implements BeaconEntity { + const factory _BeaconEntity( + {final String? id, + final String? shortcode, + final int? startsAt, + final int? expiresAt, + final String? title, + final UserEntity? leader, + final List? followers, + final List? route, + final List? landmarks, + final LocationEntity? location, + final GroupEntity? group}) = _$BeaconEntityImpl; + + @override + String? get id; + @override + String? get shortcode; + @override + int? get startsAt; + @override + int? get expiresAt; + @override + String? get title; + @override + UserEntity? get leader; + @override + List? get followers; + @override + List? get route; + @override + List? get landmarks; + @override + LocationEntity? get location; + @override + GroupEntity? get group; + @override + @JsonKey(ignore: true) + _$$BeaconEntityImplCopyWith<_$BeaconEntityImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/domain/entities/group/group_entity.dart b/lib/domain/entities/group/group_entity.dart new file mode 100644 index 00000000..a4cd0e44 --- /dev/null +++ b/lib/domain/entities/group/group_entity.dart @@ -0,0 +1,39 @@ +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +part 'group_entity.freezed.dart'; + +@freezed +class GroupEntity with _$GroupEntity { + const factory GroupEntity( + {String? id, + List? beacons, + List? members, + UserEntity? leader, + String? title, + String? shortcode, + @Default(false) bool hasBeaconActivity, + @Default(false) bool hasMemberActivity}) = _GroupEntity; +} + +extension GroupEntityCopyWithExtension on GroupEntity { + GroupEntity copywith( + {String? id, + List? beacons, + List? members, + UserEntity? leader, + String? title, + String? shortcode, + bool? hasBeaconActiby, + bool? hasMemberActivity}) { + return GroupEntity( + id: id ?? this.id, + beacons: beacons ?? this.beacons, + members: members ?? this.members, + leader: leader ?? this.leader, + title: title ?? this.title, + shortcode: shortcode ?? this.shortcode, + hasBeaconActivity: hasBeaconActiby ?? this.hasBeaconActivity, + hasMemberActivity: hasMemberActivity ?? this.hasMemberActivity); + } +} diff --git a/lib/domain/entities/group/group_entity.freezed.dart b/lib/domain/entities/group/group_entity.freezed.dart new file mode 100644 index 00000000..74507d02 --- /dev/null +++ b/lib/domain/entities/group/group_entity.freezed.dart @@ -0,0 +1,325 @@ +// 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 'group_entity.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 _$GroupEntity { + String? get id => throw _privateConstructorUsedError; + List? get beacons => throw _privateConstructorUsedError; + List? get members => throw _privateConstructorUsedError; + UserEntity? get leader => throw _privateConstructorUsedError; + String? get title => throw _privateConstructorUsedError; + String? get shortcode => throw _privateConstructorUsedError; + bool get hasBeaconActivity => throw _privateConstructorUsedError; + bool get hasMemberActivity => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $GroupEntityCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $GroupEntityCopyWith<$Res> { + factory $GroupEntityCopyWith( + GroupEntity value, $Res Function(GroupEntity) then) = + _$GroupEntityCopyWithImpl<$Res, GroupEntity>; + @useResult + $Res call( + {String? id, + List? beacons, + List? members, + UserEntity? leader, + String? title, + String? shortcode, + bool hasBeaconActivity, + bool hasMemberActivity}); + + $UserEntityCopyWith<$Res>? get leader; +} + +/// @nodoc +class _$GroupEntityCopyWithImpl<$Res, $Val extends GroupEntity> + implements $GroupEntityCopyWith<$Res> { + _$GroupEntityCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + Object? beacons = freezed, + Object? members = freezed, + Object? leader = freezed, + Object? title = freezed, + Object? shortcode = freezed, + Object? hasBeaconActivity = null, + Object? hasMemberActivity = null, + }) { + return _then(_value.copyWith( + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + beacons: freezed == beacons + ? _value.beacons + : beacons // ignore: cast_nullable_to_non_nullable + as List?, + members: freezed == members + ? _value.members + : members // ignore: cast_nullable_to_non_nullable + as List?, + leader: freezed == leader + ? _value.leader + : leader // ignore: cast_nullable_to_non_nullable + as UserEntity?, + title: freezed == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String?, + shortcode: freezed == shortcode + ? _value.shortcode + : shortcode // ignore: cast_nullable_to_non_nullable + as String?, + hasBeaconActivity: null == hasBeaconActivity + ? _value.hasBeaconActivity + : hasBeaconActivity // ignore: cast_nullable_to_non_nullable + as bool, + hasMemberActivity: null == hasMemberActivity + ? _value.hasMemberActivity + : hasMemberActivity // ignore: cast_nullable_to_non_nullable + as bool, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $UserEntityCopyWith<$Res>? get leader { + if (_value.leader == null) { + return null; + } + + return $UserEntityCopyWith<$Res>(_value.leader!, (value) { + return _then(_value.copyWith(leader: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$GroupEntityImplCopyWith<$Res> + implements $GroupEntityCopyWith<$Res> { + factory _$$GroupEntityImplCopyWith( + _$GroupEntityImpl value, $Res Function(_$GroupEntityImpl) then) = + __$$GroupEntityImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String? id, + List? beacons, + List? members, + UserEntity? leader, + String? title, + String? shortcode, + bool hasBeaconActivity, + bool hasMemberActivity}); + + @override + $UserEntityCopyWith<$Res>? get leader; +} + +/// @nodoc +class __$$GroupEntityImplCopyWithImpl<$Res> + extends _$GroupEntityCopyWithImpl<$Res, _$GroupEntityImpl> + implements _$$GroupEntityImplCopyWith<$Res> { + __$$GroupEntityImplCopyWithImpl( + _$GroupEntityImpl _value, $Res Function(_$GroupEntityImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + Object? beacons = freezed, + Object? members = freezed, + Object? leader = freezed, + Object? title = freezed, + Object? shortcode = freezed, + Object? hasBeaconActivity = null, + Object? hasMemberActivity = null, + }) { + return _then(_$GroupEntityImpl( + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + beacons: freezed == beacons + ? _value._beacons + : beacons // ignore: cast_nullable_to_non_nullable + as List?, + members: freezed == members + ? _value._members + : members // ignore: cast_nullable_to_non_nullable + as List?, + leader: freezed == leader + ? _value.leader + : leader // ignore: cast_nullable_to_non_nullable + as UserEntity?, + title: freezed == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String?, + shortcode: freezed == shortcode + ? _value.shortcode + : shortcode // ignore: cast_nullable_to_non_nullable + as String?, + hasBeaconActivity: null == hasBeaconActivity + ? _value.hasBeaconActivity + : hasBeaconActivity // ignore: cast_nullable_to_non_nullable + as bool, + hasMemberActivity: null == hasMemberActivity + ? _value.hasMemberActivity + : hasMemberActivity // ignore: cast_nullable_to_non_nullable + as bool, + )); + } +} + +/// @nodoc + +class _$GroupEntityImpl implements _GroupEntity { + const _$GroupEntityImpl( + {this.id, + final List? beacons, + final List? members, + this.leader, + this.title, + this.shortcode, + this.hasBeaconActivity = false, + this.hasMemberActivity = false}) + : _beacons = beacons, + _members = members; + + @override + final String? id; + final List? _beacons; + @override + List? get beacons { + final value = _beacons; + if (value == null) return null; + if (_beacons is EqualUnmodifiableListView) return _beacons; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + final List? _members; + @override + List? get members { + final value = _members; + if (value == null) return null; + if (_members is EqualUnmodifiableListView) return _members; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + @override + final UserEntity? leader; + @override + final String? title; + @override + final String? shortcode; + @override + @JsonKey() + final bool hasBeaconActivity; + @override + @JsonKey() + final bool hasMemberActivity; + + @override + String toString() { + return 'GroupEntity(id: $id, beacons: $beacons, members: $members, leader: $leader, title: $title, shortcode: $shortcode, hasBeaconActivity: $hasBeaconActivity, hasMemberActivity: $hasMemberActivity)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$GroupEntityImpl && + (identical(other.id, id) || other.id == id) && + const DeepCollectionEquality().equals(other._beacons, _beacons) && + const DeepCollectionEquality().equals(other._members, _members) && + (identical(other.leader, leader) || other.leader == leader) && + (identical(other.title, title) || other.title == title) && + (identical(other.shortcode, shortcode) || + other.shortcode == shortcode) && + (identical(other.hasBeaconActivity, hasBeaconActivity) || + other.hasBeaconActivity == hasBeaconActivity) && + (identical(other.hasMemberActivity, hasMemberActivity) || + other.hasMemberActivity == hasMemberActivity)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + id, + const DeepCollectionEquality().hash(_beacons), + const DeepCollectionEquality().hash(_members), + leader, + title, + shortcode, + hasBeaconActivity, + hasMemberActivity); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$GroupEntityImplCopyWith<_$GroupEntityImpl> get copyWith => + __$$GroupEntityImplCopyWithImpl<_$GroupEntityImpl>(this, _$identity); +} + +abstract class _GroupEntity implements GroupEntity { + const factory _GroupEntity( + {final String? id, + final List? beacons, + final List? members, + final UserEntity? leader, + final String? title, + final String? shortcode, + final bool hasBeaconActivity, + final bool hasMemberActivity}) = _$GroupEntityImpl; + + @override + String? get id; + @override + List? get beacons; + @override + List? get members; + @override + UserEntity? get leader; + @override + String? get title; + @override + String? get shortcode; + @override + bool get hasBeaconActivity; + @override + bool get hasMemberActivity; + @override + @JsonKey(ignore: true) + _$$GroupEntityImplCopyWith<_$GroupEntityImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/domain/entities/landmark/landmark_entity.dart b/lib/domain/entities/landmark/landmark_entity.dart new file mode 100644 index 00000000..e6d6a130 --- /dev/null +++ b/lib/domain/entities/landmark/landmark_entity.dart @@ -0,0 +1,27 @@ +import 'package:beacon/domain/entities/location/location_entity.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +part 'landmark_entity.freezed.dart'; + +@freezed +class LandMarkEntity with _$LandMarkEntity { + const factory LandMarkEntity( + {String? id, + String? title, + LocationEntity? location, + UserEntity? createdBy}) = _LandMarkEntity; +} + +extension LandMarkEntityCopyWithExtension on LandMarkEntity { + LandMarkEntity copywith( + {String? id, + String? title, + LocationEntity? location, + UserEntity? createdBy}) { + return LandMarkEntity( + id: id ?? this.id, + title: title ?? this.title, + location: location ?? this.location, + createdBy: createdBy ?? this.createdBy); + } +} diff --git a/lib/domain/entities/landmark/landmark_entity.freezed.dart b/lib/domain/entities/landmark/landmark_entity.freezed.dart new file mode 100644 index 00000000..e0aa6955 --- /dev/null +++ b/lib/domain/entities/landmark/landmark_entity.freezed.dart @@ -0,0 +1,228 @@ +// 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 'landmark_entity.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 _$LandMarkEntity { + String? get id => throw _privateConstructorUsedError; + String? get title => throw _privateConstructorUsedError; + LocationEntity? get location => throw _privateConstructorUsedError; + UserEntity? get createdBy => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $LandMarkEntityCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $LandMarkEntityCopyWith<$Res> { + factory $LandMarkEntityCopyWith( + LandMarkEntity value, $Res Function(LandMarkEntity) then) = + _$LandMarkEntityCopyWithImpl<$Res, LandMarkEntity>; + @useResult + $Res call( + {String? id, + String? title, + LocationEntity? location, + UserEntity? createdBy}); + + $LocationEntityCopyWith<$Res>? get location; + $UserEntityCopyWith<$Res>? get createdBy; +} + +/// @nodoc +class _$LandMarkEntityCopyWithImpl<$Res, $Val extends LandMarkEntity> + implements $LandMarkEntityCopyWith<$Res> { + _$LandMarkEntityCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + Object? title = freezed, + Object? location = freezed, + Object? createdBy = freezed, + }) { + return _then(_value.copyWith( + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + title: freezed == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String?, + location: freezed == location + ? _value.location + : location // ignore: cast_nullable_to_non_nullable + as LocationEntity?, + createdBy: freezed == createdBy + ? _value.createdBy + : createdBy // ignore: cast_nullable_to_non_nullable + as UserEntity?, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $LocationEntityCopyWith<$Res>? get location { + if (_value.location == null) { + return null; + } + + return $LocationEntityCopyWith<$Res>(_value.location!, (value) { + return _then(_value.copyWith(location: value) as $Val); + }); + } + + @override + @pragma('vm:prefer-inline') + $UserEntityCopyWith<$Res>? get createdBy { + if (_value.createdBy == null) { + return null; + } + + return $UserEntityCopyWith<$Res>(_value.createdBy!, (value) { + return _then(_value.copyWith(createdBy: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$LandMarkEntityImplCopyWith<$Res> + implements $LandMarkEntityCopyWith<$Res> { + factory _$$LandMarkEntityImplCopyWith(_$LandMarkEntityImpl value, + $Res Function(_$LandMarkEntityImpl) then) = + __$$LandMarkEntityImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String? id, + String? title, + LocationEntity? location, + UserEntity? createdBy}); + + @override + $LocationEntityCopyWith<$Res>? get location; + @override + $UserEntityCopyWith<$Res>? get createdBy; +} + +/// @nodoc +class __$$LandMarkEntityImplCopyWithImpl<$Res> + extends _$LandMarkEntityCopyWithImpl<$Res, _$LandMarkEntityImpl> + implements _$$LandMarkEntityImplCopyWith<$Res> { + __$$LandMarkEntityImplCopyWithImpl( + _$LandMarkEntityImpl _value, $Res Function(_$LandMarkEntityImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + Object? title = freezed, + Object? location = freezed, + Object? createdBy = freezed, + }) { + return _then(_$LandMarkEntityImpl( + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + title: freezed == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String?, + location: freezed == location + ? _value.location + : location // ignore: cast_nullable_to_non_nullable + as LocationEntity?, + createdBy: freezed == createdBy + ? _value.createdBy + : createdBy // ignore: cast_nullable_to_non_nullable + as UserEntity?, + )); + } +} + +/// @nodoc + +class _$LandMarkEntityImpl implements _LandMarkEntity { + const _$LandMarkEntityImpl( + {this.id, this.title, this.location, this.createdBy}); + + @override + final String? id; + @override + final String? title; + @override + final LocationEntity? location; + @override + final UserEntity? createdBy; + + @override + String toString() { + return 'LandMarkEntity(id: $id, title: $title, location: $location, createdBy: $createdBy)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$LandMarkEntityImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.title, title) || other.title == title) && + (identical(other.location, location) || + other.location == location) && + (identical(other.createdBy, createdBy) || + other.createdBy == createdBy)); + } + + @override + int get hashCode => Object.hash(runtimeType, id, title, location, createdBy); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$LandMarkEntityImplCopyWith<_$LandMarkEntityImpl> get copyWith => + __$$LandMarkEntityImplCopyWithImpl<_$LandMarkEntityImpl>( + this, _$identity); +} + +abstract class _LandMarkEntity implements LandMarkEntity { + const factory _LandMarkEntity( + {final String? id, + final String? title, + final LocationEntity? location, + final UserEntity? createdBy}) = _$LandMarkEntityImpl; + + @override + String? get id; + @override + String? get title; + @override + LocationEntity? get location; + @override + UserEntity? get createdBy; + @override + @JsonKey(ignore: true) + _$$LandMarkEntityImplCopyWith<_$LandMarkEntityImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/domain/entities/location/location_entity.dart b/lib/domain/entities/location/location_entity.dart new file mode 100644 index 00000000..26c43f87 --- /dev/null +++ b/lib/domain/entities/location/location_entity.dart @@ -0,0 +1,22 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +part 'location_entity.freezed.dart'; + +@freezed +class LocationEntity with _$LocationEntity { + factory LocationEntity({String? id, String? lat, String? lon}) = + _LocationEntity; +} + +extension LocationEntityCopyWithExtension on LocationEntity { + LocationEntity copywith({ + String? id, + String? lat, + String? lon, + }) { + return LocationEntity( + id: id ?? this.id, + lat: lat ?? this.lat, + lon: lon ?? this.lon, + ); + } +} diff --git a/lib/domain/entities/location/location_entity.freezed.dart b/lib/domain/entities/location/location_entity.freezed.dart new file mode 100644 index 00000000..833de3e0 --- /dev/null +++ b/lib/domain/entities/location/location_entity.freezed.dart @@ -0,0 +1,168 @@ +// 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 'location_entity.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 _$LocationEntity { + String? get id => throw _privateConstructorUsedError; + String? get lat => throw _privateConstructorUsedError; + String? get lon => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $LocationEntityCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $LocationEntityCopyWith<$Res> { + factory $LocationEntityCopyWith( + LocationEntity value, $Res Function(LocationEntity) then) = + _$LocationEntityCopyWithImpl<$Res, LocationEntity>; + @useResult + $Res call({String? id, String? lat, String? lon}); +} + +/// @nodoc +class _$LocationEntityCopyWithImpl<$Res, $Val extends LocationEntity> + implements $LocationEntityCopyWith<$Res> { + _$LocationEntityCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + Object? lat = freezed, + Object? lon = freezed, + }) { + return _then(_value.copyWith( + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + lat: freezed == lat + ? _value.lat + : lat // ignore: cast_nullable_to_non_nullable + as String?, + lon: freezed == lon + ? _value.lon + : lon // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$LocationEntityImplCopyWith<$Res> + implements $LocationEntityCopyWith<$Res> { + factory _$$LocationEntityImplCopyWith(_$LocationEntityImpl value, + $Res Function(_$LocationEntityImpl) then) = + __$$LocationEntityImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({String? id, String? lat, String? lon}); +} + +/// @nodoc +class __$$LocationEntityImplCopyWithImpl<$Res> + extends _$LocationEntityCopyWithImpl<$Res, _$LocationEntityImpl> + implements _$$LocationEntityImplCopyWith<$Res> { + __$$LocationEntityImplCopyWithImpl( + _$LocationEntityImpl _value, $Res Function(_$LocationEntityImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + Object? lat = freezed, + Object? lon = freezed, + }) { + return _then(_$LocationEntityImpl( + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + lat: freezed == lat + ? _value.lat + : lat // ignore: cast_nullable_to_non_nullable + as String?, + lon: freezed == lon + ? _value.lon + : lon // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc + +class _$LocationEntityImpl implements _LocationEntity { + _$LocationEntityImpl({this.id, this.lat, this.lon}); + + @override + final String? id; + @override + final String? lat; + @override + final String? lon; + + @override + String toString() { + return 'LocationEntity(id: $id, lat: $lat, lon: $lon)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$LocationEntityImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.lat, lat) || other.lat == lat) && + (identical(other.lon, lon) || other.lon == lon)); + } + + @override + int get hashCode => Object.hash(runtimeType, id, lat, lon); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$LocationEntityImplCopyWith<_$LocationEntityImpl> get copyWith => + __$$LocationEntityImplCopyWithImpl<_$LocationEntityImpl>( + this, _$identity); +} + +abstract class _LocationEntity implements LocationEntity { + factory _LocationEntity( + {final String? id, + final String? lat, + final String? lon}) = _$LocationEntityImpl; + + @override + String? get id; + @override + String? get lat; + @override + String? get lon; + @override + @JsonKey(ignore: true) + _$$LocationEntityImplCopyWith<_$LocationEntityImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/domain/entities/subscriptions/beacon_locations_entity/beacon_locations_entity.dart b/lib/domain/entities/subscriptions/beacon_locations_entity/beacon_locations_entity.dart new file mode 100644 index 00000000..29c26b8e --- /dev/null +++ b/lib/domain/entities/subscriptions/beacon_locations_entity/beacon_locations_entity.dart @@ -0,0 +1,15 @@ +import 'package:beacon/domain/entities/landmark/landmark_entity.dart'; +import 'package:beacon/domain/entities/location/location_entity.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +part 'beacon_locations_entity.freezed.dart'; + +@freezed +class BeaconLocationsEntity with _$BeaconLocationsEntity { + factory BeaconLocationsEntity({ + UserEntity? userSOS, + List? route, + LandMarkEntity? landmark, + UserEntity? user, + }) = _BeaconLocationsEntity; +} diff --git a/lib/domain/entities/subscriptions/beacon_locations_entity/beacon_locations_entity.freezed.dart b/lib/domain/entities/subscriptions/beacon_locations_entity/beacon_locations_entity.freezed.dart new file mode 100644 index 00000000..592eece5 --- /dev/null +++ b/lib/domain/entities/subscriptions/beacon_locations_entity/beacon_locations_entity.freezed.dart @@ -0,0 +1,258 @@ +// 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 'beacon_locations_entity.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 _$BeaconLocationsEntity { + UserEntity? get userSOS => throw _privateConstructorUsedError; + List? get route => throw _privateConstructorUsedError; + LandMarkEntity? get landmark => throw _privateConstructorUsedError; + UserEntity? get user => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $BeaconLocationsEntityCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $BeaconLocationsEntityCopyWith<$Res> { + factory $BeaconLocationsEntityCopyWith(BeaconLocationsEntity value, + $Res Function(BeaconLocationsEntity) then) = + _$BeaconLocationsEntityCopyWithImpl<$Res, BeaconLocationsEntity>; + @useResult + $Res call( + {UserEntity? userSOS, + List? route, + LandMarkEntity? landmark, + UserEntity? user}); + + $UserEntityCopyWith<$Res>? get userSOS; + $LandMarkEntityCopyWith<$Res>? get landmark; + $UserEntityCopyWith<$Res>? get user; +} + +/// @nodoc +class _$BeaconLocationsEntityCopyWithImpl<$Res, + $Val extends BeaconLocationsEntity> + implements $BeaconLocationsEntityCopyWith<$Res> { + _$BeaconLocationsEntityCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? userSOS = freezed, + Object? route = freezed, + Object? landmark = freezed, + Object? user = freezed, + }) { + return _then(_value.copyWith( + userSOS: freezed == userSOS + ? _value.userSOS + : userSOS // ignore: cast_nullable_to_non_nullable + as UserEntity?, + route: freezed == route + ? _value.route + : route // ignore: cast_nullable_to_non_nullable + as List?, + landmark: freezed == landmark + ? _value.landmark + : landmark // ignore: cast_nullable_to_non_nullable + as LandMarkEntity?, + user: freezed == user + ? _value.user + : user // ignore: cast_nullable_to_non_nullable + as UserEntity?, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $UserEntityCopyWith<$Res>? get userSOS { + if (_value.userSOS == null) { + return null; + } + + return $UserEntityCopyWith<$Res>(_value.userSOS!, (value) { + return _then(_value.copyWith(userSOS: value) as $Val); + }); + } + + @override + @pragma('vm:prefer-inline') + $LandMarkEntityCopyWith<$Res>? get landmark { + if (_value.landmark == null) { + return null; + } + + return $LandMarkEntityCopyWith<$Res>(_value.landmark!, (value) { + return _then(_value.copyWith(landmark: value) as $Val); + }); + } + + @override + @pragma('vm:prefer-inline') + $UserEntityCopyWith<$Res>? get user { + if (_value.user == null) { + return null; + } + + return $UserEntityCopyWith<$Res>(_value.user!, (value) { + return _then(_value.copyWith(user: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$BeaconLocationsEntityImplCopyWith<$Res> + implements $BeaconLocationsEntityCopyWith<$Res> { + factory _$$BeaconLocationsEntityImplCopyWith( + _$BeaconLocationsEntityImpl value, + $Res Function(_$BeaconLocationsEntityImpl) then) = + __$$BeaconLocationsEntityImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {UserEntity? userSOS, + List? route, + LandMarkEntity? landmark, + UserEntity? user}); + + @override + $UserEntityCopyWith<$Res>? get userSOS; + @override + $LandMarkEntityCopyWith<$Res>? get landmark; + @override + $UserEntityCopyWith<$Res>? get user; +} + +/// @nodoc +class __$$BeaconLocationsEntityImplCopyWithImpl<$Res> + extends _$BeaconLocationsEntityCopyWithImpl<$Res, + _$BeaconLocationsEntityImpl> + implements _$$BeaconLocationsEntityImplCopyWith<$Res> { + __$$BeaconLocationsEntityImplCopyWithImpl(_$BeaconLocationsEntityImpl _value, + $Res Function(_$BeaconLocationsEntityImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? userSOS = freezed, + Object? route = freezed, + Object? landmark = freezed, + Object? user = freezed, + }) { + return _then(_$BeaconLocationsEntityImpl( + userSOS: freezed == userSOS + ? _value.userSOS + : userSOS // ignore: cast_nullable_to_non_nullable + as UserEntity?, + route: freezed == route + ? _value._route + : route // ignore: cast_nullable_to_non_nullable + as List?, + landmark: freezed == landmark + ? _value.landmark + : landmark // ignore: cast_nullable_to_non_nullable + as LandMarkEntity?, + user: freezed == user + ? _value.user + : user // ignore: cast_nullable_to_non_nullable + as UserEntity?, + )); + } +} + +/// @nodoc + +class _$BeaconLocationsEntityImpl implements _BeaconLocationsEntity { + _$BeaconLocationsEntityImpl( + {this.userSOS, + final List? route, + this.landmark, + this.user}) + : _route = route; + + @override + final UserEntity? userSOS; + final List? _route; + @override + List? get route { + final value = _route; + if (value == null) return null; + if (_route is EqualUnmodifiableListView) return _route; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + @override + final LandMarkEntity? landmark; + @override + final UserEntity? user; + + @override + String toString() { + return 'BeaconLocationsEntity(userSOS: $userSOS, route: $route, landmark: $landmark, user: $user)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$BeaconLocationsEntityImpl && + (identical(other.userSOS, userSOS) || other.userSOS == userSOS) && + const DeepCollectionEquality().equals(other._route, _route) && + (identical(other.landmark, landmark) || + other.landmark == landmark) && + (identical(other.user, user) || other.user == user)); + } + + @override + int get hashCode => Object.hash(runtimeType, userSOS, + const DeepCollectionEquality().hash(_route), landmark, user); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$BeaconLocationsEntityImplCopyWith<_$BeaconLocationsEntityImpl> + get copyWith => __$$BeaconLocationsEntityImplCopyWithImpl< + _$BeaconLocationsEntityImpl>(this, _$identity); +} + +abstract class _BeaconLocationsEntity implements BeaconLocationsEntity { + factory _BeaconLocationsEntity( + {final UserEntity? userSOS, + final List? route, + final LandMarkEntity? landmark, + final UserEntity? user}) = _$BeaconLocationsEntityImpl; + + @override + UserEntity? get userSOS; + @override + List? get route; + @override + LandMarkEntity? get landmark; + @override + UserEntity? get user; + @override + @JsonKey(ignore: true) + _$$BeaconLocationsEntityImplCopyWith<_$BeaconLocationsEntityImpl> + get copyWith => throw _privateConstructorUsedError; +} diff --git a/lib/domain/entities/subscriptions/join_leave_beacon_entity/join_leave_beacon_entity.dart b/lib/domain/entities/subscriptions/join_leave_beacon_entity/join_leave_beacon_entity.dart new file mode 100644 index 00000000..2900bed4 --- /dev/null +++ b/lib/domain/entities/subscriptions/join_leave_beacon_entity/join_leave_beacon_entity.dart @@ -0,0 +1,10 @@ +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +part 'join_leave_beacon_entity.freezed.dart'; + +@freezed +class JoinLeaveBeaconEntity with _$JoinLeaveBeaconEntity { + factory JoinLeaveBeaconEntity( + {UserEntity? newfollower, + UserEntity? inactiveuser}) = _JoinLeaveBeaconEntity; +} diff --git a/lib/domain/entities/subscriptions/join_leave_beacon_entity/join_leave_beacon_entity.freezed.dart b/lib/domain/entities/subscriptions/join_leave_beacon_entity/join_leave_beacon_entity.freezed.dart new file mode 100644 index 00000000..51bd776c --- /dev/null +++ b/lib/domain/entities/subscriptions/join_leave_beacon_entity/join_leave_beacon_entity.freezed.dart @@ -0,0 +1,188 @@ +// 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 'join_leave_beacon_entity.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 _$JoinLeaveBeaconEntity { + UserEntity? get newfollower => throw _privateConstructorUsedError; + UserEntity? get inactiveuser => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $JoinLeaveBeaconEntityCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $JoinLeaveBeaconEntityCopyWith<$Res> { + factory $JoinLeaveBeaconEntityCopyWith(JoinLeaveBeaconEntity value, + $Res Function(JoinLeaveBeaconEntity) then) = + _$JoinLeaveBeaconEntityCopyWithImpl<$Res, JoinLeaveBeaconEntity>; + @useResult + $Res call({UserEntity? newfollower, UserEntity? inactiveuser}); + + $UserEntityCopyWith<$Res>? get newfollower; + $UserEntityCopyWith<$Res>? get inactiveuser; +} + +/// @nodoc +class _$JoinLeaveBeaconEntityCopyWithImpl<$Res, + $Val extends JoinLeaveBeaconEntity> + implements $JoinLeaveBeaconEntityCopyWith<$Res> { + _$JoinLeaveBeaconEntityCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? newfollower = freezed, + Object? inactiveuser = freezed, + }) { + return _then(_value.copyWith( + newfollower: freezed == newfollower + ? _value.newfollower + : newfollower // ignore: cast_nullable_to_non_nullable + as UserEntity?, + inactiveuser: freezed == inactiveuser + ? _value.inactiveuser + : inactiveuser // ignore: cast_nullable_to_non_nullable + as UserEntity?, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $UserEntityCopyWith<$Res>? get newfollower { + if (_value.newfollower == null) { + return null; + } + + return $UserEntityCopyWith<$Res>(_value.newfollower!, (value) { + return _then(_value.copyWith(newfollower: value) as $Val); + }); + } + + @override + @pragma('vm:prefer-inline') + $UserEntityCopyWith<$Res>? get inactiveuser { + if (_value.inactiveuser == null) { + return null; + } + + return $UserEntityCopyWith<$Res>(_value.inactiveuser!, (value) { + return _then(_value.copyWith(inactiveuser: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$JoinLeaveBeaconEntityImplCopyWith<$Res> + implements $JoinLeaveBeaconEntityCopyWith<$Res> { + factory _$$JoinLeaveBeaconEntityImplCopyWith( + _$JoinLeaveBeaconEntityImpl value, + $Res Function(_$JoinLeaveBeaconEntityImpl) then) = + __$$JoinLeaveBeaconEntityImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({UserEntity? newfollower, UserEntity? inactiveuser}); + + @override + $UserEntityCopyWith<$Res>? get newfollower; + @override + $UserEntityCopyWith<$Res>? get inactiveuser; +} + +/// @nodoc +class __$$JoinLeaveBeaconEntityImplCopyWithImpl<$Res> + extends _$JoinLeaveBeaconEntityCopyWithImpl<$Res, + _$JoinLeaveBeaconEntityImpl> + implements _$$JoinLeaveBeaconEntityImplCopyWith<$Res> { + __$$JoinLeaveBeaconEntityImplCopyWithImpl(_$JoinLeaveBeaconEntityImpl _value, + $Res Function(_$JoinLeaveBeaconEntityImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? newfollower = freezed, + Object? inactiveuser = freezed, + }) { + return _then(_$JoinLeaveBeaconEntityImpl( + newfollower: freezed == newfollower + ? _value.newfollower + : newfollower // ignore: cast_nullable_to_non_nullable + as UserEntity?, + inactiveuser: freezed == inactiveuser + ? _value.inactiveuser + : inactiveuser // ignore: cast_nullable_to_non_nullable + as UserEntity?, + )); + } +} + +/// @nodoc + +class _$JoinLeaveBeaconEntityImpl implements _JoinLeaveBeaconEntity { + _$JoinLeaveBeaconEntityImpl({this.newfollower, this.inactiveuser}); + + @override + final UserEntity? newfollower; + @override + final UserEntity? inactiveuser; + + @override + String toString() { + return 'JoinLeaveBeaconEntity(newfollower: $newfollower, inactiveuser: $inactiveuser)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$JoinLeaveBeaconEntityImpl && + (identical(other.newfollower, newfollower) || + other.newfollower == newfollower) && + (identical(other.inactiveuser, inactiveuser) || + other.inactiveuser == inactiveuser)); + } + + @override + int get hashCode => Object.hash(runtimeType, newfollower, inactiveuser); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$JoinLeaveBeaconEntityImplCopyWith<_$JoinLeaveBeaconEntityImpl> + get copyWith => __$$JoinLeaveBeaconEntityImplCopyWithImpl< + _$JoinLeaveBeaconEntityImpl>(this, _$identity); +} + +abstract class _JoinLeaveBeaconEntity implements JoinLeaveBeaconEntity { + factory _JoinLeaveBeaconEntity( + {final UserEntity? newfollower, + final UserEntity? inactiveuser}) = _$JoinLeaveBeaconEntityImpl; + + @override + UserEntity? get newfollower; + @override + UserEntity? get inactiveuser; + @override + @JsonKey(ignore: true) + _$$JoinLeaveBeaconEntityImplCopyWith<_$JoinLeaveBeaconEntityImpl> + get copyWith => throw _privateConstructorUsedError; +} diff --git a/lib/domain/entities/subscriptions/updated_group_entity/updated_group_entity.dart b/lib/domain/entities/subscriptions/updated_group_entity/updated_group_entity.dart new file mode 100644 index 00000000..d74dc9e0 --- /dev/null +++ b/lib/domain/entities/subscriptions/updated_group_entity/updated_group_entity.dart @@ -0,0 +1,32 @@ +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +part 'updated_group_entity.freezed.dart'; + +@freezed +class UpdatedGroupEntity with _$UpdatedGroupEntity { + factory UpdatedGroupEntity( + {String? id, + BeaconEntity? deletedBeacon, + BeaconEntity? updatedBeacon, + BeaconEntity? newBeacon, + UserEntity? newUser}) = _UpdatedGroupEntity; +} + +extension UpdatedGroupEntityCopyWithExtension on UpdatedGroupEntity { + UpdatedGroupEntity copywith({ + String? id, + BeaconEntity? deletedBeacon, + BeaconEntity? updatedBeacon, + BeaconEntity? newBeacon, + UserEntity? newUser, + }) { + return UpdatedGroupEntity( + id: id ?? this.id, + deletedBeacon: deletedBeacon ?? this.deletedBeacon, + updatedBeacon: updatedBeacon ?? this.updatedBeacon, + newBeacon: newBeacon ?? this.newBeacon, + newUser: newUser ?? this.newUser, + ); + } +} diff --git a/lib/domain/entities/subscriptions/updated_group_entity/updated_group_entity.freezed.dart b/lib/domain/entities/subscriptions/updated_group_entity/updated_group_entity.freezed.dart new file mode 100644 index 00000000..2e18afd3 --- /dev/null +++ b/lib/domain/entities/subscriptions/updated_group_entity/updated_group_entity.freezed.dart @@ -0,0 +1,283 @@ +// 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 'updated_group_entity.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 _$UpdatedGroupEntity { + String? get id => throw _privateConstructorUsedError; + BeaconEntity? get deletedBeacon => throw _privateConstructorUsedError; + BeaconEntity? get updatedBeacon => throw _privateConstructorUsedError; + BeaconEntity? get newBeacon => throw _privateConstructorUsedError; + UserEntity? get newUser => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $UpdatedGroupEntityCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $UpdatedGroupEntityCopyWith<$Res> { + factory $UpdatedGroupEntityCopyWith( + UpdatedGroupEntity value, $Res Function(UpdatedGroupEntity) then) = + _$UpdatedGroupEntityCopyWithImpl<$Res, UpdatedGroupEntity>; + @useResult + $Res call( + {String? id, + BeaconEntity? deletedBeacon, + BeaconEntity? updatedBeacon, + BeaconEntity? newBeacon, + UserEntity? newUser}); + + $BeaconEntityCopyWith<$Res>? get deletedBeacon; + $BeaconEntityCopyWith<$Res>? get updatedBeacon; + $BeaconEntityCopyWith<$Res>? get newBeacon; + $UserEntityCopyWith<$Res>? get newUser; +} + +/// @nodoc +class _$UpdatedGroupEntityCopyWithImpl<$Res, $Val extends UpdatedGroupEntity> + implements $UpdatedGroupEntityCopyWith<$Res> { + _$UpdatedGroupEntityCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + Object? deletedBeacon = freezed, + Object? updatedBeacon = freezed, + Object? newBeacon = freezed, + Object? newUser = freezed, + }) { + return _then(_value.copyWith( + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + deletedBeacon: freezed == deletedBeacon + ? _value.deletedBeacon + : deletedBeacon // ignore: cast_nullable_to_non_nullable + as BeaconEntity?, + updatedBeacon: freezed == updatedBeacon + ? _value.updatedBeacon + : updatedBeacon // ignore: cast_nullable_to_non_nullable + as BeaconEntity?, + newBeacon: freezed == newBeacon + ? _value.newBeacon + : newBeacon // ignore: cast_nullable_to_non_nullable + as BeaconEntity?, + newUser: freezed == newUser + ? _value.newUser + : newUser // ignore: cast_nullable_to_non_nullable + as UserEntity?, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $BeaconEntityCopyWith<$Res>? get deletedBeacon { + if (_value.deletedBeacon == null) { + return null; + } + + return $BeaconEntityCopyWith<$Res>(_value.deletedBeacon!, (value) { + return _then(_value.copyWith(deletedBeacon: value) as $Val); + }); + } + + @override + @pragma('vm:prefer-inline') + $BeaconEntityCopyWith<$Res>? get updatedBeacon { + if (_value.updatedBeacon == null) { + return null; + } + + return $BeaconEntityCopyWith<$Res>(_value.updatedBeacon!, (value) { + return _then(_value.copyWith(updatedBeacon: value) as $Val); + }); + } + + @override + @pragma('vm:prefer-inline') + $BeaconEntityCopyWith<$Res>? get newBeacon { + if (_value.newBeacon == null) { + return null; + } + + return $BeaconEntityCopyWith<$Res>(_value.newBeacon!, (value) { + return _then(_value.copyWith(newBeacon: value) as $Val); + }); + } + + @override + @pragma('vm:prefer-inline') + $UserEntityCopyWith<$Res>? get newUser { + if (_value.newUser == null) { + return null; + } + + return $UserEntityCopyWith<$Res>(_value.newUser!, (value) { + return _then(_value.copyWith(newUser: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$UpdatedGroupEntityImplCopyWith<$Res> + implements $UpdatedGroupEntityCopyWith<$Res> { + factory _$$UpdatedGroupEntityImplCopyWith(_$UpdatedGroupEntityImpl value, + $Res Function(_$UpdatedGroupEntityImpl) then) = + __$$UpdatedGroupEntityImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String? id, + BeaconEntity? deletedBeacon, + BeaconEntity? updatedBeacon, + BeaconEntity? newBeacon, + UserEntity? newUser}); + + @override + $BeaconEntityCopyWith<$Res>? get deletedBeacon; + @override + $BeaconEntityCopyWith<$Res>? get updatedBeacon; + @override + $BeaconEntityCopyWith<$Res>? get newBeacon; + @override + $UserEntityCopyWith<$Res>? get newUser; +} + +/// @nodoc +class __$$UpdatedGroupEntityImplCopyWithImpl<$Res> + extends _$UpdatedGroupEntityCopyWithImpl<$Res, _$UpdatedGroupEntityImpl> + implements _$$UpdatedGroupEntityImplCopyWith<$Res> { + __$$UpdatedGroupEntityImplCopyWithImpl(_$UpdatedGroupEntityImpl _value, + $Res Function(_$UpdatedGroupEntityImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + Object? deletedBeacon = freezed, + Object? updatedBeacon = freezed, + Object? newBeacon = freezed, + Object? newUser = freezed, + }) { + return _then(_$UpdatedGroupEntityImpl( + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + deletedBeacon: freezed == deletedBeacon + ? _value.deletedBeacon + : deletedBeacon // ignore: cast_nullable_to_non_nullable + as BeaconEntity?, + updatedBeacon: freezed == updatedBeacon + ? _value.updatedBeacon + : updatedBeacon // ignore: cast_nullable_to_non_nullable + as BeaconEntity?, + newBeacon: freezed == newBeacon + ? _value.newBeacon + : newBeacon // ignore: cast_nullable_to_non_nullable + as BeaconEntity?, + newUser: freezed == newUser + ? _value.newUser + : newUser // ignore: cast_nullable_to_non_nullable + as UserEntity?, + )); + } +} + +/// @nodoc + +class _$UpdatedGroupEntityImpl implements _UpdatedGroupEntity { + _$UpdatedGroupEntityImpl( + {this.id, + this.deletedBeacon, + this.updatedBeacon, + this.newBeacon, + this.newUser}); + + @override + final String? id; + @override + final BeaconEntity? deletedBeacon; + @override + final BeaconEntity? updatedBeacon; + @override + final BeaconEntity? newBeacon; + @override + final UserEntity? newUser; + + @override + String toString() { + return 'UpdatedGroupEntity(id: $id, deletedBeacon: $deletedBeacon, updatedBeacon: $updatedBeacon, newBeacon: $newBeacon, newUser: $newUser)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$UpdatedGroupEntityImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.deletedBeacon, deletedBeacon) || + other.deletedBeacon == deletedBeacon) && + (identical(other.updatedBeacon, updatedBeacon) || + other.updatedBeacon == updatedBeacon) && + (identical(other.newBeacon, newBeacon) || + other.newBeacon == newBeacon) && + (identical(other.newUser, newUser) || other.newUser == newUser)); + } + + @override + int get hashCode => Object.hash( + runtimeType, id, deletedBeacon, updatedBeacon, newBeacon, newUser); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$UpdatedGroupEntityImplCopyWith<_$UpdatedGroupEntityImpl> get copyWith => + __$$UpdatedGroupEntityImplCopyWithImpl<_$UpdatedGroupEntityImpl>( + this, _$identity); +} + +abstract class _UpdatedGroupEntity implements UpdatedGroupEntity { + factory _UpdatedGroupEntity( + {final String? id, + final BeaconEntity? deletedBeacon, + final BeaconEntity? updatedBeacon, + final BeaconEntity? newBeacon, + final UserEntity? newUser}) = _$UpdatedGroupEntityImpl; + + @override + String? get id; + @override + BeaconEntity? get deletedBeacon; + @override + BeaconEntity? get updatedBeacon; + @override + BeaconEntity? get newBeacon; + @override + UserEntity? get newUser; + @override + @JsonKey(ignore: true) + _$$UpdatedGroupEntityImplCopyWith<_$UpdatedGroupEntityImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/domain/entities/user/user_entity.dart b/lib/domain/entities/user/user_entity.dart new file mode 100644 index 00000000..572c9031 --- /dev/null +++ b/lib/domain/entities/user/user_entity.dart @@ -0,0 +1,45 @@ +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/domain/entities/group/group_entity.dart'; +import 'package:beacon/domain/entities/location/location_entity.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'user_entity.freezed.dart'; + +@freezed +class UserEntity with _$UserEntity { + const factory UserEntity( + {String? id, + List? groups, + List? beacons, + String? authToken, + String? email, + bool? isGuest, + String? name, + bool? isVerified, + LocationEntity? location}) = _UserEntity; +} + +extension UserEntityCopyWithExtension on UserEntity { + UserEntity copywith({ + String? id, + List? groups, + List? beacons, + String? authToken, + String? email, + bool? isGuest, + String? name, + bool? isVerified, + LocationEntity? location, + }) { + return UserEntity( + id: id ?? this.id, + groups: groups ?? this.groups, + beacons: beacons ?? this.beacons, + authToken: authToken ?? this.authToken, + email: email ?? this.email, + isGuest: isGuest ?? this.isGuest, + name: name ?? this.name, + location: location ?? this.location, + isVerified: isVerified ?? this.isVerified); + } +} diff --git a/lib/domain/entities/user/user_entity.freezed.dart b/lib/domain/entities/user/user_entity.freezed.dart new file mode 100644 index 00000000..ee0b0074 --- /dev/null +++ b/lib/domain/entities/user/user_entity.freezed.dart @@ -0,0 +1,344 @@ +// 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 'user_entity.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 _$UserEntity { + String? get id => throw _privateConstructorUsedError; + List? get groups => throw _privateConstructorUsedError; + List? get beacons => throw _privateConstructorUsedError; + String? get authToken => throw _privateConstructorUsedError; + String? get email => throw _privateConstructorUsedError; + bool? get isGuest => throw _privateConstructorUsedError; + String? get name => throw _privateConstructorUsedError; + bool? get isVerified => throw _privateConstructorUsedError; + LocationEntity? get location => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $UserEntityCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $UserEntityCopyWith<$Res> { + factory $UserEntityCopyWith( + UserEntity value, $Res Function(UserEntity) then) = + _$UserEntityCopyWithImpl<$Res, UserEntity>; + @useResult + $Res call( + {String? id, + List? groups, + List? beacons, + String? authToken, + String? email, + bool? isGuest, + String? name, + bool? isVerified, + LocationEntity? location}); + + $LocationEntityCopyWith<$Res>? get location; +} + +/// @nodoc +class _$UserEntityCopyWithImpl<$Res, $Val extends UserEntity> + implements $UserEntityCopyWith<$Res> { + _$UserEntityCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + Object? groups = freezed, + Object? beacons = freezed, + Object? authToken = freezed, + Object? email = freezed, + Object? isGuest = freezed, + Object? name = freezed, + Object? isVerified = freezed, + Object? location = freezed, + }) { + return _then(_value.copyWith( + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + groups: freezed == groups + ? _value.groups + : groups // ignore: cast_nullable_to_non_nullable + as List?, + beacons: freezed == beacons + ? _value.beacons + : beacons // ignore: cast_nullable_to_non_nullable + as List?, + authToken: freezed == authToken + ? _value.authToken + : authToken // ignore: cast_nullable_to_non_nullable + as String?, + email: freezed == email + ? _value.email + : email // ignore: cast_nullable_to_non_nullable + as String?, + isGuest: freezed == isGuest + ? _value.isGuest + : isGuest // ignore: cast_nullable_to_non_nullable + as bool?, + name: freezed == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String?, + isVerified: freezed == isVerified + ? _value.isVerified + : isVerified // ignore: cast_nullable_to_non_nullable + as bool?, + location: freezed == location + ? _value.location + : location // ignore: cast_nullable_to_non_nullable + as LocationEntity?, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $LocationEntityCopyWith<$Res>? get location { + if (_value.location == null) { + return null; + } + + return $LocationEntityCopyWith<$Res>(_value.location!, (value) { + return _then(_value.copyWith(location: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$UserEntityImplCopyWith<$Res> + implements $UserEntityCopyWith<$Res> { + factory _$$UserEntityImplCopyWith( + _$UserEntityImpl value, $Res Function(_$UserEntityImpl) then) = + __$$UserEntityImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String? id, + List? groups, + List? beacons, + String? authToken, + String? email, + bool? isGuest, + String? name, + bool? isVerified, + LocationEntity? location}); + + @override + $LocationEntityCopyWith<$Res>? get location; +} + +/// @nodoc +class __$$UserEntityImplCopyWithImpl<$Res> + extends _$UserEntityCopyWithImpl<$Res, _$UserEntityImpl> + implements _$$UserEntityImplCopyWith<$Res> { + __$$UserEntityImplCopyWithImpl( + _$UserEntityImpl _value, $Res Function(_$UserEntityImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = freezed, + Object? groups = freezed, + Object? beacons = freezed, + Object? authToken = freezed, + Object? email = freezed, + Object? isGuest = freezed, + Object? name = freezed, + Object? isVerified = freezed, + Object? location = freezed, + }) { + return _then(_$UserEntityImpl( + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + groups: freezed == groups + ? _value._groups + : groups // ignore: cast_nullable_to_non_nullable + as List?, + beacons: freezed == beacons + ? _value._beacons + : beacons // ignore: cast_nullable_to_non_nullable + as List?, + authToken: freezed == authToken + ? _value.authToken + : authToken // ignore: cast_nullable_to_non_nullable + as String?, + email: freezed == email + ? _value.email + : email // ignore: cast_nullable_to_non_nullable + as String?, + isGuest: freezed == isGuest + ? _value.isGuest + : isGuest // ignore: cast_nullable_to_non_nullable + as bool?, + name: freezed == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String?, + isVerified: freezed == isVerified + ? _value.isVerified + : isVerified // ignore: cast_nullable_to_non_nullable + as bool?, + location: freezed == location + ? _value.location + : location // ignore: cast_nullable_to_non_nullable + as LocationEntity?, + )); + } +} + +/// @nodoc + +class _$UserEntityImpl implements _UserEntity { + const _$UserEntityImpl( + {this.id, + final List? groups, + final List? beacons, + this.authToken, + this.email, + this.isGuest, + this.name, + this.isVerified, + this.location}) + : _groups = groups, + _beacons = beacons; + + @override + final String? id; + final List? _groups; + @override + List? get groups { + final value = _groups; + if (value == null) return null; + if (_groups is EqualUnmodifiableListView) return _groups; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + final List? _beacons; + @override + List? get beacons { + final value = _beacons; + if (value == null) return null; + if (_beacons is EqualUnmodifiableListView) return _beacons; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + @override + final String? authToken; + @override + final String? email; + @override + final bool? isGuest; + @override + final String? name; + @override + final bool? isVerified; + @override + final LocationEntity? location; + + @override + String toString() { + return 'UserEntity(id: $id, groups: $groups, beacons: $beacons, authToken: $authToken, email: $email, isGuest: $isGuest, name: $name, isVerified: $isVerified, location: $location)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$UserEntityImpl && + (identical(other.id, id) || other.id == id) && + const DeepCollectionEquality().equals(other._groups, _groups) && + const DeepCollectionEquality().equals(other._beacons, _beacons) && + (identical(other.authToken, authToken) || + other.authToken == authToken) && + (identical(other.email, email) || other.email == email) && + (identical(other.isGuest, isGuest) || other.isGuest == isGuest) && + (identical(other.name, name) || other.name == name) && + (identical(other.isVerified, isVerified) || + other.isVerified == isVerified) && + (identical(other.location, location) || + other.location == location)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + id, + const DeepCollectionEquality().hash(_groups), + const DeepCollectionEquality().hash(_beacons), + authToken, + email, + isGuest, + name, + isVerified, + location); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$UserEntityImplCopyWith<_$UserEntityImpl> get copyWith => + __$$UserEntityImplCopyWithImpl<_$UserEntityImpl>(this, _$identity); +} + +abstract class _UserEntity implements UserEntity { + const factory _UserEntity( + {final String? id, + final List? groups, + final List? beacons, + final String? authToken, + final String? email, + final bool? isGuest, + final String? name, + final bool? isVerified, + final LocationEntity? location}) = _$UserEntityImpl; + + @override + String? get id; + @override + List? get groups; + @override + List? get beacons; + @override + String? get authToken; + @override + String? get email; + @override + bool? get isGuest; + @override + String? get name; + @override + bool? get isVerified; + @override + LocationEntity? get location; + @override + @JsonKey(ignore: true) + _$$UserEntityImplCopyWith<_$UserEntityImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/domain/repositories/auth_repository.dart b/lib/domain/repositories/auth_repository.dart new file mode 100644 index 00000000..ff88d7d1 --- /dev/null +++ b/lib/domain/repositories/auth_repository.dart @@ -0,0 +1,20 @@ +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; + +abstract class AuthRepository { + // userinfo function + Future> getUser(); + + // Signup function + Future> register( + String name, String email, String password); + + // Login function + Future> login(String email, String password); + + Future> oAuth(String name, String email); + + Future> sendVerificationCode(); + + Future> completeVerification(); +} diff --git a/lib/domain/repositories/group_repository.dart b/lib/domain/repositories/group_repository.dart new file mode 100644 index 00000000..5155ef27 --- /dev/null +++ b/lib/domain/repositories/group_repository.dart @@ -0,0 +1,26 @@ +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; + +abstract class GroupRepository { + Future> createHike(String title, int startsAt, + int expiresAt, String lat, String lon, String groupID); + + Future> joinHike(String hikeId); + + Future>> fetchHikes( + String groupID, int page, int pageSize); + + Future>> nearbyHikes( + String groupId, String lat, String lon, double radius); + + Future>> filterHikes( + String groupId, String type); + + Future> deleteBeacon(String? beaconId); + + Future> rescheduleHike( + int expiresAt, int startsAt, String beaconId); + + Future> removeMember(String groupId, String memberId); +} diff --git a/lib/domain/repositories/hike_repository.dart b/lib/domain/repositories/hike_repository.dart new file mode 100644 index 00000000..8b8a31db --- /dev/null +++ b/lib/domain/repositories/hike_repository.dart @@ -0,0 +1,22 @@ +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/domain/entities/landmark/landmark_entity.dart'; +import 'package:beacon/domain/entities/location/location_entity.dart'; +import 'package:beacon/domain/entities/subscriptions/beacon_locations_entity/beacon_locations_entity.dart'; +import 'package:beacon/domain/entities/subscriptions/join_leave_beacon_entity/join_leave_beacon_entity.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; + +abstract class HikeRepository { + Future> updateBeaconLocation( + String beaconId, LatLng position); + Future> fetchBeaconDetails(String beaconId); + Future> createLandMark( + String id, String title, String lat, String lon); + Future> changeUserLocation(String id, LatLng latLng); + Future> sos(String beaconId); + Stream> beaconLocationsSubscription( + String beaconId); + Stream> joinLeaveBeaconSubscription( + String beaconId); +} diff --git a/lib/domain/repositories/home_repository.dart b/lib/domain/repositories/home_repository.dart new file mode 100644 index 00000000..237ba5c3 --- /dev/null +++ b/lib/domain/repositories/home_repository.dart @@ -0,0 +1,13 @@ +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/domain/entities/group/group_entity.dart'; +import 'package:beacon/domain/entities/subscriptions/updated_group_entity/updated_group_entity.dart'; + +abstract class HomeRepository { + Future>> fetchGroups(int page, int pageSize); + Future> fetchGroup(String groupId); + Future> createGroup(String title); + Future> joinGroup(String shortCode); + Stream> groupUpdateSubscription( + List groupIds); + Future> changeShortcode(String groupId); +} diff --git a/lib/domain/usecase/auth_usecase.dart b/lib/domain/usecase/auth_usecase.dart new file mode 100644 index 00000000..7a4a875f --- /dev/null +++ b/lib/domain/usecase/auth_usecase.dart @@ -0,0 +1,35 @@ +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:beacon/domain/repositories/auth_repository.dart'; + +class AuthUseCase { + final AuthRepository authRepository; + + AuthUseCase({required this.authRepository}); + + Future> registerUseCase( + String name, String email, String password) async { + return authRepository.register(name, email, password); + } + + Future> loginUserCase( + String email, String password) async { + return authRepository.login(email, password); + } + + Future> oAuthUseCase(String name, String email) async { + return authRepository.oAuth(name, email); + } + + Future> getUserInfoUseCase() async { + return authRepository.getUser(); + } + + Future> sendVerificationCode() { + return authRepository.sendVerificationCode(); + } + + Future> completeVerification() { + return authRepository.completeVerification(); + } +} diff --git a/lib/domain/usecase/group_usecase.dart b/lib/domain/usecase/group_usecase.dart new file mode 100644 index 00000000..bed1db5e --- /dev/null +++ b/lib/domain/usecase/group_usecase.dart @@ -0,0 +1,47 @@ +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:beacon/domain/repositories/group_repository.dart'; + +class GroupUseCase { + final GroupRepository _groupRepo; + + GroupUseCase(this._groupRepo); + + Future>> fetchHikes( + String groupID, int page, int pageSize) { + return _groupRepo.fetchHikes(groupID, page, pageSize); + } + + Future> joinHike(String shortcode) { + return _groupRepo.joinHike(shortcode); + } + + Future> createHike(String title, int startsAt, + int expiresAt, String lat, String lon, String groupID) { + return _groupRepo.createHike(title, startsAt, expiresAt, lat, lon, groupID); + } + + Future>> nearbyHikes( + String groupId, String lat, String lon, double radius) { + return _groupRepo.nearbyHikes(groupId, lat, lon, radius); + } + + Future>> filterHikes( + String groupId, String type) { + return _groupRepo.filterHikes(groupId, type); + } + + Future> deleteBeacon(String? beaconId) { + return _groupRepo.deleteBeacon(beaconId); + } + + Future> rescheduleHike( + int newExpiresAt, int newStartsAt, String beaconId) { + return _groupRepo.rescheduleHike(newExpiresAt, newStartsAt, beaconId); + } + + Future> removeMember(String groupId, String memberId) { + return _groupRepo.removeMember(groupId, memberId); + } +} diff --git a/lib/domain/usecase/hike_usecase.dart b/lib/domain/usecase/hike_usecase.dart new file mode 100644 index 00000000..49dfb46c --- /dev/null +++ b/lib/domain/usecase/hike_usecase.dart @@ -0,0 +1,47 @@ +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/domain/entities/landmark/landmark_entity.dart'; +import 'package:beacon/domain/entities/location/location_entity.dart'; +import 'package:beacon/domain/entities/subscriptions/beacon_locations_entity/beacon_locations_entity.dart'; +import 'package:beacon/domain/entities/subscriptions/join_leave_beacon_entity/join_leave_beacon_entity.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:beacon/domain/repositories/hike_repository.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; + +class HikeUseCase { + final HikeRepository hikeRepository; + + HikeUseCase({required this.hikeRepository}); + + Future> updateBeaconLocation( + String beaconId, LatLng position) { + return hikeRepository.updateBeaconLocation(beaconId, position); + } + + Future> fetchBeaconDetails(String beaconId) { + return hikeRepository.fetchBeaconDetails(beaconId); + } + + Future> createLandMark( + String id, String title, String lat, String lon) { + return hikeRepository.createLandMark(id, title, lat, lon); + } + + Future> changeUserLocation(String id, LatLng latlng) { + return hikeRepository.changeUserLocation(id, latlng); + } + + Stream> beaconlocationsSubscription( + String beaconId) { + return hikeRepository.beaconLocationsSubscription(beaconId); + } + + Stream> joinleavebeaconSubscription( + String beaconId) { + return hikeRepository.joinLeaveBeaconSubscription(beaconId); + } + + Future> sos(String id) { + return hikeRepository.sos(id); + } +} diff --git a/lib/domain/usecase/home_usecase.dart b/lib/domain/usecase/home_usecase.dart new file mode 100644 index 00000000..359b9728 --- /dev/null +++ b/lib/domain/usecase/home_usecase.dart @@ -0,0 +1,35 @@ +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/domain/entities/group/group_entity.dart'; +import 'package:beacon/domain/entities/subscriptions/updated_group_entity/updated_group_entity.dart'; +import 'package:beacon/domain/repositories/home_repository.dart'; + +class HomeUseCase { + final HomeRepository homeRepository; + + HomeUseCase({required this.homeRepository}); + + Future>> groups(int page, int pageSize) { + return homeRepository.fetchGroups(page, pageSize); + } + + Future> group(String groupId) { + return homeRepository.fetchGroup(groupId); + } + + Future> createGroup(String title) { + return homeRepository.createGroup(title); + } + + Future> joinGroup(String shortCode) { + return homeRepository.joinGroup(shortCode); + } + + Stream> groupUpdateSubscription( + List groupIds) { + return homeRepository.groupUpdateSubscription(groupIds); + } + + Future> changeShortcode(String groupId) { + return homeRepository.changeShortcode(groupId); + } +} diff --git a/lib/enums/view_state.dart b/lib/enums/view_state.dart deleted file mode 100644 index 4c02c01b..00000000 --- a/lib/enums/view_state.dart +++ /dev/null @@ -1,5 +0,0 @@ -/// Represents the state of the view -enum ViewState { - idle, - busy, -} diff --git a/lib/locator.dart b/lib/locator.dart index bb539136..c58180bc 100644 --- a/lib/locator.dart +++ b/lib/locator.dart @@ -1,32 +1,102 @@ -import 'package:beacon/main.dart'; -import 'package:beacon/services/database_mutation_functions.dart'; -import 'package:beacon/services/graphql_config.dart'; -import 'package:beacon/services/navigation_service.dart'; -import 'package:beacon/services/user_config.dart'; -import 'package:beacon/view_model/auth_screen_model.dart'; -import 'package:beacon/view_model/hike_screen_model.dart'; -import 'package:beacon/view_model/home_view_model.dart'; +import 'package:beacon/core/services/location_services.dart'; +import 'package:beacon/core/services/shared_prefrence_service.dart'; +import 'package:beacon/core/utils/utils.dart'; +import 'package:beacon/data/datasource/local/local_api.dart'; +import 'package:beacon/data/datasource/remote/remote_auth_api.dart'; +import 'package:beacon/data/datasource/remote/remote_group_api.dart'; +import 'package:beacon/data/datasource/remote/remote_hike_api.dart'; +import 'package:beacon/data/datasource/remote/remote_home_api.dart'; +import 'package:beacon/data/repositories/auth_repository_implementation.dart'; +import 'package:beacon/data/repositories/group_repository_implementation.dart'; +import 'package:beacon/data/repositories/hike_repository_implementation.dart'; +import 'package:beacon/data/repositories/home_repository_implementation.dart'; +import 'package:beacon/domain/repositories/auth_repository.dart'; +import 'package:beacon/domain/repositories/group_repository.dart'; +import 'package:beacon/domain/repositories/hike_repository.dart'; +import 'package:beacon/domain/repositories/home_repository.dart'; +import 'package:beacon/domain/usecase/auth_usecase.dart'; +import 'package:beacon/domain/usecase/group_usecase.dart'; +import 'package:beacon/domain/usecase/hike_usecase.dart'; +import 'package:beacon/domain/usecase/home_usecase.dart'; +import 'package:beacon/presentation/auth/auth_cubit/auth_cubit.dart'; +import 'package:beacon/presentation/auth/verification_cubit/verification_cubit.dart'; +import 'package:beacon/presentation/group/cubit/group_cubit/group_cubit.dart'; +import 'package:beacon/presentation/hike/cubit/hike_cubit/hike_cubit.dart'; +import 'package:beacon/presentation/hike/cubit/location_cubit/location_cubit.dart'; +import 'package:beacon/presentation/hike/cubit/panel_cubit/panel_cubit.dart'; +import 'package:beacon/presentation/home/home_cubit/home_cubit.dart'; +import 'package:beacon/presentation/group/cubit/members_cubit/members_cubit.dart'; +import 'package:beacon/config/graphql_config.dart'; +import 'package:beacon/config/local_notification.dart'; +import 'package:beacon/config/router/router.dart'; import 'package:get_it/get_it.dart'; GetIt locator = GetIt.instance; -final userConfig = locator(); -final navigationService = locator(); -final databaseFunctions = locator(); -final graphqlConfig = locator(); - -void setupLocator() { - //services - locator.registerSingleton(NavigationService()); - - //userConfig - locator.registerSingleton(UserConfig()); - locator.registerSingleton(GraphQLConfig()); - - //databaseMutationFunction - locator.registerSingleton(DataBaseMutationFunctions()); - - locator.registerFactory(() => DemoViewModel()); - locator.registerFactory(() => AuthViewModel()); - locator.registerFactory(() => HomeViewModel()); - locator.registerFactory(() => HikeScreenViewModel()); +final GraphQLConfig graphqlConfig = locator(); +final LocalNotification localNotif = locator(); +final localApi = locator(); +final utils = locator(); +final locationService = locator(); +final appRouter = locator(); +final sp = locator(); +Future setupLocator() async { + // shared prefrence services + locator.registerSingleton(SharedPreferenceService()); + + locator.registerLazySingleton(() => LocationService()); + locator.registerSingleton(AppRouter()); + + locator.registerSingleton(GraphQLConfig()); + + //local Notification + locator.registerSingleton(LocalNotification()); + + // hive localDb + locator.registerSingleton(LocalApi()); + + final clientAuth = await graphqlConfig.authClient(); + final subscriptionClient = await graphqlConfig.graphQlClient(); + + // Remote Api + locator.registerSingleton( + RemoteAuthApi(clientAuth, graphqlConfig.clientToQuery())); + + locator.registerSingleton( + RemoteHomeApi(clientAuth, subscriptionClient)); + locator.registerSingleton(RemoteGroupApi(clientAuth)); + locator.registerSingleton( + RemoteHikeApi(clientAuth, subscriptionClient)); + + // registering auth reporitory of domain + locator.registerSingleton( + AuthRepositoryImplementation(remoteAuthApi: locator())); + locator.registerSingleton( + HomeRepostitoryImplementation(remoteHomeApi: locator())); + locator.registerSingleton( + GroupRepostioryImplementation(remoteGroupApi: locator())); + locator.registerSingleton( + HikeRepositoryImplementatioin(remoteHikeApi: locator())); + + // use case + locator.registerSingleton( + AuthUseCase(authRepository: locator())); + locator.registerSingleton( + HomeUseCase(homeRepository: locator())); + locator.registerSingleton( + GroupUseCase(locator())); + locator.registerSingleton( + HikeUseCase(hikeRepository: locator())); + + // registering utils + locator.registerSingleton(Utils()); + + // registering cubit class + locator.registerSingleton(AuthCubit(locator())); + locator.registerSingleton(VerificationCubit(locator())); + locator.registerSingleton(HomeCubit(locator())); + locator.registerSingleton(GroupCubit(locator())); + locator.registerSingleton(MembersCubit(locator())); + locator.registerSingleton(HikeCubit(locator())); + locator.registerSingleton(LocationCubit(locator())); + locator.registerSingleton(PanelCubit(locator())); } diff --git a/lib/main.dart b/lib/main.dart index 2a9cc567..a9f94439 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,63 +1,80 @@ +import 'package:beacon/config/enviornment_config.dart'; +import 'package:beacon/presentation/auth/auth_cubit/auth_cubit.dart'; +import 'package:beacon/presentation/auth/verification_cubit/verification_cubit.dart'; +import 'package:beacon/presentation/group/cubit/group_cubit/group_cubit.dart'; +import 'package:beacon/presentation/hike/cubit/hike_cubit/hike_cubit.dart'; +import 'package:beacon/presentation/hike/cubit/location_cubit/location_cubit.dart'; +import 'package:beacon/presentation/hike/cubit/panel_cubit/panel_cubit.dart'; +import 'package:beacon/presentation/home/home_cubit/home_cubit.dart'; +import 'package:beacon/presentation/group/cubit/members_cubit/members_cubit.dart'; import 'package:beacon/locator.dart'; -import 'package:beacon/models/beacon/beacon.dart'; -import 'package:beacon/models/landmarks/landmark.dart'; -import 'package:beacon/models/location/location.dart'; -import 'package:beacon/router.dart' as router; -import 'package:beacon/view_model/base_view_model.dart'; -import 'package:beacon/views/base_view.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:hive/hive.dart'; -import 'package:path_provider/path_provider.dart' as path_provider; - -import 'models/user/user_info.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:overlay_support/overlay_support.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); + // loading variables + await EnvironmentConfig.loadEnvVariables(); + + await setupLocator(); + await localApi.init(); + await localNotif.initialize(); + SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, ]); - final appDocumentDirectory = - await path_provider.getApplicationDocumentsDirectory(); - Hive - ..init(appDocumentDirectory.path) - ..registerAdapter(UserAdapter()) - ..registerAdapter(BeaconAdapter()) - ..registerAdapter(LocationAdapter()) - ..registerAdapter(LandmarkAdapter()); - await Hive.openBox('currentUser'); - await Hive.openBox('beacons'); - setupLocator(); - runApp(MaterialApp( - debugShowCheckedModeBanner: false, - title: 'Beacon', - navigatorKey: navigationService.navigatorKey, - theme: ThemeData(fontFamily: 'FuturaBold'), - initialRoute: '/', - onGenerateRoute: router.generateRoute, - )); + + runApp(MyApp()); } -class DemoPageView extends StatelessWidget { - const DemoPageView({@required Key key}) : super(key: key); +class MyApp extends StatelessWidget { + MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - return BaseView( - builder: (context, model, child) => Scaffold( - appBar: AppBar( - title: const Text('Demo Page'), - ), - body: Container( - child: Text(model.title), + return OverlaySupport( + child: ResponsiveSizer( + builder: (context, orientation, deviceType) => MultiBlocProvider( + providers: providers, + child: MaterialApp.router( + debugShowCheckedModeBanner: false, + title: 'Beacon', + theme: ThemeData(fontFamily: 'FuturaBold'), + routerConfig: appRouter.config(), + ), ), ), ); } -} -class DemoViewModel extends BaseModel { - final String _title = "Title from the viewMode GSoC branch"; - String get title => _title; + final List> providers = [ + BlocProvider( + create: (context) => locator(), + ), + BlocProvider( + create: (context) => locator(), + ), + BlocProvider( + create: (context) => locator(), + ), + BlocProvider( + create: (context) => locator(), + ), + BlocProvider( + create: (context) => locator(), + ), + BlocProvider( + create: (context) => locator(), + ), + BlocProvider( + create: (context) => locator(), + ), + BlocProvider( + create: (context) => locator(), + ) + ]; } diff --git a/lib/models/beacon/beacon.dart b/lib/models/beacon/beacon.dart deleted file mode 100644 index 25753039..00000000 --- a/lib/models/beacon/beacon.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'dart:ffi'; - -import 'package:beacon/models/landmarks/landmark.dart'; -import 'package:beacon/models/location/location.dart'; -import 'package:beacon/models/user/user_info.dart'; -import 'package:flutter/material.dart'; -import 'package:hive/hive.dart'; -part 'beacon.g.dart'; - -@HiveType(typeId: 3) -class Beacon extends HiveObject { - Beacon( - {this.id, - this.shortcode, - this.startsAt, - this.expiresAt, - this.title, - this.leader, - this.followers, - this.route, - this.landmarks, - this.location}); - - factory Beacon.fromJson(Map json) { - return Beacon( - id: json['_id'] as String, - shortcode: json['shortcode'] as String, - title: json['title'] != null ? json['title'] as String : null, - startsAt: json['startsAt'] as int, - expiresAt: json['expiresAt'] as int, - leader: json['leader'] != null - ? User.fromJson(json['leader'] as Map) - : null, - location: json['location'] != null - ? Location.fromJson(json['location'] as Map) - : null, - followers: json['followers'] != null - ? (json['followers'] as List) - .map((e) => User.fromJson(e as Map)) - .toList() - : [], - route: json['route'] != null - ? (json['route'] as List) - .map((e) => Location.fromJson(e as Map)) - .toList() - : [], - landmarks: json['landmarks'] != null - ? (json['landmarks'] as List) - .map((e) => Landmark.fromJson(e as Map)) - .toList() - : [], - ); - } - - @HiveField(0) - String id; - @HiveField(1) - String shortcode; - @HiveField(2) - int startsAt; - @HiveField(3) - int expiresAt; - @HiveField(4) - User leader; - @HiveField(5) - List followers = []; - @HiveField(6) - List route = []; - @HiveField(7) - String title; - @HiveField(8) - List landmarks = []; - @HiveField(9) - Location location; - - print() { - debugPrint('shortCode: ${this.shortcode}'); - debugPrint('_id: ${this.id}'); - debugPrint('startsAt: ${this.startsAt}'); - debugPrint('expiresAt: ${this.expiresAt}'); - } -} diff --git a/lib/models/beacon/beacon.g.dart b/lib/models/beacon/beacon.g.dart deleted file mode 100644 index 6e89b399..00000000 --- a/lib/models/beacon/beacon.g.dart +++ /dev/null @@ -1,68 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'beacon.dart'; - -// ************************************************************************** -// TypeAdapterGenerator -// ************************************************************************** - -class BeaconAdapter extends TypeAdapter { - @override - final int typeId = 3; - - @override - Beacon read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return Beacon( - id: fields[0] as String, - shortcode: fields[1] as String, - startsAt: fields[2] as int, - expiresAt: fields[3] as int, - title: fields[7] as String, - leader: fields[4] as User, - followers: (fields[5] as List)?.cast(), - route: (fields[6] as List)?.cast(), - landmarks: (fields[8] as List)?.cast(), - location: fields[9] as Location, - ); - } - - @override - void write(BinaryWriter writer, Beacon obj) { - writer - ..writeByte(10) - ..writeByte(0) - ..write(obj.id) - ..writeByte(1) - ..write(obj.shortcode) - ..writeByte(2) - ..write(obj.startsAt) - ..writeByte(3) - ..write(obj.expiresAt) - ..writeByte(4) - ..write(obj.leader) - ..writeByte(5) - ..write(obj.followers) - ..writeByte(6) - ..write(obj.route) - ..writeByte(7) - ..write(obj.title) - ..writeByte(8) - ..write(obj.landmarks) - ..writeByte(9) - ..write(obj.location); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is BeaconAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} diff --git a/lib/models/landmarks/landmark.dart b/lib/models/landmarks/landmark.dart deleted file mode 100644 index 53520a7a..00000000 --- a/lib/models/landmarks/landmark.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'dart:ffi'; - -import 'package:beacon/models/location/location.dart'; -import 'package:beacon/models/user/user_info.dart'; -import 'package:flutter/material.dart'; -import 'package:hive/hive.dart'; -part 'landmark.g.dart'; - -@HiveType(typeId: 4) -class Landmark extends HiveObject { - Landmark({this.title, this.location}); - - factory Landmark.fromJson(Map json) { - return Landmark( - title: json['title'] != null ? json['title'] as String : null, - location: json['location'] != null - ? Location.fromJson(json['location'] as Map) - : null, - ); - } - - @HiveField(0) - String title; - @HiveField(1) - Location location; - - print() { - debugPrint('title: ${this.title}'); - } -} diff --git a/lib/models/landmarks/landmark.g.dart b/lib/models/landmarks/landmark.g.dart deleted file mode 100644 index e7a8f403..00000000 --- a/lib/models/landmarks/landmark.g.dart +++ /dev/null @@ -1,44 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'landmark.dart'; - -// ************************************************************************** -// TypeAdapterGenerator -// ************************************************************************** - -class LandmarkAdapter extends TypeAdapter { - @override - final int typeId = 4; - - @override - Landmark read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return Landmark( - title: fields[0] as String, - location: fields[1] as Location, - ); - } - - @override - void write(BinaryWriter writer, Landmark obj) { - writer - ..writeByte(2) - ..writeByte(0) - ..write(obj.title) - ..writeByte(1) - ..write(obj.location); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is LandmarkAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} diff --git a/lib/models/location/location.dart b/lib/models/location/location.dart deleted file mode 100644 index afc59615..00000000 --- a/lib/models/location/location.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'dart:ffi'; - -import 'package:flutter/material.dart'; -import 'package:hive/hive.dart'; -part 'location.g.dart'; - -@HiveType(typeId: 2) -class Location extends HiveObject { - Location({this.lat, this.lon}); - - factory Location.fromJson(Map json) { - return Location( - lat: json['lat'] != null ? json['lat'] as String : null, - lon: json['lon'] != null ? json['lon'] as String : null, - ); - } - - @HiveField(0) - String lat; - @HiveField(1) - String lon; - - print() { - debugPrint('lat: ${this.lat}'); - debugPrint('long: ${this.lon}'); - } -} diff --git a/lib/models/location/location.g.dart b/lib/models/location/location.g.dart deleted file mode 100644 index bc8f0d2e..00000000 --- a/lib/models/location/location.g.dart +++ /dev/null @@ -1,44 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'location.dart'; - -// ************************************************************************** -// TypeAdapterGenerator -// ************************************************************************** - -class LocationAdapter extends TypeAdapter { - @override - final int typeId = 2; - - @override - Location read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return Location( - lat: fields[0] as String, - lon: fields[1] as String, - ); - } - - @override - void write(BinaryWriter writer, Location obj) { - writer - ..writeByte(2) - ..writeByte(0) - ..write(obj.lat) - ..writeByte(1) - ..write(obj.lon); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is LocationAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} diff --git a/lib/models/user/user_info.dart b/lib/models/user/user_info.dart deleted file mode 100644 index 40d37211..00000000 --- a/lib/models/user/user_info.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'dart:ffi'; - -import 'package:beacon/models/beacon/beacon.dart'; -import 'package:beacon/models/location/location.dart'; -import 'package:flutter/material.dart'; -import 'package:hive/hive.dart'; -part 'user_info.g.dart'; - -@HiveType(typeId: 1) -class User extends HiveObject { - User( - {this.authToken, - this.email, - this.name, - this.location, - this.beacon, - this.id, - this.isGuest}); - - factory User.fromJson(Map json) { - return User( - id: json['_id'] as String, - name: json['name'] != null ? json['name'] as String : 'Anonymous', - email: json['email'] != null ? json['email'] as String : '', - location: json['location'] != null - ? Location.fromJson(json['location'] as Map) - : null, - beacon: json['beacons'] != null - ? (json['beacons'] as List) - .map((e) => Beacon.fromJson(e as Map)) - .toList() - : [], - isGuest: json['isGuest'] != null ? json['isGuest'] as bool : false, - ); - } - - @HiveField(0) - String id; - @HiveField(1) - String authToken; - @HiveField(2) - String name; - @HiveField(3) - String email; - @HiveField(4) - List beacon = []; - @HiveField(5) - Location location; - @HiveField(6) - bool isGuest = false; - - print() { - debugPrint('authToken: ${this.authToken}'); - debugPrint('_id: ${this.id}'); - debugPrint('firstName: ${this.name}'); - debugPrint('email: ${this.email}'); - debugPrint('location: ${this.location}'); - debugPrint('beacons: ${this.beacon}'); - } - - // updateBeacon(List beaconList) { - // this.beacon = beaconList; - // } - - update(User details) { - this.authToken = details.authToken; - this.name = details.name; - this.email = details.email; - this.location = details.location; - this.beacon = details.beacon; - this.isGuest = details.isGuest; - } -} diff --git a/lib/models/user/user_info.g.dart b/lib/models/user/user_info.g.dart deleted file mode 100644 index 2ea20c20..00000000 --- a/lib/models/user/user_info.g.dart +++ /dev/null @@ -1,59 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'user_info.dart'; - -// ************************************************************************** -// TypeAdapterGenerator -// ************************************************************************** - -class UserAdapter extends TypeAdapter { - @override - final int typeId = 1; - - @override - User read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return User( - authToken: fields[1] as String, - email: fields[3] as String, - name: fields[2] as String, - location: fields[5] as Location, - beacon: (fields[4] as List)?.cast(), - id: fields[0] as String, - isGuest: fields[6] as bool, - ); - } - - @override - void write(BinaryWriter writer, User obj) { - writer - ..writeByte(7) - ..writeByte(0) - ..write(obj.id) - ..writeByte(1) - ..write(obj.authToken) - ..writeByte(2) - ..write(obj.name) - ..writeByte(3) - ..write(obj.email) - ..writeByte(4) - ..write(obj.beacon) - ..writeByte(5) - ..write(obj.location) - ..writeByte(6) - ..write(obj.isGuest); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is UserAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} diff --git a/lib/presentation/auth/auth_cubit/auth_cubit.dart b/lib/presentation/auth/auth_cubit/auth_cubit.dart new file mode 100644 index 00000000..8ed11a61 --- /dev/null +++ b/lib/presentation/auth/auth_cubit/auth_cubit.dart @@ -0,0 +1,97 @@ +import 'package:beacon/config/router/router.dart'; +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/domain/usecase/auth_usecase.dart'; +import 'package:beacon/presentation/auth/auth_cubit/auth_state.dart'; +import 'package:beacon/locator.dart'; +import 'package:beacon/presentation/auth/verification_cubit/verification_cubit.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:google_sign_in/google_sign_in.dart'; + +class AuthCubit extends Cubit { + static AuthCubit? _instance; + final AuthUseCase authUseCase; + AuthCubit._internal({required this.authUseCase}) : super(InitialAuthState()); + + factory AuthCubit(AuthUseCase authUseCase) { + return _instance ?? AuthCubit._internal(authUseCase: authUseCase); + } + + Future register( + String name, + String email, + String password, + ) async { + emit(AuthLoadingState()); + final dataState = await authUseCase.registerUseCase(name, email, password); + if (dataState is DataSuccess && dataState.data != null) { + if (dataState.data!.isVerified == false) { + // show verification screen + emit(AuthVerificationState()); + } else { + emit(SuccessState(message: "Welcome")); + } + } else { + emit(AuthErrorState(error: dataState.error!)); + } + } + + Future navigate() async { + await sp.deleteData('time'); + await sp.deleteData('otp'); + await locator().sendEmailVerification(); + appRouter.replace(VerificationScreenRoute()); + } + + Future login(String email, String password) async { + emit(AuthLoadingState()); + final dataState = await authUseCase.loginUserCase(email, password); + + if (dataState is DataSuccess && dataState.data != null) { + if (dataState.data!.isVerified == false) { + // show verification screen + emit(AuthVerificationState()); + } else { + emit(SuccessState()); + } + } else { + emit(AuthErrorState(error: dataState.error!)); + } + } + + void requestFocus(FocusNode focusNode, BuildContext context) { + FocusScope.of(context).requestFocus(focusNode); + } + + Future isGuest() async { + bool? isguest = await localApi.userModel.isGuest; + return isguest!; + } + + void googleSignIn() async { + const List scopes = [ + 'email', + 'https://www.googleapis.com/auth/contacts.readonly', + ]; + + GoogleSignIn _googleSignIn = GoogleSignIn( + scopes: scopes, + ); + + final gAuth = await _googleSignIn.signIn(); + + if (gAuth != null && gAuth.displayName != null) { + var dataState = + await authUseCase.oAuthUseCase(gAuth.displayName!, gAuth.email); + + if (dataState is DataSuccess && dataState.data != null) { + emit(SuccessState()); + } else { + emit(AuthErrorState(error: dataState.error!)); + } + } else { + emit(AuthErrorState( + error: 'Something went wrong please try again later!')); + } + } +} diff --git a/lib/presentation/auth/auth_cubit/auth_state.dart b/lib/presentation/auth/auth_cubit/auth_state.dart new file mode 100644 index 00000000..ec3591f6 --- /dev/null +++ b/lib/presentation/auth/auth_cubit/auth_state.dart @@ -0,0 +1,16 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'auth_state.freezed.dart'; + +@freezed +abstract class AuthState with _$AuthState { + const factory AuthState.initial() = InitialAuthState; + + const factory AuthState.loading() = AuthLoadingState; + + const factory AuthState.error({String? error}) = AuthErrorState; + + const factory AuthState.success({String? message}) = SuccessState; + + const factory AuthState.verify() = AuthVerificationState; +} diff --git a/lib/presentation/auth/auth_cubit/auth_state.freezed.dart b/lib/presentation/auth/auth_cubit/auth_state.freezed.dart new file mode 100644 index 00000000..8bda4037 --- /dev/null +++ b/lib/presentation/auth/auth_cubit/auth_state.freezed.dart @@ -0,0 +1,757 @@ +// 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 'auth_state.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 _$AuthState { + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(String? error) error, + required TResult Function(String? message) success, + required TResult Function() verify, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(String? error)? error, + TResult? Function(String? message)? success, + TResult? Function()? verify, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function(String? error)? error, + TResult Function(String? message)? success, + TResult Function()? verify, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(InitialAuthState value) initial, + required TResult Function(AuthLoadingState value) loading, + required TResult Function(AuthErrorState value) error, + required TResult Function(SuccessState value) success, + required TResult Function(AuthVerificationState value) verify, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialAuthState value)? initial, + TResult? Function(AuthLoadingState value)? loading, + TResult? Function(AuthErrorState value)? error, + TResult? Function(SuccessState value)? success, + TResult? Function(AuthVerificationState value)? verify, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialAuthState value)? initial, + TResult Function(AuthLoadingState value)? loading, + TResult Function(AuthErrorState value)? error, + TResult Function(SuccessState value)? success, + TResult Function(AuthVerificationState value)? verify, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $AuthStateCopyWith<$Res> { + factory $AuthStateCopyWith(AuthState value, $Res Function(AuthState) then) = + _$AuthStateCopyWithImpl<$Res, AuthState>; +} + +/// @nodoc +class _$AuthStateCopyWithImpl<$Res, $Val extends AuthState> + implements $AuthStateCopyWith<$Res> { + _$AuthStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; +} + +/// @nodoc +abstract class _$$InitialAuthStateImplCopyWith<$Res> { + factory _$$InitialAuthStateImplCopyWith(_$InitialAuthStateImpl value, + $Res Function(_$InitialAuthStateImpl) then) = + __$$InitialAuthStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$InitialAuthStateImplCopyWithImpl<$Res> + extends _$AuthStateCopyWithImpl<$Res, _$InitialAuthStateImpl> + implements _$$InitialAuthStateImplCopyWith<$Res> { + __$$InitialAuthStateImplCopyWithImpl(_$InitialAuthStateImpl _value, + $Res Function(_$InitialAuthStateImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$InitialAuthStateImpl implements InitialAuthState { + const _$InitialAuthStateImpl(); + + @override + String toString() { + return 'AuthState.initial()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$InitialAuthStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(String? error) error, + required TResult Function(String? message) success, + required TResult Function() verify, + }) { + return initial(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(String? error)? error, + TResult? Function(String? message)? success, + TResult? Function()? verify, + }) { + return initial?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function(String? error)? error, + TResult Function(String? message)? success, + TResult Function()? verify, + required TResult orElse(), + }) { + if (initial != null) { + return initial(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialAuthState value) initial, + required TResult Function(AuthLoadingState value) loading, + required TResult Function(AuthErrorState value) error, + required TResult Function(SuccessState value) success, + required TResult Function(AuthVerificationState value) verify, + }) { + return initial(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialAuthState value)? initial, + TResult? Function(AuthLoadingState value)? loading, + TResult? Function(AuthErrorState value)? error, + TResult? Function(SuccessState value)? success, + TResult? Function(AuthVerificationState value)? verify, + }) { + return initial?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialAuthState value)? initial, + TResult Function(AuthLoadingState value)? loading, + TResult Function(AuthErrorState value)? error, + TResult Function(SuccessState value)? success, + TResult Function(AuthVerificationState value)? verify, + required TResult orElse(), + }) { + if (initial != null) { + return initial(this); + } + return orElse(); + } +} + +abstract class InitialAuthState implements AuthState { + const factory InitialAuthState() = _$InitialAuthStateImpl; +} + +/// @nodoc +abstract class _$$AuthLoadingStateImplCopyWith<$Res> { + factory _$$AuthLoadingStateImplCopyWith(_$AuthLoadingStateImpl value, + $Res Function(_$AuthLoadingStateImpl) then) = + __$$AuthLoadingStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$AuthLoadingStateImplCopyWithImpl<$Res> + extends _$AuthStateCopyWithImpl<$Res, _$AuthLoadingStateImpl> + implements _$$AuthLoadingStateImplCopyWith<$Res> { + __$$AuthLoadingStateImplCopyWithImpl(_$AuthLoadingStateImpl _value, + $Res Function(_$AuthLoadingStateImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$AuthLoadingStateImpl implements AuthLoadingState { + const _$AuthLoadingStateImpl(); + + @override + String toString() { + return 'AuthState.loading()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$AuthLoadingStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(String? error) error, + required TResult Function(String? message) success, + required TResult Function() verify, + }) { + return loading(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(String? error)? error, + TResult? Function(String? message)? success, + TResult? Function()? verify, + }) { + return loading?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function(String? error)? error, + TResult Function(String? message)? success, + TResult Function()? verify, + required TResult orElse(), + }) { + if (loading != null) { + return loading(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialAuthState value) initial, + required TResult Function(AuthLoadingState value) loading, + required TResult Function(AuthErrorState value) error, + required TResult Function(SuccessState value) success, + required TResult Function(AuthVerificationState value) verify, + }) { + return loading(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialAuthState value)? initial, + TResult? Function(AuthLoadingState value)? loading, + TResult? Function(AuthErrorState value)? error, + TResult? Function(SuccessState value)? success, + TResult? Function(AuthVerificationState value)? verify, + }) { + return loading?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialAuthState value)? initial, + TResult Function(AuthLoadingState value)? loading, + TResult Function(AuthErrorState value)? error, + TResult Function(SuccessState value)? success, + TResult Function(AuthVerificationState value)? verify, + required TResult orElse(), + }) { + if (loading != null) { + return loading(this); + } + return orElse(); + } +} + +abstract class AuthLoadingState implements AuthState { + const factory AuthLoadingState() = _$AuthLoadingStateImpl; +} + +/// @nodoc +abstract class _$$AuthErrorStateImplCopyWith<$Res> { + factory _$$AuthErrorStateImplCopyWith(_$AuthErrorStateImpl value, + $Res Function(_$AuthErrorStateImpl) then) = + __$$AuthErrorStateImplCopyWithImpl<$Res>; + @useResult + $Res call({String? error}); +} + +/// @nodoc +class __$$AuthErrorStateImplCopyWithImpl<$Res> + extends _$AuthStateCopyWithImpl<$Res, _$AuthErrorStateImpl> + implements _$$AuthErrorStateImplCopyWith<$Res> { + __$$AuthErrorStateImplCopyWithImpl( + _$AuthErrorStateImpl _value, $Res Function(_$AuthErrorStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? error = freezed, + }) { + return _then(_$AuthErrorStateImpl( + error: freezed == error + ? _value.error + : error // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc + +class _$AuthErrorStateImpl implements AuthErrorState { + const _$AuthErrorStateImpl({this.error}); + + @override + final String? error; + + @override + String toString() { + return 'AuthState.error(error: $error)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$AuthErrorStateImpl && + (identical(other.error, error) || other.error == error)); + } + + @override + int get hashCode => Object.hash(runtimeType, error); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$AuthErrorStateImplCopyWith<_$AuthErrorStateImpl> get copyWith => + __$$AuthErrorStateImplCopyWithImpl<_$AuthErrorStateImpl>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(String? error) error, + required TResult Function(String? message) success, + required TResult Function() verify, + }) { + return error(this.error); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(String? error)? error, + TResult? Function(String? message)? success, + TResult? Function()? verify, + }) { + return error?.call(this.error); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function(String? error)? error, + TResult Function(String? message)? success, + TResult Function()? verify, + required TResult orElse(), + }) { + if (error != null) { + return error(this.error); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialAuthState value) initial, + required TResult Function(AuthLoadingState value) loading, + required TResult Function(AuthErrorState value) error, + required TResult Function(SuccessState value) success, + required TResult Function(AuthVerificationState value) verify, + }) { + return error(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialAuthState value)? initial, + TResult? Function(AuthLoadingState value)? loading, + TResult? Function(AuthErrorState value)? error, + TResult? Function(SuccessState value)? success, + TResult? Function(AuthVerificationState value)? verify, + }) { + return error?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialAuthState value)? initial, + TResult Function(AuthLoadingState value)? loading, + TResult Function(AuthErrorState value)? error, + TResult Function(SuccessState value)? success, + TResult Function(AuthVerificationState value)? verify, + required TResult orElse(), + }) { + if (error != null) { + return error(this); + } + return orElse(); + } +} + +abstract class AuthErrorState implements AuthState { + const factory AuthErrorState({final String? error}) = _$AuthErrorStateImpl; + + String? get error; + @JsonKey(ignore: true) + _$$AuthErrorStateImplCopyWith<_$AuthErrorStateImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$SuccessStateImplCopyWith<$Res> { + factory _$$SuccessStateImplCopyWith( + _$SuccessStateImpl value, $Res Function(_$SuccessStateImpl) then) = + __$$SuccessStateImplCopyWithImpl<$Res>; + @useResult + $Res call({String? message}); +} + +/// @nodoc +class __$$SuccessStateImplCopyWithImpl<$Res> + extends _$AuthStateCopyWithImpl<$Res, _$SuccessStateImpl> + implements _$$SuccessStateImplCopyWith<$Res> { + __$$SuccessStateImplCopyWithImpl( + _$SuccessStateImpl _value, $Res Function(_$SuccessStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? message = freezed, + }) { + return _then(_$SuccessStateImpl( + message: freezed == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc + +class _$SuccessStateImpl implements SuccessState { + const _$SuccessStateImpl({this.message}); + + @override + final String? message; + + @override + String toString() { + return 'AuthState.success(message: $message)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SuccessStateImpl && + (identical(other.message, message) || other.message == message)); + } + + @override + int get hashCode => Object.hash(runtimeType, message); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$SuccessStateImplCopyWith<_$SuccessStateImpl> get copyWith => + __$$SuccessStateImplCopyWithImpl<_$SuccessStateImpl>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(String? error) error, + required TResult Function(String? message) success, + required TResult Function() verify, + }) { + return success(message); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(String? error)? error, + TResult? Function(String? message)? success, + TResult? Function()? verify, + }) { + return success?.call(message); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function(String? error)? error, + TResult Function(String? message)? success, + TResult Function()? verify, + required TResult orElse(), + }) { + if (success != null) { + return success(message); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialAuthState value) initial, + required TResult Function(AuthLoadingState value) loading, + required TResult Function(AuthErrorState value) error, + required TResult Function(SuccessState value) success, + required TResult Function(AuthVerificationState value) verify, + }) { + return success(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialAuthState value)? initial, + TResult? Function(AuthLoadingState value)? loading, + TResult? Function(AuthErrorState value)? error, + TResult? Function(SuccessState value)? success, + TResult? Function(AuthVerificationState value)? verify, + }) { + return success?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialAuthState value)? initial, + TResult Function(AuthLoadingState value)? loading, + TResult Function(AuthErrorState value)? error, + TResult Function(SuccessState value)? success, + TResult Function(AuthVerificationState value)? verify, + required TResult orElse(), + }) { + if (success != null) { + return success(this); + } + return orElse(); + } +} + +abstract class SuccessState implements AuthState { + const factory SuccessState({final String? message}) = _$SuccessStateImpl; + + String? get message; + @JsonKey(ignore: true) + _$$SuccessStateImplCopyWith<_$SuccessStateImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$AuthVerificationStateImplCopyWith<$Res> { + factory _$$AuthVerificationStateImplCopyWith( + _$AuthVerificationStateImpl value, + $Res Function(_$AuthVerificationStateImpl) then) = + __$$AuthVerificationStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$AuthVerificationStateImplCopyWithImpl<$Res> + extends _$AuthStateCopyWithImpl<$Res, _$AuthVerificationStateImpl> + implements _$$AuthVerificationStateImplCopyWith<$Res> { + __$$AuthVerificationStateImplCopyWithImpl(_$AuthVerificationStateImpl _value, + $Res Function(_$AuthVerificationStateImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$AuthVerificationStateImpl implements AuthVerificationState { + const _$AuthVerificationStateImpl(); + + @override + String toString() { + return 'AuthState.verify()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$AuthVerificationStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(String? error) error, + required TResult Function(String? message) success, + required TResult Function() verify, + }) { + return verify(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(String? error)? error, + TResult? Function(String? message)? success, + TResult? Function()? verify, + }) { + return verify?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function(String? error)? error, + TResult Function(String? message)? success, + TResult Function()? verify, + required TResult orElse(), + }) { + if (verify != null) { + return verify(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialAuthState value) initial, + required TResult Function(AuthLoadingState value) loading, + required TResult Function(AuthErrorState value) error, + required TResult Function(SuccessState value) success, + required TResult Function(AuthVerificationState value) verify, + }) { + return verify(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialAuthState value)? initial, + TResult? Function(AuthLoadingState value)? loading, + TResult? Function(AuthErrorState value)? error, + TResult? Function(SuccessState value)? success, + TResult? Function(AuthVerificationState value)? verify, + }) { + return verify?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialAuthState value)? initial, + TResult Function(AuthLoadingState value)? loading, + TResult Function(AuthErrorState value)? error, + TResult Function(SuccessState value)? success, + TResult Function(AuthVerificationState value)? verify, + required TResult orElse(), + }) { + if (verify != null) { + return verify(this); + } + return orElse(); + } +} + +abstract class AuthVerificationState implements AuthState { + const factory AuthVerificationState() = _$AuthVerificationStateImpl; +} diff --git a/lib/presentation/auth/auth_screen.dart b/lib/presentation/auth/auth_screen.dart new file mode 100644 index 00000000..1cd86592 --- /dev/null +++ b/lib/presentation/auth/auth_screen.dart @@ -0,0 +1,478 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:beacon/presentation/auth/auth_cubit/auth_cubit.dart'; +import 'package:beacon/presentation/auth/auth_cubit/auth_state.dart'; +import 'package:beacon/presentation/widgets/text_field.dart'; +import 'package:beacon/locator.dart'; +import 'package:beacon/presentation/widgets/hike_button.dart'; +import 'package:beacon/presentation/widgets/loading_screen.dart'; +import 'package:beacon/presentation/widgets/shape_painter.dart'; +import 'package:beacon/core/utils/validators.dart'; +import 'package:beacon/core/utils/constants.dart'; +import 'package:beacon/presentation/widgets/indication_painter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; + +@RoutePage() +class AuthScreen extends StatefulWidget { + const AuthScreen({super.key}); + + @override + _AuthScreenState createState() => _AuthScreenState(); +} + +class _AuthScreenState extends State + with SingleTickerProviderStateMixin { + Future onPopHome() async { + return showDialog( + context: context, + builder: (context) => AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + contentPadding: EdgeInsets.all(25.0), + title: Text( + 'Confirm Exit', + style: TextStyle(fontSize: 25, color: kYellow), + ), + content: Text( + 'Do you really want to exit?', + style: TextStyle(fontSize: 18, color: kBlack), + ), + actions: [ + HikeButton( + buttonHeight: 2.5.h, + buttonWidth: 8.w, + onTap: () => appRouter.maybePop(false), + text: 'No', + ), + HikeButton( + buttonHeight: 2.5.h, + buttonWidth: 8.w, + onTap: () => appRouter.maybePop(true), + text: 'Yes', + ), + ], + ), + ); + } + + PageController _pageController = PageController(); + + Color leftColor = Colors.white; + Color rightColor = Colors.black; + + @override + Widget build(BuildContext context) { + Size screensize = MediaQuery.of(context).size; + final authCubit = BlocProvider.of(context); + return PopScope( + canPop: false, + onPopInvoked: (didPop) async { + bool? popped = await onPopHome(); + + if (popped == true) { + await SystemNavigator.pop(); + } + }, + child: BlocConsumer( + listener: (context, state) { + if (state is SuccessState) { + appRouter.replaceNamed('/home'); + state.message != null + ? utils.showSnackBar(state.message!, context) + : null; + } else if (state is AuthVerificationState) { + context.read().navigate(); + } else if (state is AuthErrorState) { + utils.showSnackBar(state.error!, context, + duration: Duration(seconds: 2)); + } + }, + builder: (context, state) { + return state is AuthLoadingState + ? LoadingScreen() + : Scaffold( + resizeToAvoidBottomInset: true, + body: Container( + width: screensize.width, + height: screensize.height >= 775.0 + ? screensize.height + : 775.0, + child: Stack( + children: [ + CustomPaint( + size: Size(screensize.width, screensize.height), + painter: ShapePainter(), + ), + Container( + alignment: Alignment.center, + padding: + EdgeInsets.only(top: screensize.height / 3.5), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsets.only(top: 20.0), + child: + _buildMenuBar(context, _pageController), + ), + Expanded( + flex: 2, + child: PageView( + controller: _pageController, + onPageChanged: (i) { + if (i == 0) { + setState(() { + rightColor = Colors.black; + leftColor = Colors.white; + }); + Future.delayed( + Duration(milliseconds: 500), () { + authCubit.requestFocus( + loginEmailFocus, context); + }); + } else if (i == 1) { + setState(() { + rightColor = Colors.white; + leftColor = Colors.black; + }); + Future.delayed( + Duration(milliseconds: 500), () { + authCubit.requestFocus( + signUpNameFocus, context); + }); + } + }, + children: [ + new ConstrainedBox( + constraints: + const BoxConstraints.expand(), + child: _buildSignIn(context), + ), + new ConstrainedBox( + constraints: + const BoxConstraints.expand(), + child: _buildSignUp( + context, + ), + ), + ], + ), + ), + ], + ), + ), + ], + ), + ), + ); + }, + )); + } + + Widget _buildMenuBar(BuildContext context, PageController pageController) { + Size screensize = MediaQuery.of(context).size; + return Container( + padding: EdgeInsets.symmetric(horizontal: 13.5.w), + width: screensize.width, + height: screensize.height < 800 ? 7.5.h : 6.h, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(25.0)), + ), + child: CustomPaint( + painter: TabIndicationPainter(pageController: pageController), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + child: TextButton( + style: ButtonStyle( + overlayColor: WidgetStateProperty.all(Colors.transparent), + ), + //highlightColor: Colors.white, + onPressed: () { + pageController.animateToPage(0, + duration: Duration(milliseconds: 500), + curve: Curves.decelerate); + leftColor = Colors.white; + rightColor = Colors.black; + setState(() {}); + }, + child: Text( + "Existing", + style: TextStyle( + color: leftColor, + fontSize: 18.0, + ), + ), + ), + ), + Expanded( + child: TextButton( + style: ButtonStyle( + overlayColor: WidgetStateProperty.all(Colors.transparent), + ), + onPressed: () { + pageController.animateToPage(1, + duration: Duration(milliseconds: 500), + curve: Curves.decelerate); + rightColor = Colors.white; + leftColor = Colors.black; + setState(() {}); + }, + child: Text( + "New", + style: TextStyle( + color: rightColor, + fontSize: 18.0, + ), + ), + ), + ), + ], + ), + ), + ); + } + + GlobalKey _signInFormKey = GlobalKey(); + GlobalKey _registerFormKey = GlobalKey(); + final loginEmailController = TextEditingController(); + final loginPasswordController = TextEditingController(); + + final signUpNameController = TextEditingController(); + final signUpEmailController = TextEditingController(); + final signUpPasswordController = TextEditingController(); + + final signUpNameFocus = FocusNode(); + final signUpEmailFocus = FocusNode(); + final signUpPasswordFocus = FocusNode(); + + final loginEmailFocus = FocusNode(); + final loginPasswordFocus = FocusNode(); + Widget _buildSignIn(BuildContext context) { + Size screensize = MediaQuery.of(context).size; + + final authCubit = BlocProvider.of(context); + return SingleChildScrollView( + child: Container( + padding: EdgeInsets.only(top: 3.h, left: 8.5.w, right: 8.5.w), + width: screensize.width, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Card( + elevation: 2.0, + color: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + child: Form( + key: _signInFormKey, + child: Container( + width: screensize.width - 70, + child: Column( + children: [ + Container( + height: 13.h, + padding: EdgeInsets.symmetric( + horizontal: 10, vertical: 10.0), + child: CustomTextField( + iconData: Icons.email, + hintText: 'Email Address', + controller: loginEmailController, + focusNode: loginEmailFocus, + nextFocusNode: loginPasswordFocus, + validator: Validator.validateEmail, + ), + ), + separator(), + Container( + height: 13.h, + padding: EdgeInsets.symmetric( + horizontal: 10, vertical: 10.0), + child: CustomTextField( + iconData: Icons.lock, + hintText: 'Password', + controller: loginPasswordController, + focusNode: loginPasswordFocus, + showTrailing: true, + validator: Validator.validatePassword), + ), + ], + ), + ), + ), + ), + SizedBox( + height: 3.5.h, + ), + BlocBuilder( + builder: (context, state) { + return HikeButton( + onTap: () { + if (_signInFormKey.currentState!.validate()) { + authCubit.login( + loginEmailController.text.trim(), + loginPasswordController.text.trim(), + ); + } else { + utils.showSnackBar( + 'Please complete all the fields', context); + } + }, + text: 'LOGIN', + ); + }, + ), + Padding( + padding: EdgeInsets.only( + left: 15.0, right: 15.0, top: 15.0, bottom: 15.0), + child: Text( + "Or", + style: TextStyle( + color: Colors.black, + fontSize: 16.0, + ), + ), + ), + HikeButton( + onTap: () { + context.read().googleSignIn(); + }, + text: '', + widget: Container( + width: 110, + child: Row( + children: [ + Image( + image: AssetImage( + 'images/google.png', + ), + height: 30, + ), + Spacer(), + Text( + 'Sign In', + style: TextStyle(fontSize: 17, color: Colors.white), + ) + ], + ), + ), + buttonColor: kYellow, + ), + ], + ), + ), + ); + } + + Widget _buildSignUp(BuildContext context) { + final authCubit = BlocProvider.of(context); + Size screensize = MediaQuery.of(context).size; + return SingleChildScrollView( + child: Container( + padding: EdgeInsets.only(top: 3.h, left: 8.5.w, right: 8.5.w), + child: Column( + children: [ + Card( + elevation: 2.0, + color: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + child: Form( + key: _registerFormKey, + child: Container( + width: screensize.width - 70, + // height: 280.0, + child: Column(children: [ + Container( + height: 13.h, + padding: EdgeInsets.symmetric( + horizontal: 10, vertical: 10.0), + child: CustomTextField( + iconData: Icons.person_2_sharp, + hintText: 'Name', + controller: signUpNameController, + focusNode: signUpNameFocus, + nextFocusNode: signUpEmailFocus, + validator: Validator.validateName, + ), + ), + separator(), + Container( + height: 13.h, + padding: EdgeInsets.symmetric( + horizontal: 10, vertical: 10.0), + child: CustomTextField( + iconData: Icons.mail, + hintText: 'Email Address', + controller: signUpEmailController, + focusNode: signUpEmailFocus, + nextFocusNode: signUpPasswordFocus, + validator: Validator.validateEmail, + ), + ), + separator(), + Container( + height: 13.h, + padding: EdgeInsets.symmetric( + horizontal: 10, vertical: 10.0), + child: CustomTextField( + iconData: Icons.lock, + hintText: 'Password', + controller: signUpPasswordController, + focusNode: signUpPasswordFocus, + showTrailing: true, + validator: Validator.validatePassword), + ), + ])), + ), + ), + SizedBox( + height: 3.5.h, + ), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(5.0)), + ), + child: BlocBuilder( + builder: (context, state) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(5.0)), + ), + child: HikeButton( + onTap: () { + if (_registerFormKey.currentState!.validate()) { + authCubit.register( + signUpNameController.text.trim(), + signUpEmailController.text.trim(), + signUpPasswordController.text.trim()); + } else { + utils.showSnackBar( + 'Please complete all the fields', context); + } + }, + text: 'SIGN UP', + ), + ); + }, + )) + ], + ), + ), + ); + } + + Widget separator() { + return Container( + width: 62.w, + height: 0.2.h, + color: Colors.grey[400], + ); + } +} diff --git a/lib/presentation/auth/verfication_screen.dart b/lib/presentation/auth/verfication_screen.dart new file mode 100644 index 00000000..cf48f241 --- /dev/null +++ b/lib/presentation/auth/verfication_screen.dart @@ -0,0 +1,102 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:beacon/config/router/router.dart'; +import 'package:beacon/core/utils/constants.dart'; +import 'package:beacon/locator.dart'; +import 'package:beacon/presentation/auth/verification_cubit/verification_cubit.dart'; +import 'package:beacon/presentation/auth/verification_cubit/verification_state.dart'; +import 'package:beacon/presentation/widgets/hike_button.dart'; +import 'package:beacon/presentation/widgets/shape_painter.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:gap/gap.dart'; +import 'package:pinput/pinput.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; + +@RoutePage() +class VerificationScreen extends StatefulWidget { + const VerificationScreen({super.key}); + + @override + State createState() => _VerificationScreenState(); +} + +class _VerificationScreenState extends State { + @override + void initState() { + super.initState(); + } + + TextEditingController _controller = TextEditingController(); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + width: 100.w, + height: 100.h >= 775.0 ? 100.h : 775.0, + child: Stack( + children: [ + CustomPaint( + size: Size(100.w, 100.h), + painter: ShapePainter(), + ), + Align( + alignment: Alignment(-0.9, -0.9), + child: FloatingActionButton( + backgroundColor: kYellow, + onPressed: () async { + await sp.deleteData('time'); + await sp.deleteData('otp'); + appRouter.replace(AuthScreenRoute()); + }, + child: Icon(CupertinoIcons.back)), + ), + Container( + width: 100.w, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + 'A verification code is sent to your email', + style: TextStyle(color: kBlack, fontSize: 16), + ), + Gap(10), + Pinput( + controller: _controller, + onCompleted: (pin) => print(pin), + ), + Gap(20), + BlocBuilder( + builder: (context, state) { + return HikeButton( + onTap: state is OTPSentState + ? () async { + if (_controller.text == state.otp) { + await locator() + .completeVerification(); + if (state is OTPVerifiedState) { + appRouter.push(VerificationScreenRoute()); + } + } else { + utils.showSnackBar( + 'Please enter valid otp', context); + } + } + : null, + buttonColor: + state is OTPSendingState ? kBlack : kYellow, + text: ' Verify ', + ); + }, + ), + ], + ), + ) + ], + ), + ), + ); + } +} diff --git a/lib/presentation/auth/verification_cubit/verification_cubit.dart b/lib/presentation/auth/verification_cubit/verification_cubit.dart new file mode 100644 index 00000000..08d1b976 --- /dev/null +++ b/lib/presentation/auth/verification_cubit/verification_cubit.dart @@ -0,0 +1,46 @@ +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/domain/usecase/auth_usecase.dart'; +import 'package:beacon/locator.dart'; +import 'package:beacon/presentation/auth/verification_cubit/verification_state.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class VerificationCubit extends Cubit { + AuthUseCase _authUseCase; + VerificationCubit(this._authUseCase) : super(InitialOTPState()); + + emitVerificationSentstate(String otp) { + emit(OTPSentState(otp: otp)); + } + + _clear() async { + await sp.deleteData('time'); + await sp.deleteData('otp'); + } + + Future sendEmailVerification() async { + emit(OTPSendingState()); + final dataState = await _authUseCase.sendVerificationCode(); + + if (dataState is DataSuccess && dataState.data != null) { + await sp.init(); + await sp.saveData('time', DateTime.now().toIso8601String()); + await sp.saveData('otp', dataState.data!); + emit(OTPSentState(otp: dataState.data)); + } else { + emit(OTPFailureState()); + } + } + + Future completeVerification() async { + emit(OTPVerifyingState()); + + final dataState = await _authUseCase.completeVerification(); + + if (dataState is DataSuccess && dataState.data != null) { + _clear(); + appRouter.replaceNamed('/home'); + } else if (dataState is DataFailed) { + emit(OTPFailureState()); + } + } +} diff --git a/lib/presentation/auth/verification_cubit/verification_state.dart b/lib/presentation/auth/verification_cubit/verification_state.dart new file mode 100644 index 00000000..18c1ef98 --- /dev/null +++ b/lib/presentation/auth/verification_cubit/verification_state.dart @@ -0,0 +1,13 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'verification_state.freezed.dart'; + +@freezed +class OTPVerificationState with _$OTPVerificationState { + factory OTPVerificationState.initial() = InitialOTPState; + factory OTPVerificationState.otpSending() = OTPSendingState; + factory OTPVerificationState.otpSent({String? otp}) = OTPSentState; + factory OTPVerificationState.otpVerifying() = OTPVerifyingState; + factory OTPVerificationState.otpVerified() = OTPVerifiedState; + factory OTPVerificationState.failure() = OTPFailureState; +} diff --git a/lib/presentation/auth/verification_cubit/verification_state.freezed.dart b/lib/presentation/auth/verification_cubit/verification_state.freezed.dart new file mode 100644 index 00000000..688d823a --- /dev/null +++ b/lib/presentation/auth/verification_cubit/verification_state.freezed.dart @@ -0,0 +1,887 @@ +// 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 'verification_state.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 _$OTPVerificationState { + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() otpSending, + required TResult Function(String? otp) otpSent, + required TResult Function() otpVerifying, + required TResult Function() otpVerified, + required TResult Function() failure, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? otpSending, + TResult? Function(String? otp)? otpSent, + TResult? Function()? otpVerifying, + TResult? Function()? otpVerified, + TResult? Function()? failure, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? otpSending, + TResult Function(String? otp)? otpSent, + TResult Function()? otpVerifying, + TResult Function()? otpVerified, + TResult Function()? failure, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(InitialOTPState value) initial, + required TResult Function(OTPSendingState value) otpSending, + required TResult Function(OTPSentState value) otpSent, + required TResult Function(OTPVerifyingState value) otpVerifying, + required TResult Function(OTPVerifiedState value) otpVerified, + required TResult Function(OTPFailureState value) failure, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialOTPState value)? initial, + TResult? Function(OTPSendingState value)? otpSending, + TResult? Function(OTPSentState value)? otpSent, + TResult? Function(OTPVerifyingState value)? otpVerifying, + TResult? Function(OTPVerifiedState value)? otpVerified, + TResult? Function(OTPFailureState value)? failure, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialOTPState value)? initial, + TResult Function(OTPSendingState value)? otpSending, + TResult Function(OTPSentState value)? otpSent, + TResult Function(OTPVerifyingState value)? otpVerifying, + TResult Function(OTPVerifiedState value)? otpVerified, + TResult Function(OTPFailureState value)? failure, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $OTPVerificationStateCopyWith<$Res> { + factory $OTPVerificationStateCopyWith(OTPVerificationState value, + $Res Function(OTPVerificationState) then) = + _$OTPVerificationStateCopyWithImpl<$Res, OTPVerificationState>; +} + +/// @nodoc +class _$OTPVerificationStateCopyWithImpl<$Res, + $Val extends OTPVerificationState> + implements $OTPVerificationStateCopyWith<$Res> { + _$OTPVerificationStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; +} + +/// @nodoc +abstract class _$$InitialOTPStateImplCopyWith<$Res> { + factory _$$InitialOTPStateImplCopyWith(_$InitialOTPStateImpl value, + $Res Function(_$InitialOTPStateImpl) then) = + __$$InitialOTPStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$InitialOTPStateImplCopyWithImpl<$Res> + extends _$OTPVerificationStateCopyWithImpl<$Res, _$InitialOTPStateImpl> + implements _$$InitialOTPStateImplCopyWith<$Res> { + __$$InitialOTPStateImplCopyWithImpl( + _$InitialOTPStateImpl _value, $Res Function(_$InitialOTPStateImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$InitialOTPStateImpl implements InitialOTPState { + _$InitialOTPStateImpl(); + + @override + String toString() { + return 'OTPVerificationState.initial()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$InitialOTPStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() otpSending, + required TResult Function(String? otp) otpSent, + required TResult Function() otpVerifying, + required TResult Function() otpVerified, + required TResult Function() failure, + }) { + return initial(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? otpSending, + TResult? Function(String? otp)? otpSent, + TResult? Function()? otpVerifying, + TResult? Function()? otpVerified, + TResult? Function()? failure, + }) { + return initial?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? otpSending, + TResult Function(String? otp)? otpSent, + TResult Function()? otpVerifying, + TResult Function()? otpVerified, + TResult Function()? failure, + required TResult orElse(), + }) { + if (initial != null) { + return initial(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialOTPState value) initial, + required TResult Function(OTPSendingState value) otpSending, + required TResult Function(OTPSentState value) otpSent, + required TResult Function(OTPVerifyingState value) otpVerifying, + required TResult Function(OTPVerifiedState value) otpVerified, + required TResult Function(OTPFailureState value) failure, + }) { + return initial(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialOTPState value)? initial, + TResult? Function(OTPSendingState value)? otpSending, + TResult? Function(OTPSentState value)? otpSent, + TResult? Function(OTPVerifyingState value)? otpVerifying, + TResult? Function(OTPVerifiedState value)? otpVerified, + TResult? Function(OTPFailureState value)? failure, + }) { + return initial?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialOTPState value)? initial, + TResult Function(OTPSendingState value)? otpSending, + TResult Function(OTPSentState value)? otpSent, + TResult Function(OTPVerifyingState value)? otpVerifying, + TResult Function(OTPVerifiedState value)? otpVerified, + TResult Function(OTPFailureState value)? failure, + required TResult orElse(), + }) { + if (initial != null) { + return initial(this); + } + return orElse(); + } +} + +abstract class InitialOTPState implements OTPVerificationState { + factory InitialOTPState() = _$InitialOTPStateImpl; +} + +/// @nodoc +abstract class _$$OTPSendingStateImplCopyWith<$Res> { + factory _$$OTPSendingStateImplCopyWith(_$OTPSendingStateImpl value, + $Res Function(_$OTPSendingStateImpl) then) = + __$$OTPSendingStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$OTPSendingStateImplCopyWithImpl<$Res> + extends _$OTPVerificationStateCopyWithImpl<$Res, _$OTPSendingStateImpl> + implements _$$OTPSendingStateImplCopyWith<$Res> { + __$$OTPSendingStateImplCopyWithImpl( + _$OTPSendingStateImpl _value, $Res Function(_$OTPSendingStateImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$OTPSendingStateImpl implements OTPSendingState { + _$OTPSendingStateImpl(); + + @override + String toString() { + return 'OTPVerificationState.otpSending()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$OTPSendingStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() otpSending, + required TResult Function(String? otp) otpSent, + required TResult Function() otpVerifying, + required TResult Function() otpVerified, + required TResult Function() failure, + }) { + return otpSending(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? otpSending, + TResult? Function(String? otp)? otpSent, + TResult? Function()? otpVerifying, + TResult? Function()? otpVerified, + TResult? Function()? failure, + }) { + return otpSending?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? otpSending, + TResult Function(String? otp)? otpSent, + TResult Function()? otpVerifying, + TResult Function()? otpVerified, + TResult Function()? failure, + required TResult orElse(), + }) { + if (otpSending != null) { + return otpSending(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialOTPState value) initial, + required TResult Function(OTPSendingState value) otpSending, + required TResult Function(OTPSentState value) otpSent, + required TResult Function(OTPVerifyingState value) otpVerifying, + required TResult Function(OTPVerifiedState value) otpVerified, + required TResult Function(OTPFailureState value) failure, + }) { + return otpSending(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialOTPState value)? initial, + TResult? Function(OTPSendingState value)? otpSending, + TResult? Function(OTPSentState value)? otpSent, + TResult? Function(OTPVerifyingState value)? otpVerifying, + TResult? Function(OTPVerifiedState value)? otpVerified, + TResult? Function(OTPFailureState value)? failure, + }) { + return otpSending?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialOTPState value)? initial, + TResult Function(OTPSendingState value)? otpSending, + TResult Function(OTPSentState value)? otpSent, + TResult Function(OTPVerifyingState value)? otpVerifying, + TResult Function(OTPVerifiedState value)? otpVerified, + TResult Function(OTPFailureState value)? failure, + required TResult orElse(), + }) { + if (otpSending != null) { + return otpSending(this); + } + return orElse(); + } +} + +abstract class OTPSendingState implements OTPVerificationState { + factory OTPSendingState() = _$OTPSendingStateImpl; +} + +/// @nodoc +abstract class _$$OTPSentStateImplCopyWith<$Res> { + factory _$$OTPSentStateImplCopyWith( + _$OTPSentStateImpl value, $Res Function(_$OTPSentStateImpl) then) = + __$$OTPSentStateImplCopyWithImpl<$Res>; + @useResult + $Res call({String? otp}); +} + +/// @nodoc +class __$$OTPSentStateImplCopyWithImpl<$Res> + extends _$OTPVerificationStateCopyWithImpl<$Res, _$OTPSentStateImpl> + implements _$$OTPSentStateImplCopyWith<$Res> { + __$$OTPSentStateImplCopyWithImpl( + _$OTPSentStateImpl _value, $Res Function(_$OTPSentStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? otp = freezed, + }) { + return _then(_$OTPSentStateImpl( + otp: freezed == otp + ? _value.otp + : otp // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc + +class _$OTPSentStateImpl implements OTPSentState { + _$OTPSentStateImpl({this.otp}); + + @override + final String? otp; + + @override + String toString() { + return 'OTPVerificationState.otpSent(otp: $otp)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$OTPSentStateImpl && + (identical(other.otp, otp) || other.otp == otp)); + } + + @override + int get hashCode => Object.hash(runtimeType, otp); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$OTPSentStateImplCopyWith<_$OTPSentStateImpl> get copyWith => + __$$OTPSentStateImplCopyWithImpl<_$OTPSentStateImpl>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() otpSending, + required TResult Function(String? otp) otpSent, + required TResult Function() otpVerifying, + required TResult Function() otpVerified, + required TResult Function() failure, + }) { + return otpSent(otp); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? otpSending, + TResult? Function(String? otp)? otpSent, + TResult? Function()? otpVerifying, + TResult? Function()? otpVerified, + TResult? Function()? failure, + }) { + return otpSent?.call(otp); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? otpSending, + TResult Function(String? otp)? otpSent, + TResult Function()? otpVerifying, + TResult Function()? otpVerified, + TResult Function()? failure, + required TResult orElse(), + }) { + if (otpSent != null) { + return otpSent(otp); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialOTPState value) initial, + required TResult Function(OTPSendingState value) otpSending, + required TResult Function(OTPSentState value) otpSent, + required TResult Function(OTPVerifyingState value) otpVerifying, + required TResult Function(OTPVerifiedState value) otpVerified, + required TResult Function(OTPFailureState value) failure, + }) { + return otpSent(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialOTPState value)? initial, + TResult? Function(OTPSendingState value)? otpSending, + TResult? Function(OTPSentState value)? otpSent, + TResult? Function(OTPVerifyingState value)? otpVerifying, + TResult? Function(OTPVerifiedState value)? otpVerified, + TResult? Function(OTPFailureState value)? failure, + }) { + return otpSent?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialOTPState value)? initial, + TResult Function(OTPSendingState value)? otpSending, + TResult Function(OTPSentState value)? otpSent, + TResult Function(OTPVerifyingState value)? otpVerifying, + TResult Function(OTPVerifiedState value)? otpVerified, + TResult Function(OTPFailureState value)? failure, + required TResult orElse(), + }) { + if (otpSent != null) { + return otpSent(this); + } + return orElse(); + } +} + +abstract class OTPSentState implements OTPVerificationState { + factory OTPSentState({final String? otp}) = _$OTPSentStateImpl; + + String? get otp; + @JsonKey(ignore: true) + _$$OTPSentStateImplCopyWith<_$OTPSentStateImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$OTPVerifyingStateImplCopyWith<$Res> { + factory _$$OTPVerifyingStateImplCopyWith(_$OTPVerifyingStateImpl value, + $Res Function(_$OTPVerifyingStateImpl) then) = + __$$OTPVerifyingStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$OTPVerifyingStateImplCopyWithImpl<$Res> + extends _$OTPVerificationStateCopyWithImpl<$Res, _$OTPVerifyingStateImpl> + implements _$$OTPVerifyingStateImplCopyWith<$Res> { + __$$OTPVerifyingStateImplCopyWithImpl(_$OTPVerifyingStateImpl _value, + $Res Function(_$OTPVerifyingStateImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$OTPVerifyingStateImpl implements OTPVerifyingState { + _$OTPVerifyingStateImpl(); + + @override + String toString() { + return 'OTPVerificationState.otpVerifying()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$OTPVerifyingStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() otpSending, + required TResult Function(String? otp) otpSent, + required TResult Function() otpVerifying, + required TResult Function() otpVerified, + required TResult Function() failure, + }) { + return otpVerifying(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? otpSending, + TResult? Function(String? otp)? otpSent, + TResult? Function()? otpVerifying, + TResult? Function()? otpVerified, + TResult? Function()? failure, + }) { + return otpVerifying?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? otpSending, + TResult Function(String? otp)? otpSent, + TResult Function()? otpVerifying, + TResult Function()? otpVerified, + TResult Function()? failure, + required TResult orElse(), + }) { + if (otpVerifying != null) { + return otpVerifying(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialOTPState value) initial, + required TResult Function(OTPSendingState value) otpSending, + required TResult Function(OTPSentState value) otpSent, + required TResult Function(OTPVerifyingState value) otpVerifying, + required TResult Function(OTPVerifiedState value) otpVerified, + required TResult Function(OTPFailureState value) failure, + }) { + return otpVerifying(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialOTPState value)? initial, + TResult? Function(OTPSendingState value)? otpSending, + TResult? Function(OTPSentState value)? otpSent, + TResult? Function(OTPVerifyingState value)? otpVerifying, + TResult? Function(OTPVerifiedState value)? otpVerified, + TResult? Function(OTPFailureState value)? failure, + }) { + return otpVerifying?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialOTPState value)? initial, + TResult Function(OTPSendingState value)? otpSending, + TResult Function(OTPSentState value)? otpSent, + TResult Function(OTPVerifyingState value)? otpVerifying, + TResult Function(OTPVerifiedState value)? otpVerified, + TResult Function(OTPFailureState value)? failure, + required TResult orElse(), + }) { + if (otpVerifying != null) { + return otpVerifying(this); + } + return orElse(); + } +} + +abstract class OTPVerifyingState implements OTPVerificationState { + factory OTPVerifyingState() = _$OTPVerifyingStateImpl; +} + +/// @nodoc +abstract class _$$OTPVerifiedStateImplCopyWith<$Res> { + factory _$$OTPVerifiedStateImplCopyWith(_$OTPVerifiedStateImpl value, + $Res Function(_$OTPVerifiedStateImpl) then) = + __$$OTPVerifiedStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$OTPVerifiedStateImplCopyWithImpl<$Res> + extends _$OTPVerificationStateCopyWithImpl<$Res, _$OTPVerifiedStateImpl> + implements _$$OTPVerifiedStateImplCopyWith<$Res> { + __$$OTPVerifiedStateImplCopyWithImpl(_$OTPVerifiedStateImpl _value, + $Res Function(_$OTPVerifiedStateImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$OTPVerifiedStateImpl implements OTPVerifiedState { + _$OTPVerifiedStateImpl(); + + @override + String toString() { + return 'OTPVerificationState.otpVerified()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$OTPVerifiedStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() otpSending, + required TResult Function(String? otp) otpSent, + required TResult Function() otpVerifying, + required TResult Function() otpVerified, + required TResult Function() failure, + }) { + return otpVerified(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? otpSending, + TResult? Function(String? otp)? otpSent, + TResult? Function()? otpVerifying, + TResult? Function()? otpVerified, + TResult? Function()? failure, + }) { + return otpVerified?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? otpSending, + TResult Function(String? otp)? otpSent, + TResult Function()? otpVerifying, + TResult Function()? otpVerified, + TResult Function()? failure, + required TResult orElse(), + }) { + if (otpVerified != null) { + return otpVerified(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialOTPState value) initial, + required TResult Function(OTPSendingState value) otpSending, + required TResult Function(OTPSentState value) otpSent, + required TResult Function(OTPVerifyingState value) otpVerifying, + required TResult Function(OTPVerifiedState value) otpVerified, + required TResult Function(OTPFailureState value) failure, + }) { + return otpVerified(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialOTPState value)? initial, + TResult? Function(OTPSendingState value)? otpSending, + TResult? Function(OTPSentState value)? otpSent, + TResult? Function(OTPVerifyingState value)? otpVerifying, + TResult? Function(OTPVerifiedState value)? otpVerified, + TResult? Function(OTPFailureState value)? failure, + }) { + return otpVerified?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialOTPState value)? initial, + TResult Function(OTPSendingState value)? otpSending, + TResult Function(OTPSentState value)? otpSent, + TResult Function(OTPVerifyingState value)? otpVerifying, + TResult Function(OTPVerifiedState value)? otpVerified, + TResult Function(OTPFailureState value)? failure, + required TResult orElse(), + }) { + if (otpVerified != null) { + return otpVerified(this); + } + return orElse(); + } +} + +abstract class OTPVerifiedState implements OTPVerificationState { + factory OTPVerifiedState() = _$OTPVerifiedStateImpl; +} + +/// @nodoc +abstract class _$$OTPFailureStateImplCopyWith<$Res> { + factory _$$OTPFailureStateImplCopyWith(_$OTPFailureStateImpl value, + $Res Function(_$OTPFailureStateImpl) then) = + __$$OTPFailureStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$OTPFailureStateImplCopyWithImpl<$Res> + extends _$OTPVerificationStateCopyWithImpl<$Res, _$OTPFailureStateImpl> + implements _$$OTPFailureStateImplCopyWith<$Res> { + __$$OTPFailureStateImplCopyWithImpl( + _$OTPFailureStateImpl _value, $Res Function(_$OTPFailureStateImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$OTPFailureStateImpl implements OTPFailureState { + _$OTPFailureStateImpl(); + + @override + String toString() { + return 'OTPVerificationState.failure()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$OTPFailureStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() otpSending, + required TResult Function(String? otp) otpSent, + required TResult Function() otpVerifying, + required TResult Function() otpVerified, + required TResult Function() failure, + }) { + return failure(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? otpSending, + TResult? Function(String? otp)? otpSent, + TResult? Function()? otpVerifying, + TResult? Function()? otpVerified, + TResult? Function()? failure, + }) { + return failure?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? otpSending, + TResult Function(String? otp)? otpSent, + TResult Function()? otpVerifying, + TResult Function()? otpVerified, + TResult Function()? failure, + required TResult orElse(), + }) { + if (failure != null) { + return failure(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialOTPState value) initial, + required TResult Function(OTPSendingState value) otpSending, + required TResult Function(OTPSentState value) otpSent, + required TResult Function(OTPVerifyingState value) otpVerifying, + required TResult Function(OTPVerifiedState value) otpVerified, + required TResult Function(OTPFailureState value) failure, + }) { + return failure(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialOTPState value)? initial, + TResult? Function(OTPSendingState value)? otpSending, + TResult? Function(OTPSentState value)? otpSent, + TResult? Function(OTPVerifyingState value)? otpVerifying, + TResult? Function(OTPVerifiedState value)? otpVerified, + TResult? Function(OTPFailureState value)? failure, + }) { + return failure?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialOTPState value)? initial, + TResult Function(OTPSendingState value)? otpSending, + TResult Function(OTPSentState value)? otpSent, + TResult Function(OTPVerifyingState value)? otpVerifying, + TResult Function(OTPVerifiedState value)? otpVerified, + TResult Function(OTPFailureState value)? failure, + required TResult orElse(), + }) { + if (failure != null) { + return failure(this); + } + return orElse(); + } +} + +abstract class OTPFailureState implements OTPVerificationState { + factory OTPFailureState() = _$OTPFailureStateImpl; +} diff --git a/lib/presentation/group/cubit/group_cubit/group_cubit.dart b/lib/presentation/group/cubit/group_cubit/group_cubit.dart new file mode 100644 index 00000000..44cab9df --- /dev/null +++ b/lib/presentation/group/cubit/group_cubit/group_cubit.dart @@ -0,0 +1,680 @@ +import 'dart:async'; +import 'package:beacon/config/local_notification.dart'; +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/domain/entities/group/group_entity.dart'; +import 'package:beacon/domain/usecase/group_usecase.dart'; +import 'package:beacon/domain/usecase/hike_usecase.dart'; +import 'package:beacon/presentation/group/cubit/group_cubit/group_state.dart'; +import 'package:beacon/presentation/home/home_cubit/home_cubit.dart'; +import 'package:beacon/locator.dart'; +import 'package:beacon/config/router/router.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:intl/intl.dart'; + +class GroupCubit extends Cubit { + final GroupUseCase _groupUseCase; + + GroupCubit._internal(this._groupUseCase) : super(InitialGroupState()) { + _initializeStates(); + } + + static GroupCubit? _instance; + + factory GroupCubit(GroupUseCase groupUseCase) { + return _instance ?? GroupCubit._internal(groupUseCase); + } + + int _page = 1; + int _pageSize = 4; + List _beacons = []; + List _nearbyBeacons = []; + List _statusBeacons = []; + + double _radius = 1000.0; + Position? _currentPosition; + String? _groupId; + + // for all beacon state + bool _isCompletelyFetched = false; + bool _isLoadingMore = false; + + // for status beacon state + + filters _currentFilter = filters.ALL; + + late AllBeaconGroupState _allBeaconGroupState; + late NearbyBeaconGroupState _nearbyBeaconGroupState; + late StatusFilterBeaconGroupState _statusFilterBeaconGroupState; + late ShrimmerGroupState _shrimmerGroupState; + late LoadingGroupState _loadingGroupState; + + init(GroupEntity group) { + _groupId = group.id!; + _currentPosition = locationService.currentPosition; + } + + _initializeStates() { + _shrimmerGroupState = ShrimmerGroupState(); + _allBeaconGroupState = AllBeaconGroupState(beacons: []); + + _nearbyBeaconGroupState = + NearbyBeaconGroupState(beacons: _nearbyBeacons, radius: _radius); + + _statusFilterBeaconGroupState = + StatusFilterBeaconGroupState(beacons: [], type: _currentFilter); + + _loadingGroupState = LoadingGroupState(); + } + + clear() { + _page = 1; + _beacons.clear(); + _nearbyBeacons.clear(); + _statusBeacons.clear(); + _isLoadingMore = false; + _isCompletelyFetched = false; + _currentFilter = filters.ALL; + _groupId = null; + emit(InitialGroupState()); + } + + Future createHike(String title, int startsAt, int expiresAt, + String groupID, bool isInstant) async { + emit(_loadingGroupState); + + bool isLocationEnabled = await fetchPosition(); + if (!isLocationEnabled) { + emit(ErrorGroupState( + message: 'To create a hike please enable your location!', + )); + return; + } + + final dataState = await _groupUseCase.createHike( + title, + startsAt, + expiresAt, + _currentPosition!.latitude.toString(), + _currentPosition!.longitude.toString(), + groupID); + + // checking the current state of the screen to add the beacon in the same list of state + + if (dataState is DataSuccess && dataState.data != null) { + BeaconEntity beacon = dataState.data!; + + // for adding beacons in group card + locator().addBeaconInGroup(beacon, _groupId!); + + locator().scheduleNotification(beacon); + + _beacons.add(beacon); + _beacons = sortHikes(_beacons); + if (_currentFilter == filters.ALL) { + if (isInstant) { + appRouter.popAndPush(HikeScreenRoute(beacon: beacon, isLeader: true)); + return; + } + emit(_allBeaconGroupState.copyWith( + beacons: _beacons, message: 'New Hike created')); + } + if (_currentFilter == filters.NEARBY) { + if (isInstant) { + appRouter.popAndPush(HikeScreenRoute(beacon: beacon, isLeader: true)); + return; + } + _nearbyBeacons.add(beacon); + _nearbyBeacons = sortHikes(_nearbyBeacons); + emit(_nearbyBeaconGroupState.copyWith(beacons: _nearbyBeacons)); + } else if (_currentFilter == filters.UPCOMING) { + if (isInstant) { + appRouter.popAndPush(HikeScreenRoute(beacon: beacon, isLeader: true)); + return; + } + _statusBeacons.add(beacon); + _statusBeacons = sortHikes(_statusBeacons); + emit(_statusFilterBeaconGroupState.copyWith( + beacons: _statusBeacons, type: _currentFilter)); + } + } else { + _emitErrorState(dataState.error); + } + } + + _emitErrorState(String? message) { + if (_currentFilter == filters.ALL) { + emit(_allBeaconGroupState.copyWith( + beacons: _beacons, + message: message, + )); + } else if (_currentFilter == filters.NEARBY) { + emit(_nearbyBeaconGroupState.copyWith( + beacons: _nearbyBeacons, + message: message, + )); + } else { + emit(_statusFilterBeaconGroupState.copyWith( + beacons: _statusBeacons, message: message, type: _currentFilter)); + } + } + + // ALL HIKES + Future allHikes(String groupId) async { + print('calling hikes'); + + if (_isCompletelyFetched || _isLoadingMore) return; + + if (_page == 1) { + emit((ShrimmerGroupState())); + } else { + _isLoadingMore = true; + emit(AllBeaconGroupState( + beacons: _beacons, + isLoadingMore: _isLoadingMore, + isCompletelyFetched: _isCompletelyFetched)); + } + + final dataState = await _groupUseCase.fetchHikes(groupId, _page, _pageSize); + + if (dataState is DataSuccess && dataState.data != null) { + print(dataState.data!.length.toString()); + + addBeaconList(dataState.data!); + _beacons = sortHikes(_beacons); + int length = dataState.data!.length; + if (length < 4) { + _isCompletelyFetched = true; + } + _page++; + _isLoadingMore = false; + emit(_allBeaconGroupState.copyWith( + beacons: _beacons, + isLoadingMore: _isLoadingMore, + isCompletelyFetched: _isCompletelyFetched)); + } else { + _isLoadingMore = false; + emit(_allBeaconGroupState.copyWith( + beacons: _beacons, + isLoadingMore: _isLoadingMore, + message: dataState.error, + isCompletelyFetched: _isCompletelyFetched)); + } + } + + void addBeaconList(List newBeacons) { + late bool isBeaconPresent; + var beacons = newBeacons; + for (int i = 0; i < beacons.length; i++) { + var beacon = beacons[i]; + isBeaconPresent = false; + for (int j = 0; j < _beacons.length; j++) { + if (beacon.id! == _beacons[j].id!) { + isBeaconPresent = true; + _beacons.removeAt(j); + _beacons.add(beacon); + } + } + if (isBeaconPresent == false) { + _beacons.add(beacon); + } + } + } + + Future fetchPosition() async { + _currentPosition = locationService.currentPosition; + if (_currentPosition == null) { + try { + _currentPosition = await locationService.getCurrentLocation(); + return true; + } catch (e) { + print('error: $e'); + return false; + } + } + return true; + } + + // NEARBY HIKES + Future nearbyHikes(String groupId, {double radius = 1000.0}) async { + _currentFilter = filters.NEARBY; + + emit(_shrimmerGroupState); + bool isLocationEnabled = await fetchPosition(); + if (!isLocationEnabled) { + emit(ErrorGroupState( + message: 'To see nearby hikes Please give access to your location!', + )); + return; + } + _radius = radius; + + final dataState = await _groupUseCase.nearbyHikes( + groupId, + _currentPosition!.latitude.toString(), + _currentPosition!.longitude.toString(), + radius); + + if (dataState is DataSuccess && dataState.data != null) { + _nearbyBeacons.clear(); + dataState.data!.forEach((beacon) { + _nearbyBeacons.add(beacon); + }); + _nearbyBeacons = sortHikes(_nearbyBeacons); + emit(_nearbyBeaconGroupState.copyWith( + beacons: _nearbyBeacons, + radius: _radius, + message: 'Beacons under $radius km..')); + } else { + emit(_nearbyBeaconGroupState.copyWith( + beacons: _nearbyBeacons, radius: _radius, message: dataState.error)); + } + } + + // ACTIVE UPCOMING INACTIVE HIKES + Future _filterHikes(String groupId, filters type) async { + print('calling filter hikes'); + print(_currentFilter.toString()); + emit(ShrimmerGroupState()); + final dataState = await _groupUseCase.filterHikes(groupId, type.name); + + if (dataState is DataSuccess && dataState.data != null) { + for (var beacon in dataState.data!) { + _statusBeacons.add(beacon); + } + _statusBeacons = sortHikes(_statusBeacons); + + print('calling function inside: ${_statusBeacons.length.toString()}'); + + emit(_statusFilterBeaconGroupState.copyWith( + beacons: _statusBeacons, type: type)); + } else { + emit(_statusFilterBeaconGroupState.copyWith( + beacons: _statusBeacons, type: type, message: dataState.error)); + } + } + + // DELETE BEACON + Future deleteBeacon(BeaconEntity beacon) async { + if (beacon.leader!.id != localApi.userModel.id) { + // for showing the snackbar + reloadState(message: 'You are not the leader of beacon!'); + return false; + } + var dataState = await _groupUseCase.deleteBeacon(beacon.id); + if (dataState is DataSuccess && dataState.data != null) { + // for removing beacons from group card + locator().deleteBeaconFromGroup(beacon, _groupId!); + + _beacons.removeWhere((currentBeacon) => currentBeacon.id! == beacon.id!); + _nearbyBeacons + .removeWhere((currentBeacon) => currentBeacon.id! == beacon.id!); + _statusBeacons + .removeWhere((currentBeacon) => currentBeacon.id! == beacon.id!); + + return true; + } else { + return false; + } + } + + List sortHikes(List _beacons) { + List inactive = []; + var now = DateTime.now(); + + _beacons.sort((a, b) => a.startsAt!.compareTo(b.startsAt!)); + + _beacons.removeWhere((beacon) { + var expiresAt = DateTime.fromMillisecondsSinceEpoch(beacon.expiresAt!); + if (expiresAt.isBefore(now)) { + inactive.add(beacon); + return true; + } + return false; + }); + + _beacons.addAll(inactive); + return _beacons; + } + + void changeFilter(filters filter) { + // emitting all loading state + print(filter.toString()); + if (filter == filters.ALL) { + _currentFilter = filters.ALL; + emit(ShrimmerGroupState()); + Future.delayed(Duration(milliseconds: 500), () { + emit(_allBeaconGroupState.copyWith(beacons: _beacons)); + }); + } + // emitting active upcoming and active on the basis of filter chosen + else if (filter == filters.ACTIVE || + filter == filters.INACTIVE || + filter == filters.UPCOMING) { + _loadfilterBeacons(filter, _groupId!); + } + } + + void _loadfilterBeacons(filters type, String groupId) { + if (type == _currentFilter) { + emit(ShrimmerGroupState()); + Future.delayed(Duration(milliseconds: 500), () { + emit(_statusFilterBeaconGroupState.copyWith( + beacons: _statusBeacons, type: type)); + }); + } else { + _currentFilter = type; + _statusBeacons.clear(); + _filterHikes(groupId, type); + } + } + + void reloadState({String? message}) { + emit(_loadingGroupState); + String? _message = message; + if (_currentFilter == filters.ALL) { + emit(_allBeaconGroupState.copyWith(beacons: _beacons, message: _message)); + } else if (_currentFilter == filters.NEARBY) { + emit(_nearbyBeaconGroupState.copyWith( + beacons: _nearbyBeacons, + message: _message, + )); + } else if (_currentFilter == filters.UPCOMING || + _currentFilter == filters.ACTIVE || + _currentFilter == filters.INACTIVE) { + emit(_statusFilterBeaconGroupState.copyWith( + beacons: _statusBeacons, message: _message, type: _currentFilter)); + } + } + + Future addBeacon(BeaconEntity newBeacon, String message) async { + List updatedBeacons = List.from(_beacons) + ..removeWhere((beacon) => beacon.id == newBeacon.id) + ..add(newBeacon); + _beacons = updatedBeacons; + updatedBeacons = sortHikes(updatedBeacons); + + print('here is come'); + + var newstate = _allBeaconGroupState.copyWith( + beacons: updatedBeacons, message: message); + print(newstate == _allBeaconGroupState); + emit(_shrimmerGroupState); + emit(_allBeaconGroupState.copyWith( + beacons: updatedBeacons, message: message)); + + if (state is NearbyBeaconGroupState) { + // first checking that the distance it is searching is that + + double distance = await locationService.calculateDistance( + LatLng(_currentPosition!.latitude, _currentPosition!.longitude), + LatLng(double.parse(newBeacon.location!.lat!), + double.parse(newBeacon.location!.lon!))); + + if (distance > _radius) return null; + + List updatedBeacons = List.from(_nearbyBeacons) + ..removeWhere((beacon) => beacon.id! == newBeacon.id!) + ..add(newBeacon); + + _nearbyBeacons = updatedBeacons; + + updatedBeacons = sortHikes(updatedBeacons); + emit(_nearbyBeaconGroupState.copyWith( + beacons: updatedBeacons, radius: _radius)); + } else if (state is StatusFilterBeaconGroupState) { + List updatedBeacons = List.from(_statusBeacons) + ..removeWhere((beacon) => beacon.id! == newBeacon.id!) + ..add(newBeacon); + + _statusBeacons = updatedBeacons; + updatedBeacons = sortHikes(updatedBeacons); + + emit(_statusFilterBeaconGroupState.copyWith( + beacons: updatedBeacons, + message: message, + )); + } + } + + Future removeBeacon(BeaconEntity deletedBeacon, String message) async { + for (int i = 0; i < _beacons.length; i++) { + if (_beacons[i].id! == deletedBeacon.id!) { + _beacons.removeAt(i); + break; + } + } + + var updatedBeacons = List.from(_beacons) + ..removeWhere((beacon) => beacon.id! == deletedBeacon.id!); + _beacons = updatedBeacons; + + emit(_allBeaconGroupState.copyWith( + beacons: updatedBeacons, + message: message, + )); + + if (state is NearbyBeaconGroupState) { + // first checking that the distance it is searching is that + + var updatedBeacons = List.from(_nearbyBeacons) + ..removeWhere((beacon) => beacon.id! == deletedBeacon.id!); + _nearbyBeacons = updatedBeacons; + emit(_nearbyBeaconGroupState.copyWith( + beacons: updatedBeacons, radius: _radius, message: message)); + } else if (state is StatusFilterBeaconGroupState) { + var updatedBeacons = List.from(_statusBeacons) + ..removeWhere((beacon) => beacon.id! == deletedBeacon.id!); + _statusBeacons = updatedBeacons; + emit(_statusFilterBeaconGroupState.copyWith( + beacons: updatedBeacons, + message: message, + )); + } + } + + Future rescheduleHike( + int newExpiresAt, int newStartsAt, String beaconId) async { + emit(ShrimmerGroupState()); + + DataState dataState = + await _groupUseCase.rescheduleHike(newExpiresAt, newStartsAt, beaconId); + + if (dataState is DataSuccess && dataState.data != null) { + BeaconEntity updatedBeacon = dataState.data!; + // adding the beacon in list and loading again + _beacons.removeWhere((beacon) => beacon.id! == updatedBeacon.id!); + _beacons.add(updatedBeacon); + _beacons = sortHikes(_beacons); + if (_currentFilter == filters.ALL) { + emit(_allBeaconGroupState.copyWith( + beacons: _beacons, message: 'Hike Rescheduled')); + return; + } + if (_currentFilter == filters.NEARBY) { + _nearbyBeacons.removeWhere((beacon) => beacon.id! == updatedBeacon.id!); + _nearbyBeacons.add(updatedBeacon); + _nearbyBeacons = sortHikes(_nearbyBeacons); + emit(_nearbyBeaconGroupState.copyWith( + beacons: _nearbyBeacons, radius: _radius)); + } else if (_currentFilter == filters.UPCOMING) { + _statusBeacons.removeWhere((beacon) => beacon.id! == updatedBeacon.id!); + _statusBeacons.add(updatedBeacon); + _statusBeacons = sortHikes(_statusBeacons); + + emit(_statusFilterBeaconGroupState.copyWith( + beacons: _nearbyBeacons, + message: 'Hike Rescheduled', + type: _currentFilter)); + } + } else { + if (_currentFilter == filters.ALL) { + emit(_allBeaconGroupState.copyWith( + beacons: _beacons, message: dataState.error)); + } else if (_currentFilter == filters.NEARBY) { + emit(_nearbyBeaconGroupState.copyWith( + beacons: _nearbyBeacons, + radius: _radius, + message: dataState.error)); + } else if (_currentFilter == filters.UPCOMING) { + emit(_statusFilterBeaconGroupState.copyWith( + beacons: _nearbyBeacons, + message: dataState.error, + type: _currentFilter)); + } + } + } + + void emitState({ + String? allBeaconMessage, + String? nearbyBeaconMessage, + String? statusBeaconMessage, + }) { + // creating this version to emit two same states consecutively + int version = DateTime.now().millisecondsSinceEpoch; + if (_currentFilter == filters.ALL) { + emit(_allBeaconGroupState.copyWith( + beacons: _beacons, + isCompletelyFetched: _isCompletelyFetched, + isLoadingMore: _isLoadingMore, + message: allBeaconMessage, + type: _currentFilter, + version: version)); + } else if (_currentFilter == filters.NEARBY) { + emit(_nearbyBeaconGroupState.copyWith( + beacons: _nearbyBeacons, + message: nearbyBeaconMessage, + type: _currentFilter, + radius: _radius, + version: version)); + } else { + emit(_statusFilterBeaconGroupState.copyWith( + beacons: _statusBeacons, + message: statusBeaconMessage, + type: _currentFilter, + version: version)); + } + return; + } + + _updateUserLocation(String beaconId) async { + var location = locationService.currentPosition; + if (location != null) { + // updating the current location before entering the map + await locator().changeUserLocation( + beaconId, LatLng(location.latitude, location.longitude)); + } else { + location = await locationService.getCurrentLocation(); + if (location == null) { + emit(ErrorGroupState(message: 'Please enable your location!')); + return; + } + await locator().changeUserLocation( + beaconId, LatLng(location.latitude, location.longitude)); + } + } + + Future joinBeacon( + BeaconEntity beacon, bool hasEnded, bool hasStarted) async { + if (hasEnded) { + String message = 'Beacon is not active anymore!'; + // function for emitting state on the basis of filter + if (beacon.leader!.id == localApi.userModel.id) { + // will allow the leader to join the beacon + + emit(_loadingGroupState); + + await _updateUserLocation(beacon.id!); + + appRouter.popAndPush(HikeScreenRoute( + beacon: beacon, + isLeader: (beacon.leader!.id == localApi.userModel.id))); + } + emitState( + allBeaconMessage: message, + nearbyBeaconMessage: message, + statusBeaconMessage: message); + return; + } + if (!hasStarted) { + String message = + 'Beacon has not yet started! \nPlease come back at ${DateFormat("hh:mm a, d/M/y").format(DateTime.fromMillisecondsSinceEpoch(beacon.startsAt!)).toString()}'; + emitState( + allBeaconMessage: message, + nearbyBeaconMessage: message, + statusBeaconMessage: message); + return; + } + bool isJoinee = false; + for (var i in beacon.followers!) { + if (i!.id == localApi.userModel.id) { + isJoinee = true; + } + } + if (hasStarted && + (beacon.leader!.id == localApi.userModel.id || isJoinee)) { + emit(_loadingGroupState); + + await _updateUserLocation(beacon.id!); + + appRouter.popAndPush(HikeScreenRoute( + beacon: beacon, + isLeader: (beacon.leader!.id == localApi.userModel.id))); + } else { + emit(_loadingGroupState); + await _updateUserLocation(beacon.id!); + final dataState = await _groupUseCase.joinHike(beacon.shortcode!); + if (dataState is DataFailed) return; + appRouter.popAndPush(HikeScreenRoute( + beacon: dataState.data!, + isLeader: dataState.data!.leader!.id == localApi.userModel.id)); + } + } + + Future joinBeaconWithShortCode(String shortcode) async { + _currentFilter = filters.ALL; + emit(_allBeaconGroupState.copyWith( + beacons: _beacons, + isCompletelyFetched: _isCompletelyFetched, + isLoadingMore: _isLoadingMore, + type: _currentFilter)); + emit(_loadingGroupState); + + for (var beacon in _beacons) { + if (beacon.shortcode == shortcode) { + bool hasEnded = DateTime.now() + .isAfter(DateTime.fromMillisecondsSinceEpoch(beacon.expiresAt!)); + bool hasStarted = DateTime.now() + .isAfter((DateTime.fromMillisecondsSinceEpoch(beacon.startsAt!))); + await joinBeacon(beacon, hasEnded, hasStarted); + emitState(); + return; + } + } + + final dataState = await _groupUseCase.joinHike(shortcode); + + if (dataState is DataSuccess) { + var beacon = dataState.data!; + if (_groupId != beacon.group!.id) { + var message = 'The beacon is not the part of this group!'; + emitState( + allBeaconMessage: message, + nearbyBeaconMessage: message, + statusBeaconMessage: message); + return; + } + bool hasEnded = DateTime.now() + .isAfter(DateTime.fromMillisecondsSinceEpoch(beacon.expiresAt!)); + bool hasStarted = DateTime.now() + .isAfter((DateTime.fromMillisecondsSinceEpoch(beacon.startsAt!))); + + _beacons.add(beacon); + await joinBeacon(beacon, hasEnded, hasStarted); + } else if (dataState is DataFailed) { + emitState(allBeaconMessage: dataState.error); + } + } +} diff --git a/lib/presentation/group/cubit/group_cubit/group_state.dart b/lib/presentation/group/cubit/group_cubit/group_state.dart new file mode 100644 index 00000000..f8043bd1 --- /dev/null +++ b/lib/presentation/group/cubit/group_cubit/group_state.dart @@ -0,0 +1,41 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +part 'group_state.freezed.dart'; + +enum filters { ALL, ACTIVE, UPCOMING, NEARBY, INACTIVE } + +@freezed +abstract class GroupState with _$GroupState { + const factory GroupState.initial() = InitialGroupState; + + const factory GroupState.loading() = LoadingGroupState; + + const factory GroupState.shrimmer() = ShrimmerGroupState; + + const factory GroupState.allBeacon( + {@Default(false) bool isLoadingMore, + @Default(false) bool isCompletelyFetched, + String? message, + @Default(filters.ALL) filters type, + required List beacons, + @Default(0) int version}) = AllBeaconGroupState; + + const factory GroupState.nearbyBeacon( + {String? message, + @Default(filters.NEARBY) filters type, + required List beacons, + @Default(1000.0) double radius, + @Default(0) int version}) = NearbyBeaconGroupState; + + const factory GroupState.statusFilterBeacon( + {@Default(false) bool isLoadingMore, + @Default(false) bool isCompletelyFetched, + String? message, + filters? type, + required List beacons, + @Default(0) int version}) = StatusFilterBeaconGroupState; + + const factory GroupState.error({ + required String message, + }) = ErrorGroupState; +} diff --git a/lib/presentation/group/cubit/group_cubit/group_state.freezed.dart b/lib/presentation/group/cubit/group_cubit/group_state.freezed.dart new file mode 100644 index 00000000..0ee4c3f4 --- /dev/null +++ b/lib/presentation/group/cubit/group_cubit/group_state.freezed.dart @@ -0,0 +1,1790 @@ +// 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 'group_state.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 _$GroupState { + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function() shrimmer, + required TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version) + allBeacon, + required TResult Function(String? message, filters type, + List beacons, double radius, int version) + nearbyBeacon, + required TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version) + statusFilterBeacon, + required TResult Function(String message) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function()? shrimmer, + TResult? Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version)? + allBeacon, + TResult? Function(String? message, filters type, List beacons, + double radius, int version)? + nearbyBeacon, + TResult? Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version)? + statusFilterBeacon, + TResult? Function(String message)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function()? shrimmer, + TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version)? + allBeacon, + TResult Function(String? message, filters type, List beacons, + double radius, int version)? + nearbyBeacon, + TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version)? + statusFilterBeacon, + TResult Function(String message)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(InitialGroupState value) initial, + required TResult Function(LoadingGroupState value) loading, + required TResult Function(ShrimmerGroupState value) shrimmer, + required TResult Function(AllBeaconGroupState value) allBeacon, + required TResult Function(NearbyBeaconGroupState value) nearbyBeacon, + required TResult Function(StatusFilterBeaconGroupState value) + statusFilterBeacon, + required TResult Function(ErrorGroupState value) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialGroupState value)? initial, + TResult? Function(LoadingGroupState value)? loading, + TResult? Function(ShrimmerGroupState value)? shrimmer, + TResult? Function(AllBeaconGroupState value)? allBeacon, + TResult? Function(NearbyBeaconGroupState value)? nearbyBeacon, + TResult? Function(StatusFilterBeaconGroupState value)? statusFilterBeacon, + TResult? Function(ErrorGroupState value)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialGroupState value)? initial, + TResult Function(LoadingGroupState value)? loading, + TResult Function(ShrimmerGroupState value)? shrimmer, + TResult Function(AllBeaconGroupState value)? allBeacon, + TResult Function(NearbyBeaconGroupState value)? nearbyBeacon, + TResult Function(StatusFilterBeaconGroupState value)? statusFilterBeacon, + TResult Function(ErrorGroupState value)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $GroupStateCopyWith<$Res> { + factory $GroupStateCopyWith( + GroupState value, $Res Function(GroupState) then) = + _$GroupStateCopyWithImpl<$Res, GroupState>; +} + +/// @nodoc +class _$GroupStateCopyWithImpl<$Res, $Val extends GroupState> + implements $GroupStateCopyWith<$Res> { + _$GroupStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; +} + +/// @nodoc +abstract class _$$InitialGroupStateImplCopyWith<$Res> { + factory _$$InitialGroupStateImplCopyWith(_$InitialGroupStateImpl value, + $Res Function(_$InitialGroupStateImpl) then) = + __$$InitialGroupStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$InitialGroupStateImplCopyWithImpl<$Res> + extends _$GroupStateCopyWithImpl<$Res, _$InitialGroupStateImpl> + implements _$$InitialGroupStateImplCopyWith<$Res> { + __$$InitialGroupStateImplCopyWithImpl(_$InitialGroupStateImpl _value, + $Res Function(_$InitialGroupStateImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$InitialGroupStateImpl implements InitialGroupState { + const _$InitialGroupStateImpl(); + + @override + String toString() { + return 'GroupState.initial()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$InitialGroupStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function() shrimmer, + required TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version) + allBeacon, + required TResult Function(String? message, filters type, + List beacons, double radius, int version) + nearbyBeacon, + required TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version) + statusFilterBeacon, + required TResult Function(String message) error, + }) { + return initial(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function()? shrimmer, + TResult? Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version)? + allBeacon, + TResult? Function(String? message, filters type, List beacons, + double radius, int version)? + nearbyBeacon, + TResult? Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version)? + statusFilterBeacon, + TResult? Function(String message)? error, + }) { + return initial?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function()? shrimmer, + TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version)? + allBeacon, + TResult Function(String? message, filters type, List beacons, + double radius, int version)? + nearbyBeacon, + TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version)? + statusFilterBeacon, + TResult Function(String message)? error, + required TResult orElse(), + }) { + if (initial != null) { + return initial(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialGroupState value) initial, + required TResult Function(LoadingGroupState value) loading, + required TResult Function(ShrimmerGroupState value) shrimmer, + required TResult Function(AllBeaconGroupState value) allBeacon, + required TResult Function(NearbyBeaconGroupState value) nearbyBeacon, + required TResult Function(StatusFilterBeaconGroupState value) + statusFilterBeacon, + required TResult Function(ErrorGroupState value) error, + }) { + return initial(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialGroupState value)? initial, + TResult? Function(LoadingGroupState value)? loading, + TResult? Function(ShrimmerGroupState value)? shrimmer, + TResult? Function(AllBeaconGroupState value)? allBeacon, + TResult? Function(NearbyBeaconGroupState value)? nearbyBeacon, + TResult? Function(StatusFilterBeaconGroupState value)? statusFilterBeacon, + TResult? Function(ErrorGroupState value)? error, + }) { + return initial?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialGroupState value)? initial, + TResult Function(LoadingGroupState value)? loading, + TResult Function(ShrimmerGroupState value)? shrimmer, + TResult Function(AllBeaconGroupState value)? allBeacon, + TResult Function(NearbyBeaconGroupState value)? nearbyBeacon, + TResult Function(StatusFilterBeaconGroupState value)? statusFilterBeacon, + TResult Function(ErrorGroupState value)? error, + required TResult orElse(), + }) { + if (initial != null) { + return initial(this); + } + return orElse(); + } +} + +abstract class InitialGroupState implements GroupState { + const factory InitialGroupState() = _$InitialGroupStateImpl; +} + +/// @nodoc +abstract class _$$LoadingGroupStateImplCopyWith<$Res> { + factory _$$LoadingGroupStateImplCopyWith(_$LoadingGroupStateImpl value, + $Res Function(_$LoadingGroupStateImpl) then) = + __$$LoadingGroupStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$LoadingGroupStateImplCopyWithImpl<$Res> + extends _$GroupStateCopyWithImpl<$Res, _$LoadingGroupStateImpl> + implements _$$LoadingGroupStateImplCopyWith<$Res> { + __$$LoadingGroupStateImplCopyWithImpl(_$LoadingGroupStateImpl _value, + $Res Function(_$LoadingGroupStateImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$LoadingGroupStateImpl implements LoadingGroupState { + const _$LoadingGroupStateImpl(); + + @override + String toString() { + return 'GroupState.loading()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$LoadingGroupStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function() shrimmer, + required TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version) + allBeacon, + required TResult Function(String? message, filters type, + List beacons, double radius, int version) + nearbyBeacon, + required TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version) + statusFilterBeacon, + required TResult Function(String message) error, + }) { + return loading(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function()? shrimmer, + TResult? Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version)? + allBeacon, + TResult? Function(String? message, filters type, List beacons, + double radius, int version)? + nearbyBeacon, + TResult? Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version)? + statusFilterBeacon, + TResult? Function(String message)? error, + }) { + return loading?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function()? shrimmer, + TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version)? + allBeacon, + TResult Function(String? message, filters type, List beacons, + double radius, int version)? + nearbyBeacon, + TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version)? + statusFilterBeacon, + TResult Function(String message)? error, + required TResult orElse(), + }) { + if (loading != null) { + return loading(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialGroupState value) initial, + required TResult Function(LoadingGroupState value) loading, + required TResult Function(ShrimmerGroupState value) shrimmer, + required TResult Function(AllBeaconGroupState value) allBeacon, + required TResult Function(NearbyBeaconGroupState value) nearbyBeacon, + required TResult Function(StatusFilterBeaconGroupState value) + statusFilterBeacon, + required TResult Function(ErrorGroupState value) error, + }) { + return loading(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialGroupState value)? initial, + TResult? Function(LoadingGroupState value)? loading, + TResult? Function(ShrimmerGroupState value)? shrimmer, + TResult? Function(AllBeaconGroupState value)? allBeacon, + TResult? Function(NearbyBeaconGroupState value)? nearbyBeacon, + TResult? Function(StatusFilterBeaconGroupState value)? statusFilterBeacon, + TResult? Function(ErrorGroupState value)? error, + }) { + return loading?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialGroupState value)? initial, + TResult Function(LoadingGroupState value)? loading, + TResult Function(ShrimmerGroupState value)? shrimmer, + TResult Function(AllBeaconGroupState value)? allBeacon, + TResult Function(NearbyBeaconGroupState value)? nearbyBeacon, + TResult Function(StatusFilterBeaconGroupState value)? statusFilterBeacon, + TResult Function(ErrorGroupState value)? error, + required TResult orElse(), + }) { + if (loading != null) { + return loading(this); + } + return orElse(); + } +} + +abstract class LoadingGroupState implements GroupState { + const factory LoadingGroupState() = _$LoadingGroupStateImpl; +} + +/// @nodoc +abstract class _$$ShrimmerGroupStateImplCopyWith<$Res> { + factory _$$ShrimmerGroupStateImplCopyWith(_$ShrimmerGroupStateImpl value, + $Res Function(_$ShrimmerGroupStateImpl) then) = + __$$ShrimmerGroupStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$ShrimmerGroupStateImplCopyWithImpl<$Res> + extends _$GroupStateCopyWithImpl<$Res, _$ShrimmerGroupStateImpl> + implements _$$ShrimmerGroupStateImplCopyWith<$Res> { + __$$ShrimmerGroupStateImplCopyWithImpl(_$ShrimmerGroupStateImpl _value, + $Res Function(_$ShrimmerGroupStateImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$ShrimmerGroupStateImpl implements ShrimmerGroupState { + const _$ShrimmerGroupStateImpl(); + + @override + String toString() { + return 'GroupState.shrimmer()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$ShrimmerGroupStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function() shrimmer, + required TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version) + allBeacon, + required TResult Function(String? message, filters type, + List beacons, double radius, int version) + nearbyBeacon, + required TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version) + statusFilterBeacon, + required TResult Function(String message) error, + }) { + return shrimmer(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function()? shrimmer, + TResult? Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version)? + allBeacon, + TResult? Function(String? message, filters type, List beacons, + double radius, int version)? + nearbyBeacon, + TResult? Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version)? + statusFilterBeacon, + TResult? Function(String message)? error, + }) { + return shrimmer?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function()? shrimmer, + TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version)? + allBeacon, + TResult Function(String? message, filters type, List beacons, + double radius, int version)? + nearbyBeacon, + TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version)? + statusFilterBeacon, + TResult Function(String message)? error, + required TResult orElse(), + }) { + if (shrimmer != null) { + return shrimmer(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialGroupState value) initial, + required TResult Function(LoadingGroupState value) loading, + required TResult Function(ShrimmerGroupState value) shrimmer, + required TResult Function(AllBeaconGroupState value) allBeacon, + required TResult Function(NearbyBeaconGroupState value) nearbyBeacon, + required TResult Function(StatusFilterBeaconGroupState value) + statusFilterBeacon, + required TResult Function(ErrorGroupState value) error, + }) { + return shrimmer(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialGroupState value)? initial, + TResult? Function(LoadingGroupState value)? loading, + TResult? Function(ShrimmerGroupState value)? shrimmer, + TResult? Function(AllBeaconGroupState value)? allBeacon, + TResult? Function(NearbyBeaconGroupState value)? nearbyBeacon, + TResult? Function(StatusFilterBeaconGroupState value)? statusFilterBeacon, + TResult? Function(ErrorGroupState value)? error, + }) { + return shrimmer?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialGroupState value)? initial, + TResult Function(LoadingGroupState value)? loading, + TResult Function(ShrimmerGroupState value)? shrimmer, + TResult Function(AllBeaconGroupState value)? allBeacon, + TResult Function(NearbyBeaconGroupState value)? nearbyBeacon, + TResult Function(StatusFilterBeaconGroupState value)? statusFilterBeacon, + TResult Function(ErrorGroupState value)? error, + required TResult orElse(), + }) { + if (shrimmer != null) { + return shrimmer(this); + } + return orElse(); + } +} + +abstract class ShrimmerGroupState implements GroupState { + const factory ShrimmerGroupState() = _$ShrimmerGroupStateImpl; +} + +/// @nodoc +abstract class _$$AllBeaconGroupStateImplCopyWith<$Res> { + factory _$$AllBeaconGroupStateImplCopyWith(_$AllBeaconGroupStateImpl value, + $Res Function(_$AllBeaconGroupStateImpl) then) = + __$$AllBeaconGroupStateImplCopyWithImpl<$Res>; + @useResult + $Res call( + {bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version}); +} + +/// @nodoc +class __$$AllBeaconGroupStateImplCopyWithImpl<$Res> + extends _$GroupStateCopyWithImpl<$Res, _$AllBeaconGroupStateImpl> + implements _$$AllBeaconGroupStateImplCopyWith<$Res> { + __$$AllBeaconGroupStateImplCopyWithImpl(_$AllBeaconGroupStateImpl _value, + $Res Function(_$AllBeaconGroupStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? isLoadingMore = null, + Object? isCompletelyFetched = null, + Object? message = freezed, + Object? type = null, + Object? beacons = null, + Object? version = null, + }) { + return _then(_$AllBeaconGroupStateImpl( + isLoadingMore: null == isLoadingMore + ? _value.isLoadingMore + : isLoadingMore // ignore: cast_nullable_to_non_nullable + as bool, + isCompletelyFetched: null == isCompletelyFetched + ? _value.isCompletelyFetched + : isCompletelyFetched // ignore: cast_nullable_to_non_nullable + as bool, + message: freezed == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String?, + type: null == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as filters, + beacons: null == beacons + ? _value._beacons + : beacons // ignore: cast_nullable_to_non_nullable + as List, + version: null == version + ? _value.version + : version // ignore: cast_nullable_to_non_nullable + as int, + )); + } +} + +/// @nodoc + +class _$AllBeaconGroupStateImpl implements AllBeaconGroupState { + const _$AllBeaconGroupStateImpl( + {this.isLoadingMore = false, + this.isCompletelyFetched = false, + this.message, + this.type = filters.ALL, + required final List beacons, + this.version = 0}) + : _beacons = beacons; + + @override + @JsonKey() + final bool isLoadingMore; + @override + @JsonKey() + final bool isCompletelyFetched; + @override + final String? message; + @override + @JsonKey() + final filters type; + final List _beacons; + @override + List get beacons { + if (_beacons is EqualUnmodifiableListView) return _beacons; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_beacons); + } + + @override + @JsonKey() + final int version; + + @override + String toString() { + return 'GroupState.allBeacon(isLoadingMore: $isLoadingMore, isCompletelyFetched: $isCompletelyFetched, message: $message, type: $type, beacons: $beacons, version: $version)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$AllBeaconGroupStateImpl && + (identical(other.isLoadingMore, isLoadingMore) || + other.isLoadingMore == isLoadingMore) && + (identical(other.isCompletelyFetched, isCompletelyFetched) || + other.isCompletelyFetched == isCompletelyFetched) && + (identical(other.message, message) || other.message == message) && + (identical(other.type, type) || other.type == type) && + const DeepCollectionEquality().equals(other._beacons, _beacons) && + (identical(other.version, version) || other.version == version)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + isLoadingMore, + isCompletelyFetched, + message, + type, + const DeepCollectionEquality().hash(_beacons), + version); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$AllBeaconGroupStateImplCopyWith<_$AllBeaconGroupStateImpl> get copyWith => + __$$AllBeaconGroupStateImplCopyWithImpl<_$AllBeaconGroupStateImpl>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function() shrimmer, + required TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version) + allBeacon, + required TResult Function(String? message, filters type, + List beacons, double radius, int version) + nearbyBeacon, + required TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version) + statusFilterBeacon, + required TResult Function(String message) error, + }) { + return allBeacon( + isLoadingMore, isCompletelyFetched, message, type, beacons, version); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function()? shrimmer, + TResult? Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version)? + allBeacon, + TResult? Function(String? message, filters type, List beacons, + double radius, int version)? + nearbyBeacon, + TResult? Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version)? + statusFilterBeacon, + TResult? Function(String message)? error, + }) { + return allBeacon?.call( + isLoadingMore, isCompletelyFetched, message, type, beacons, version); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function()? shrimmer, + TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version)? + allBeacon, + TResult Function(String? message, filters type, List beacons, + double radius, int version)? + nearbyBeacon, + TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version)? + statusFilterBeacon, + TResult Function(String message)? error, + required TResult orElse(), + }) { + if (allBeacon != null) { + return allBeacon( + isLoadingMore, isCompletelyFetched, message, type, beacons, version); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialGroupState value) initial, + required TResult Function(LoadingGroupState value) loading, + required TResult Function(ShrimmerGroupState value) shrimmer, + required TResult Function(AllBeaconGroupState value) allBeacon, + required TResult Function(NearbyBeaconGroupState value) nearbyBeacon, + required TResult Function(StatusFilterBeaconGroupState value) + statusFilterBeacon, + required TResult Function(ErrorGroupState value) error, + }) { + return allBeacon(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialGroupState value)? initial, + TResult? Function(LoadingGroupState value)? loading, + TResult? Function(ShrimmerGroupState value)? shrimmer, + TResult? Function(AllBeaconGroupState value)? allBeacon, + TResult? Function(NearbyBeaconGroupState value)? nearbyBeacon, + TResult? Function(StatusFilterBeaconGroupState value)? statusFilterBeacon, + TResult? Function(ErrorGroupState value)? error, + }) { + return allBeacon?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialGroupState value)? initial, + TResult Function(LoadingGroupState value)? loading, + TResult Function(ShrimmerGroupState value)? shrimmer, + TResult Function(AllBeaconGroupState value)? allBeacon, + TResult Function(NearbyBeaconGroupState value)? nearbyBeacon, + TResult Function(StatusFilterBeaconGroupState value)? statusFilterBeacon, + TResult Function(ErrorGroupState value)? error, + required TResult orElse(), + }) { + if (allBeacon != null) { + return allBeacon(this); + } + return orElse(); + } +} + +abstract class AllBeaconGroupState implements GroupState { + const factory AllBeaconGroupState( + {final bool isLoadingMore, + final bool isCompletelyFetched, + final String? message, + final filters type, + required final List beacons, + final int version}) = _$AllBeaconGroupStateImpl; + + bool get isLoadingMore; + bool get isCompletelyFetched; + String? get message; + filters get type; + List get beacons; + int get version; + @JsonKey(ignore: true) + _$$AllBeaconGroupStateImplCopyWith<_$AllBeaconGroupStateImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$NearbyBeaconGroupStateImplCopyWith<$Res> { + factory _$$NearbyBeaconGroupStateImplCopyWith( + _$NearbyBeaconGroupStateImpl value, + $Res Function(_$NearbyBeaconGroupStateImpl) then) = + __$$NearbyBeaconGroupStateImplCopyWithImpl<$Res>; + @useResult + $Res call( + {String? message, + filters type, + List beacons, + double radius, + int version}); +} + +/// @nodoc +class __$$NearbyBeaconGroupStateImplCopyWithImpl<$Res> + extends _$GroupStateCopyWithImpl<$Res, _$NearbyBeaconGroupStateImpl> + implements _$$NearbyBeaconGroupStateImplCopyWith<$Res> { + __$$NearbyBeaconGroupStateImplCopyWithImpl( + _$NearbyBeaconGroupStateImpl _value, + $Res Function(_$NearbyBeaconGroupStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? message = freezed, + Object? type = null, + Object? beacons = null, + Object? radius = null, + Object? version = null, + }) { + return _then(_$NearbyBeaconGroupStateImpl( + message: freezed == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String?, + type: null == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as filters, + beacons: null == beacons + ? _value._beacons + : beacons // ignore: cast_nullable_to_non_nullable + as List, + radius: null == radius + ? _value.radius + : radius // ignore: cast_nullable_to_non_nullable + as double, + version: null == version + ? _value.version + : version // ignore: cast_nullable_to_non_nullable + as int, + )); + } +} + +/// @nodoc + +class _$NearbyBeaconGroupStateImpl implements NearbyBeaconGroupState { + const _$NearbyBeaconGroupStateImpl( + {this.message, + this.type = filters.NEARBY, + required final List beacons, + this.radius = 1000.0, + this.version = 0}) + : _beacons = beacons; + + @override + final String? message; + @override + @JsonKey() + final filters type; + final List _beacons; + @override + List get beacons { + if (_beacons is EqualUnmodifiableListView) return _beacons; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_beacons); + } + + @override + @JsonKey() + final double radius; + @override + @JsonKey() + final int version; + + @override + String toString() { + return 'GroupState.nearbyBeacon(message: $message, type: $type, beacons: $beacons, radius: $radius, version: $version)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$NearbyBeaconGroupStateImpl && + (identical(other.message, message) || other.message == message) && + (identical(other.type, type) || other.type == type) && + const DeepCollectionEquality().equals(other._beacons, _beacons) && + (identical(other.radius, radius) || other.radius == radius) && + (identical(other.version, version) || other.version == version)); + } + + @override + int get hashCode => Object.hash(runtimeType, message, type, + const DeepCollectionEquality().hash(_beacons), radius, version); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$NearbyBeaconGroupStateImplCopyWith<_$NearbyBeaconGroupStateImpl> + get copyWith => __$$NearbyBeaconGroupStateImplCopyWithImpl< + _$NearbyBeaconGroupStateImpl>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function() shrimmer, + required TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version) + allBeacon, + required TResult Function(String? message, filters type, + List beacons, double radius, int version) + nearbyBeacon, + required TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version) + statusFilterBeacon, + required TResult Function(String message) error, + }) { + return nearbyBeacon(message, type, beacons, radius, version); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function()? shrimmer, + TResult? Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version)? + allBeacon, + TResult? Function(String? message, filters type, List beacons, + double radius, int version)? + nearbyBeacon, + TResult? Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version)? + statusFilterBeacon, + TResult? Function(String message)? error, + }) { + return nearbyBeacon?.call(message, type, beacons, radius, version); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function()? shrimmer, + TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version)? + allBeacon, + TResult Function(String? message, filters type, List beacons, + double radius, int version)? + nearbyBeacon, + TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version)? + statusFilterBeacon, + TResult Function(String message)? error, + required TResult orElse(), + }) { + if (nearbyBeacon != null) { + return nearbyBeacon(message, type, beacons, radius, version); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialGroupState value) initial, + required TResult Function(LoadingGroupState value) loading, + required TResult Function(ShrimmerGroupState value) shrimmer, + required TResult Function(AllBeaconGroupState value) allBeacon, + required TResult Function(NearbyBeaconGroupState value) nearbyBeacon, + required TResult Function(StatusFilterBeaconGroupState value) + statusFilterBeacon, + required TResult Function(ErrorGroupState value) error, + }) { + return nearbyBeacon(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialGroupState value)? initial, + TResult? Function(LoadingGroupState value)? loading, + TResult? Function(ShrimmerGroupState value)? shrimmer, + TResult? Function(AllBeaconGroupState value)? allBeacon, + TResult? Function(NearbyBeaconGroupState value)? nearbyBeacon, + TResult? Function(StatusFilterBeaconGroupState value)? statusFilterBeacon, + TResult? Function(ErrorGroupState value)? error, + }) { + return nearbyBeacon?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialGroupState value)? initial, + TResult Function(LoadingGroupState value)? loading, + TResult Function(ShrimmerGroupState value)? shrimmer, + TResult Function(AllBeaconGroupState value)? allBeacon, + TResult Function(NearbyBeaconGroupState value)? nearbyBeacon, + TResult Function(StatusFilterBeaconGroupState value)? statusFilterBeacon, + TResult Function(ErrorGroupState value)? error, + required TResult orElse(), + }) { + if (nearbyBeacon != null) { + return nearbyBeacon(this); + } + return orElse(); + } +} + +abstract class NearbyBeaconGroupState implements GroupState { + const factory NearbyBeaconGroupState( + {final String? message, + final filters type, + required final List beacons, + final double radius, + final int version}) = _$NearbyBeaconGroupStateImpl; + + String? get message; + filters get type; + List get beacons; + double get radius; + int get version; + @JsonKey(ignore: true) + _$$NearbyBeaconGroupStateImplCopyWith<_$NearbyBeaconGroupStateImpl> + get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$StatusFilterBeaconGroupStateImplCopyWith<$Res> { + factory _$$StatusFilterBeaconGroupStateImplCopyWith( + _$StatusFilterBeaconGroupStateImpl value, + $Res Function(_$StatusFilterBeaconGroupStateImpl) then) = + __$$StatusFilterBeaconGroupStateImplCopyWithImpl<$Res>; + @useResult + $Res call( + {bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version}); +} + +/// @nodoc +class __$$StatusFilterBeaconGroupStateImplCopyWithImpl<$Res> + extends _$GroupStateCopyWithImpl<$Res, _$StatusFilterBeaconGroupStateImpl> + implements _$$StatusFilterBeaconGroupStateImplCopyWith<$Res> { + __$$StatusFilterBeaconGroupStateImplCopyWithImpl( + _$StatusFilterBeaconGroupStateImpl _value, + $Res Function(_$StatusFilterBeaconGroupStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? isLoadingMore = null, + Object? isCompletelyFetched = null, + Object? message = freezed, + Object? type = freezed, + Object? beacons = null, + Object? version = null, + }) { + return _then(_$StatusFilterBeaconGroupStateImpl( + isLoadingMore: null == isLoadingMore + ? _value.isLoadingMore + : isLoadingMore // ignore: cast_nullable_to_non_nullable + as bool, + isCompletelyFetched: null == isCompletelyFetched + ? _value.isCompletelyFetched + : isCompletelyFetched // ignore: cast_nullable_to_non_nullable + as bool, + message: freezed == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String?, + type: freezed == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as filters?, + beacons: null == beacons + ? _value._beacons + : beacons // ignore: cast_nullable_to_non_nullable + as List, + version: null == version + ? _value.version + : version // ignore: cast_nullable_to_non_nullable + as int, + )); + } +} + +/// @nodoc + +class _$StatusFilterBeaconGroupStateImpl + implements StatusFilterBeaconGroupState { + const _$StatusFilterBeaconGroupStateImpl( + {this.isLoadingMore = false, + this.isCompletelyFetched = false, + this.message, + this.type, + required final List beacons, + this.version = 0}) + : _beacons = beacons; + + @override + @JsonKey() + final bool isLoadingMore; + @override + @JsonKey() + final bool isCompletelyFetched; + @override + final String? message; + @override + final filters? type; + final List _beacons; + @override + List get beacons { + if (_beacons is EqualUnmodifiableListView) return _beacons; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_beacons); + } + + @override + @JsonKey() + final int version; + + @override + String toString() { + return 'GroupState.statusFilterBeacon(isLoadingMore: $isLoadingMore, isCompletelyFetched: $isCompletelyFetched, message: $message, type: $type, beacons: $beacons, version: $version)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$StatusFilterBeaconGroupStateImpl && + (identical(other.isLoadingMore, isLoadingMore) || + other.isLoadingMore == isLoadingMore) && + (identical(other.isCompletelyFetched, isCompletelyFetched) || + other.isCompletelyFetched == isCompletelyFetched) && + (identical(other.message, message) || other.message == message) && + (identical(other.type, type) || other.type == type) && + const DeepCollectionEquality().equals(other._beacons, _beacons) && + (identical(other.version, version) || other.version == version)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + isLoadingMore, + isCompletelyFetched, + message, + type, + const DeepCollectionEquality().hash(_beacons), + version); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$StatusFilterBeaconGroupStateImplCopyWith< + _$StatusFilterBeaconGroupStateImpl> + get copyWith => __$$StatusFilterBeaconGroupStateImplCopyWithImpl< + _$StatusFilterBeaconGroupStateImpl>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function() shrimmer, + required TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version) + allBeacon, + required TResult Function(String? message, filters type, + List beacons, double radius, int version) + nearbyBeacon, + required TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version) + statusFilterBeacon, + required TResult Function(String message) error, + }) { + return statusFilterBeacon( + isLoadingMore, isCompletelyFetched, message, type, beacons, version); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function()? shrimmer, + TResult? Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version)? + allBeacon, + TResult? Function(String? message, filters type, List beacons, + double radius, int version)? + nearbyBeacon, + TResult? Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version)? + statusFilterBeacon, + TResult? Function(String message)? error, + }) { + return statusFilterBeacon?.call( + isLoadingMore, isCompletelyFetched, message, type, beacons, version); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function()? shrimmer, + TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version)? + allBeacon, + TResult Function(String? message, filters type, List beacons, + double radius, int version)? + nearbyBeacon, + TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version)? + statusFilterBeacon, + TResult Function(String message)? error, + required TResult orElse(), + }) { + if (statusFilterBeacon != null) { + return statusFilterBeacon( + isLoadingMore, isCompletelyFetched, message, type, beacons, version); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialGroupState value) initial, + required TResult Function(LoadingGroupState value) loading, + required TResult Function(ShrimmerGroupState value) shrimmer, + required TResult Function(AllBeaconGroupState value) allBeacon, + required TResult Function(NearbyBeaconGroupState value) nearbyBeacon, + required TResult Function(StatusFilterBeaconGroupState value) + statusFilterBeacon, + required TResult Function(ErrorGroupState value) error, + }) { + return statusFilterBeacon(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialGroupState value)? initial, + TResult? Function(LoadingGroupState value)? loading, + TResult? Function(ShrimmerGroupState value)? shrimmer, + TResult? Function(AllBeaconGroupState value)? allBeacon, + TResult? Function(NearbyBeaconGroupState value)? nearbyBeacon, + TResult? Function(StatusFilterBeaconGroupState value)? statusFilterBeacon, + TResult? Function(ErrorGroupState value)? error, + }) { + return statusFilterBeacon?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialGroupState value)? initial, + TResult Function(LoadingGroupState value)? loading, + TResult Function(ShrimmerGroupState value)? shrimmer, + TResult Function(AllBeaconGroupState value)? allBeacon, + TResult Function(NearbyBeaconGroupState value)? nearbyBeacon, + TResult Function(StatusFilterBeaconGroupState value)? statusFilterBeacon, + TResult Function(ErrorGroupState value)? error, + required TResult orElse(), + }) { + if (statusFilterBeacon != null) { + return statusFilterBeacon(this); + } + return orElse(); + } +} + +abstract class StatusFilterBeaconGroupState implements GroupState { + const factory StatusFilterBeaconGroupState( + {final bool isLoadingMore, + final bool isCompletelyFetched, + final String? message, + final filters? type, + required final List beacons, + final int version}) = _$StatusFilterBeaconGroupStateImpl; + + bool get isLoadingMore; + bool get isCompletelyFetched; + String? get message; + filters? get type; + List get beacons; + int get version; + @JsonKey(ignore: true) + _$$StatusFilterBeaconGroupStateImplCopyWith< + _$StatusFilterBeaconGroupStateImpl> + get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$ErrorGroupStateImplCopyWith<$Res> { + factory _$$ErrorGroupStateImplCopyWith(_$ErrorGroupStateImpl value, + $Res Function(_$ErrorGroupStateImpl) then) = + __$$ErrorGroupStateImplCopyWithImpl<$Res>; + @useResult + $Res call({String message}); +} + +/// @nodoc +class __$$ErrorGroupStateImplCopyWithImpl<$Res> + extends _$GroupStateCopyWithImpl<$Res, _$ErrorGroupStateImpl> + implements _$$ErrorGroupStateImplCopyWith<$Res> { + __$$ErrorGroupStateImplCopyWithImpl( + _$ErrorGroupStateImpl _value, $Res Function(_$ErrorGroupStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? message = null, + }) { + return _then(_$ErrorGroupStateImpl( + message: null == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc + +class _$ErrorGroupStateImpl implements ErrorGroupState { + const _$ErrorGroupStateImpl({required this.message}); + + @override + final String message; + + @override + String toString() { + return 'GroupState.error(message: $message)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ErrorGroupStateImpl && + (identical(other.message, message) || other.message == message)); + } + + @override + int get hashCode => Object.hash(runtimeType, message); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$ErrorGroupStateImplCopyWith<_$ErrorGroupStateImpl> get copyWith => + __$$ErrorGroupStateImplCopyWithImpl<_$ErrorGroupStateImpl>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function() shrimmer, + required TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version) + allBeacon, + required TResult Function(String? message, filters type, + List beacons, double radius, int version) + nearbyBeacon, + required TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version) + statusFilterBeacon, + required TResult Function(String message) error, + }) { + return error(message); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function()? shrimmer, + TResult? Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version)? + allBeacon, + TResult? Function(String? message, filters type, List beacons, + double radius, int version)? + nearbyBeacon, + TResult? Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version)? + statusFilterBeacon, + TResult? Function(String message)? error, + }) { + return error?.call(message); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function()? shrimmer, + TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters type, + List beacons, + int version)? + allBeacon, + TResult Function(String? message, filters type, List beacons, + double radius, int version)? + nearbyBeacon, + TResult Function( + bool isLoadingMore, + bool isCompletelyFetched, + String? message, + filters? type, + List beacons, + int version)? + statusFilterBeacon, + TResult Function(String message)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(message); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialGroupState value) initial, + required TResult Function(LoadingGroupState value) loading, + required TResult Function(ShrimmerGroupState value) shrimmer, + required TResult Function(AllBeaconGroupState value) allBeacon, + required TResult Function(NearbyBeaconGroupState value) nearbyBeacon, + required TResult Function(StatusFilterBeaconGroupState value) + statusFilterBeacon, + required TResult Function(ErrorGroupState value) error, + }) { + return error(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialGroupState value)? initial, + TResult? Function(LoadingGroupState value)? loading, + TResult? Function(ShrimmerGroupState value)? shrimmer, + TResult? Function(AllBeaconGroupState value)? allBeacon, + TResult? Function(NearbyBeaconGroupState value)? nearbyBeacon, + TResult? Function(StatusFilterBeaconGroupState value)? statusFilterBeacon, + TResult? Function(ErrorGroupState value)? error, + }) { + return error?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialGroupState value)? initial, + TResult Function(LoadingGroupState value)? loading, + TResult Function(ShrimmerGroupState value)? shrimmer, + TResult Function(AllBeaconGroupState value)? allBeacon, + TResult Function(NearbyBeaconGroupState value)? nearbyBeacon, + TResult Function(StatusFilterBeaconGroupState value)? statusFilterBeacon, + TResult Function(ErrorGroupState value)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(this); + } + return orElse(); + } +} + +abstract class ErrorGroupState implements GroupState { + const factory ErrorGroupState({required final String message}) = + _$ErrorGroupStateImpl; + + String get message; + @JsonKey(ignore: true) + _$$ErrorGroupStateImplCopyWith<_$ErrorGroupStateImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/presentation/group/cubit/members_cubit/members_cubit.dart b/lib/presentation/group/cubit/members_cubit/members_cubit.dart new file mode 100644 index 00000000..76cb2ced --- /dev/null +++ b/lib/presentation/group/cubit/members_cubit/members_cubit.dart @@ -0,0 +1,67 @@ +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/domain/entities/group/group_entity.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:beacon/domain/usecase/group_usecase.dart'; +import 'package:beacon/presentation/home/home_cubit/home_cubit.dart'; +import 'package:beacon/presentation/group/cubit/members_cubit/members_state.dart'; +import 'package:beacon/locator.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class MembersCubit extends Cubit { + final GroupUseCase _groupUseCase; + + MembersCubit._internal(this._groupUseCase) : super(InitialMemberState()); + + static MembersCubit? _instance; + + factory MembersCubit(GroupUseCase _groupUseCase) { + return _instance ?? MembersCubit._internal(_groupUseCase); + } + + String? _groupId; + List _members = []; + + init(GroupEntity group) { + _groupId = group.id!; + _members.add(group.leader!); + group.members!.forEach((member) { + _members.add(member!); + }); + } + + clear() { + _groupId = null; + _members.clear(); + } + + loadMembers() { + emit(LoadedMemberState(members: _members)); + } + + void removeMember(String memberId) async { + emit(LoadingMemberState()); + final dataState = await _groupUseCase.removeMember(_groupId!, memberId); + + if (dataState is DataSuccess) { + locator().removeMember(_groupId!, dataState.data!); + var updatedList = List.from(_members) + ..removeWhere((member) => member.id == memberId); + _members = updatedList; + + emit(LoadedMemberState( + members: updatedList, + message: + '${dataState.data!.name} is no longer the member of group!')); + } else { + emit(LoadedMemberState(members: _members, message: dataState.error)); + } + } + + void addMember(UserEntity member) { + _members.add(member); + + emit(LoadedMemberState( + members: List.from(_members), + message: '${member.name} joined the group!')); + } +} diff --git a/lib/presentation/group/cubit/members_cubit/members_state.dart b/lib/presentation/group/cubit/members_cubit/members_state.dart new file mode 100644 index 00000000..178e0370 --- /dev/null +++ b/lib/presentation/group/cubit/members_cubit/members_state.dart @@ -0,0 +1,11 @@ +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +part 'members_state.freezed.dart'; + +@freezed +class MembersState with _$MembersState { + factory MembersState.initial() = InitialMemberState; + factory MembersState.loading() = LoadingMemberState; + factory MembersState.reload({List? members, String? message}) = + LoadedMemberState; +} diff --git a/lib/presentation/group/cubit/members_cubit/members_state.freezed.dart b/lib/presentation/group/cubit/members_cubit/members_state.freezed.dart new file mode 100644 index 00000000..8f649350 --- /dev/null +++ b/lib/presentation/group/cubit/members_cubit/members_state.freezed.dart @@ -0,0 +1,462 @@ +// 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 'members_state.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 _$MembersState { + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(List? members, String? message) + reload, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(List? members, String? message)? reload, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function(List? members, String? message)? reload, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(InitialMemberState value) initial, + required TResult Function(LoadingMemberState value) loading, + required TResult Function(LoadedMemberState value) reload, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialMemberState value)? initial, + TResult? Function(LoadingMemberState value)? loading, + TResult? Function(LoadedMemberState value)? reload, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialMemberState value)? initial, + TResult Function(LoadingMemberState value)? loading, + TResult Function(LoadedMemberState value)? reload, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $MembersStateCopyWith<$Res> { + factory $MembersStateCopyWith( + MembersState value, $Res Function(MembersState) then) = + _$MembersStateCopyWithImpl<$Res, MembersState>; +} + +/// @nodoc +class _$MembersStateCopyWithImpl<$Res, $Val extends MembersState> + implements $MembersStateCopyWith<$Res> { + _$MembersStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; +} + +/// @nodoc +abstract class _$$InitialMemberStateImplCopyWith<$Res> { + factory _$$InitialMemberStateImplCopyWith(_$InitialMemberStateImpl value, + $Res Function(_$InitialMemberStateImpl) then) = + __$$InitialMemberStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$InitialMemberStateImplCopyWithImpl<$Res> + extends _$MembersStateCopyWithImpl<$Res, _$InitialMemberStateImpl> + implements _$$InitialMemberStateImplCopyWith<$Res> { + __$$InitialMemberStateImplCopyWithImpl(_$InitialMemberStateImpl _value, + $Res Function(_$InitialMemberStateImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$InitialMemberStateImpl implements InitialMemberState { + _$InitialMemberStateImpl(); + + @override + String toString() { + return 'MembersState.initial()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$InitialMemberStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(List? members, String? message) + reload, + }) { + return initial(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(List? members, String? message)? reload, + }) { + return initial?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function(List? members, String? message)? reload, + required TResult orElse(), + }) { + if (initial != null) { + return initial(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialMemberState value) initial, + required TResult Function(LoadingMemberState value) loading, + required TResult Function(LoadedMemberState value) reload, + }) { + return initial(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialMemberState value)? initial, + TResult? Function(LoadingMemberState value)? loading, + TResult? Function(LoadedMemberState value)? reload, + }) { + return initial?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialMemberState value)? initial, + TResult Function(LoadingMemberState value)? loading, + TResult Function(LoadedMemberState value)? reload, + required TResult orElse(), + }) { + if (initial != null) { + return initial(this); + } + return orElse(); + } +} + +abstract class InitialMemberState implements MembersState { + factory InitialMemberState() = _$InitialMemberStateImpl; +} + +/// @nodoc +abstract class _$$LoadingMemberStateImplCopyWith<$Res> { + factory _$$LoadingMemberStateImplCopyWith(_$LoadingMemberStateImpl value, + $Res Function(_$LoadingMemberStateImpl) then) = + __$$LoadingMemberStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$LoadingMemberStateImplCopyWithImpl<$Res> + extends _$MembersStateCopyWithImpl<$Res, _$LoadingMemberStateImpl> + implements _$$LoadingMemberStateImplCopyWith<$Res> { + __$$LoadingMemberStateImplCopyWithImpl(_$LoadingMemberStateImpl _value, + $Res Function(_$LoadingMemberStateImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$LoadingMemberStateImpl implements LoadingMemberState { + _$LoadingMemberStateImpl(); + + @override + String toString() { + return 'MembersState.loading()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$LoadingMemberStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(List? members, String? message) + reload, + }) { + return loading(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(List? members, String? message)? reload, + }) { + return loading?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function(List? members, String? message)? reload, + required TResult orElse(), + }) { + if (loading != null) { + return loading(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialMemberState value) initial, + required TResult Function(LoadingMemberState value) loading, + required TResult Function(LoadedMemberState value) reload, + }) { + return loading(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialMemberState value)? initial, + TResult? Function(LoadingMemberState value)? loading, + TResult? Function(LoadedMemberState value)? reload, + }) { + return loading?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialMemberState value)? initial, + TResult Function(LoadingMemberState value)? loading, + TResult Function(LoadedMemberState value)? reload, + required TResult orElse(), + }) { + if (loading != null) { + return loading(this); + } + return orElse(); + } +} + +abstract class LoadingMemberState implements MembersState { + factory LoadingMemberState() = _$LoadingMemberStateImpl; +} + +/// @nodoc +abstract class _$$LoadedMemberStateImplCopyWith<$Res> { + factory _$$LoadedMemberStateImplCopyWith(_$LoadedMemberStateImpl value, + $Res Function(_$LoadedMemberStateImpl) then) = + __$$LoadedMemberStateImplCopyWithImpl<$Res>; + @useResult + $Res call({List? members, String? message}); +} + +/// @nodoc +class __$$LoadedMemberStateImplCopyWithImpl<$Res> + extends _$MembersStateCopyWithImpl<$Res, _$LoadedMemberStateImpl> + implements _$$LoadedMemberStateImplCopyWith<$Res> { + __$$LoadedMemberStateImplCopyWithImpl(_$LoadedMemberStateImpl _value, + $Res Function(_$LoadedMemberStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? members = freezed, + Object? message = freezed, + }) { + return _then(_$LoadedMemberStateImpl( + members: freezed == members + ? _value._members + : members // ignore: cast_nullable_to_non_nullable + as List?, + message: freezed == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc + +class _$LoadedMemberStateImpl implements LoadedMemberState { + _$LoadedMemberStateImpl({final List? members, this.message}) + : _members = members; + + final List? _members; + @override + List? get members { + final value = _members; + if (value == null) return null; + if (_members is EqualUnmodifiableListView) return _members; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + @override + final String? message; + + @override + String toString() { + return 'MembersState.reload(members: $members, message: $message)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$LoadedMemberStateImpl && + const DeepCollectionEquality().equals(other._members, _members) && + (identical(other.message, message) || other.message == message)); + } + + @override + int get hashCode => Object.hash( + runtimeType, const DeepCollectionEquality().hash(_members), message); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$LoadedMemberStateImplCopyWith<_$LoadedMemberStateImpl> get copyWith => + __$$LoadedMemberStateImplCopyWithImpl<_$LoadedMemberStateImpl>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(List? members, String? message) + reload, + }) { + return reload(members, message); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(List? members, String? message)? reload, + }) { + return reload?.call(members, message); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function(List? members, String? message)? reload, + required TResult orElse(), + }) { + if (reload != null) { + return reload(members, message); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialMemberState value) initial, + required TResult Function(LoadingMemberState value) loading, + required TResult Function(LoadedMemberState value) reload, + }) { + return reload(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialMemberState value)? initial, + TResult? Function(LoadingMemberState value)? loading, + TResult? Function(LoadedMemberState value)? reload, + }) { + return reload?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialMemberState value)? initial, + TResult Function(LoadingMemberState value)? loading, + TResult Function(LoadedMemberState value)? reload, + required TResult orElse(), + }) { + if (reload != null) { + return reload(this); + } + return orElse(); + } +} + +abstract class LoadedMemberState implements MembersState { + factory LoadedMemberState( + {final List? members, + final String? message}) = _$LoadedMemberStateImpl; + + List? get members; + String? get message; + @JsonKey(ignore: true) + _$$LoadedMemberStateImplCopyWith<_$LoadedMemberStateImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/presentation/group/group_screen.dart b/lib/presentation/group/group_screen.dart new file mode 100644 index 00000000..43b7e4c5 --- /dev/null +++ b/lib/presentation/group/group_screen.dart @@ -0,0 +1,414 @@ +import 'dart:developer'; + +import 'package:auto_route/auto_route.dart'; +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/domain/entities/group/group_entity.dart'; +import 'package:beacon/presentation/group/cubit/group_cubit/group_cubit.dart'; +import 'package:beacon/presentation/group/cubit/group_cubit/group_state.dart'; +import 'package:beacon/presentation/group/cubit/members_cubit/members_cubit.dart'; +import 'package:beacon/presentation/group/widgets/create_join_dialog.dart'; +import 'package:beacon/presentation/group/widgets/beacon_card.dart'; +import 'package:beacon/presentation/group/widgets/group_widgets.dart'; +import 'package:beacon/presentation/widgets/shimmer.dart'; +import 'package:beacon/presentation/widgets/hike_button.dart'; +import 'package:beacon/presentation/widgets/loading_screen.dart'; +import 'package:beacon/presentation/widgets/shape_painter.dart'; +import 'package:beacon/locator.dart'; +import 'package:beacon/core/utils/constants.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; +import 'package:gap/gap.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; + +@RoutePage() +class GroupScreen extends StatefulWidget { + final GroupEntity group; + + GroupScreen(this.group); + + @override + _GroupScreenState createState() => _GroupScreenState(); +} + +class _GroupScreenState extends State + with TickerProviderStateMixin { + late List fetchingUserBeacons; + late List fetchingNearbyBeacons; + late GroupCubit _groupCubit; + late MembersCubit _membersCubit; + late ScrollController _scrollController; + + @override + void initState() { + super.initState(); + _scrollController = ScrollController(); + _scrollController.addListener(_listener); + _groupCubit = BlocProvider.of(context); + _membersCubit = BlocProvider.of(context); + _groupCubit.init(widget.group); + _groupCubit.allHikes(widget.group.id!); + _membersCubit.init(widget.group); + } + + void _listener() { + if (_scrollController.position.pixels == + _scrollController.position.maxScrollExtent) { + final state = _groupCubit.state; + if (state is AllBeaconGroupState && !state.isCompletelyFetched) { + _groupCubit.allHikes(widget.group.id!); + } + } + } + + @override + void dispose() { + _groupCubit.clear(); + _membersCubit.clear(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + TabController tabController = TabController(length: 1, vsync: this); + + return Scaffold( + resizeToAvoidBottomInset: false, + body: SafeArea( + child: BlocConsumer( + listener: (context, state) { + if (state is AllBeaconGroupState && state.message != null) { + utils.showSnackBar(state.message!, context); + } + }, + builder: (context, state) { + return ModalProgressHUD( + progressIndicator: const LoadingScreen(), + inAsyncCall: state is LoadingGroupState, + child: Stack( + children: [ + CustomPaint( + size: Size(100.w, 100.h - 200), + painter: ShapePainter(), + ), + _buildGroupName(), + Align( + alignment: Alignment(0.9, -0.70), + child: GroupWidgetUtils.filterBeacons( + context, widget.group.id!, _groupCubit), + ), + Align( + alignment: Alignment(0.5, -0.70), + child: GroupWidgetUtils.membersWidget(context), + ), + _buildJoinCreateButton(), + Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + height: MediaQuery.of(context).size.height * 0.565, + margin: EdgeInsets.only(top: 20), + decoration: BoxDecoration( + color: kLightBlue, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(50.0), + topRight: Radius.circular(50.0), + ), + ), + child: Column( + children: [ + TabBar( + indicatorSize: TabBarIndicatorSize.tab, + indicatorColor: kBlue, + labelColor: kBlack, + tabs: [ + _buildTab(state), + ], + controller: tabController, + ), + Expanded( + child: TabBarView( + controller: tabController, + children: [ + _groupBeacons(state), + ], + ), + ) + ], + ), + ) + ], + ), + ], + ), + ); + }, + ), + ), + ); + } + + Widget _buildJoinCreateButton() { + return Padding( + padding: EdgeInsets.fromLTRB(4.w, 23.h, 4.w, 5), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: 45.w, + child: HikeButton( + buttonWidth: homebwidth, + buttonHeight: homebheight - 2, + text: 'Create Hike', + textColor: Colors.white, + borderColor: Colors.white, + buttonColor: kYellow, + onTap: () { + CreateJoinBeaconDialog.createHikeDialog( + context, widget.group.id!); + }, + ), + ), + SizedBox(width: 1.w), + Container( + width: 45.w, + child: HikeButton( + buttonWidth: homebwidth, + buttonHeight: homebheight - 2, + text: 'Join a Hike', + textColor: kYellow, + borderColor: kYellow, + buttonColor: Colors.white, + onTap: () async { + CreateJoinBeaconDialog.joinBeaconDialog(context); + }, + ), + ), + ], + ), + ); + } + + Widget _buildTab(GroupState state) { + return Tab( + text: state is AllBeaconGroupState + ? 'All Beacons' + : state is NearbyBeaconGroupState + ? 'Nearby Beacons' + : state is StatusFilterBeaconGroupState + ? '${state.type!.name[0] + state.type!.name.substring(1).toLowerCase()} Beacons' + : 'Loading Beacons..', + ); + } + + Widget _groupBeacons(GroupState state) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 2.0), + child: Builder( + builder: (context) { + if (state is ShrimmerGroupState) { + return Center(child: ShimmerWidget.getPlaceholder()); + } else if (state is AllBeaconGroupState) { + final beacons = state.beacons; + String message = 'You haven\'t joined or created any beacon yet'; + return _buildBeaconsList(beacons, state.isLoadingMore, + state.isCompletelyFetched, message); + } else if (state is NearbyBeaconGroupState) { + final beacons = state.beacons; + String message = + 'No beacons found under ${state.radius.toStringAsFixed(2)} m... radius'; + return _buildBeaconsList(beacons, false, false, message); + } else if (state is StatusFilterBeaconGroupState) { + final beacons = state.beacons; + var type = state.type!.name; + String message = + 'No ${type[0].toUpperCase() + type.substring(1).toLowerCase()} beacons found'; + return _buildBeaconsList(beacons, false, false, message); + } else if (state is ErrorGroupState) { + return _buildErrorWidget(state.message); + } + return _buildErrorWidget('Something went wrong!'); + }, + ), + ); + } + + Widget _buildGroupName() { + return Align( + alignment: Alignment(-0.7, -0.95), + child: Container( + width: MediaQuery.of(context).size.width * 0.6, + child: Text( + 'Welcome to Group ${widget.group.title!}', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 25, color: Colors.white), + ), + ), + ); + } + + Widget _buildBeaconsList(List beacons, bool isLoadingMore, + bool isCompletelyFetched, String message) { + return Container( + alignment: Alignment.center, + child: beacons.isEmpty + ? SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + child: _noBeaconsWidget(message), + ) + : ListView.builder( + controller: _scrollController, + physics: AlwaysScrollableScrollPhysics(), + scrollDirection: Axis.vertical, + itemCount: beacons.length + + (isLoadingMore && !isCompletelyFetched ? 1 : 0), + padding: EdgeInsets.all(8), + itemBuilder: (context, index) { + if (index == beacons.length) { + return LinearProgressIndicator(); + } + return _buildBeaconCard(beacons[index]); + }, + ), + ); + } + + Widget _buildBeaconCard(BeaconEntity beacon) { + return Slidable( + key: Key(beacon.id!.toString()), + startActionPane: ActionPane( + dragDismissible: true, + dismissible: DismissiblePane( + onDismissed: () { + _groupCubit.reloadState(message: 'Beacon deleted'); + }, + confirmDismiss: () async { + bool? value = await GroupWidgetUtils.deleteDialog(context); + if (value == null || !value) { + return false; + } + bool delete = await _groupCubit.deleteBeacon(beacon); + return delete; + }, + ), + motion: ScrollMotion(), + children: [ + SlidableAction( + onPressed: null, + backgroundColor: Color.fromARGB(255, 217, 100, 94), + foregroundColor: Colors.white, + icon: Icons.delete, + label: 'Delete', + ), + ], + ), + endActionPane: ActionPane( + motion: ScrollMotion(), + children: [ + SlidableAction( + flex: 1, + onPressed: (context) { + GroupWidgetUtils.reScheduleHikeDialog(context, beacon); + }, + backgroundColor: Colors.blueGrey, + foregroundColor: Colors.white, + icon: Icons.edit_calendar, + label: 'Reschedule', + ), + ], + ), + child: BeaconCard(beacon: beacon), + ); + } + + Widget _buildErrorWidget(String message) { + return Center( + child: SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + child: Column( + children: [ + Text( + message, + textAlign: TextAlign.center, + style: TextStyle(color: kBlack, fontSize: 20), + ), + Gap(5), + FloatingActionButton( + onPressed: () async { + try { + await locationService.openSettings(); + } catch (e) { + log('error: $e'); + } + }, + child: Icon( + Icons.settings, + color: kBlack, + ), + backgroundColor: kYellow, + ), + Gap(15), + RichText( + text: TextSpan( + style: TextStyle(color: kBlack, fontSize: 20), + children: [ + TextSpan( + text: 'Join', + style: TextStyle(fontWeight: FontWeight.bold)), + TextSpan(text: ' a Hike or '), + TextSpan( + text: 'Create', + style: TextStyle(fontWeight: FontWeight.bold)), + TextSpan(text: ' a new one! '), + ], + ), + ), + SizedBox( + height: 2.h, + ), + ], + ), + ), + ); + } + + Widget _noBeaconsWidget(String message) { + return SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + child: Column( + children: [ + Text( + message, + textAlign: TextAlign.center, + style: TextStyle(color: kBlack, fontSize: 20), + ), + SizedBox( + height: 2.h, + ), + RichText( + text: TextSpan( + style: TextStyle(color: kBlack, fontSize: 20), + children: [ + TextSpan( + text: 'Join', + style: TextStyle(fontWeight: FontWeight.bold)), + TextSpan(text: ' a Hike or '), + TextSpan( + text: 'Create', + style: TextStyle(fontWeight: FontWeight.bold)), + TextSpan(text: ' a new one! '), + ], + ), + ), + SizedBox( + height: 2.h, + ), + ], + ), + ); + } +} diff --git a/lib/presentation/group/widgets/beacon_card.dart b/lib/presentation/group/widgets/beacon_card.dart new file mode 100644 index 00000000..0e330e2f --- /dev/null +++ b/lib/presentation/group/widgets/beacon_card.dart @@ -0,0 +1,317 @@ +import 'dart:async'; +import 'package:auto_route/auto_route.dart'; +import 'package:beacon/core/utils/constants.dart'; +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/locator.dart'; +import 'package:beacon/presentation/group/cubit/group_cubit/group_cubit.dart'; +import 'package:beacon/presentation/hike/widgets/active_beacon.dart'; +import 'package:beacon/presentation/widgets/hike_button.dart'; +import 'package:beacon/presentation/group/widgets/timer.dart'; +import 'package:beacon/config/router/router.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:gap/gap.dart'; +import 'package:intl/intl.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; + +class BeaconCard extends StatefulWidget { + final BeaconEntity beacon; + const BeaconCard({required this.beacon}); + + @override + State createState() => _BeaconCardState(); +} + +class _BeaconCardState extends State { + late bool hasStarted; + late bool hasEnded; + late bool willStart; + DateTime now = DateTime.now(); + late DateTime startAt; + late DateTime expiresAt; + Timer? _rebuildTimer; + + @override + void initState() { + startAt = DateTime.fromMillisecondsSinceEpoch(widget.beacon.startsAt!); + expiresAt = DateTime.fromMillisecondsSinceEpoch(widget.beacon.expiresAt!); + hasStarted = now.isAfter(startAt); + hasEnded = now.isAfter(expiresAt); + willStart = now.isBefore(startAt); + scheduleRebuild(); + super.initState(); + } + + void scheduleRebuild() { + if (hasEnded) return; + + late int seconds; + + if (willStart) { + Duration difference = startAt.difference(now); + seconds = difference.inSeconds; + } else if (hasStarted && !hasEnded) { + Duration difference = expiresAt.difference(now); + seconds = difference.inSeconds; + } + _rebuildTimer?.cancel(); + + _rebuildTimer = Timer(Duration(milliseconds: seconds * 1000 + 1000), () { + var now = DateTime.now(); + hasStarted = now.isAfter(startAt); + hasEnded = now.isAfter(expiresAt); + willStart = now.isBefore(startAt); + setState(() {}); + + Future.delayed(Duration(seconds: 1), () { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + duration: Duration(seconds: 5), + content: Text( + '${widget.beacon.title} is now active! \nYou can join the hike', + style: TextStyle(color: Colors.black), + ), + backgroundColor: kLightBlue.withOpacity(0.8), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(10), + ), + ), + behavior: SnackBarBehavior.floating, + elevation: 5, + action: SnackBarAction( + textColor: kBlue, + label: 'Click to Join', + onPressed: () async { + appRouter.push(HikeScreenRoute( + beacon: widget.beacon, + isLeader: widget.beacon.id! == localApi.userModel.id!)); + }, + ), + ), + ); + }); + }); + } + + @override + void dispose() { + _rebuildTimer?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + BeaconEntity beacon = widget.beacon; + + return InkWell( + onTap: () async { + locator().joinBeacon(beacon, hasEnded, hasStarted); + }, + child: Container( + margin: const EdgeInsets.symmetric( + vertical: 10.0, + horizontal: 10.0, + ), + padding: EdgeInsets.only(left: 16.0, right: 16.0, bottom: 8, top: 8), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: 70.w, + child: Text( + '${beacon.title} by ${beacon.leader!.name} ', + style: Style.titleTextStyle, + ), + ), + Align( + alignment: Alignment.topRight, + child: (hasStarted && !hasEnded) + ? BlinkIcon() + : willStart + ? Align( + alignment: Alignment.topRight, + child: Icon( + Icons.circle, + color: kYellow, + size: 10, + ), + ) + : null, + ), + ], + ), + SizedBox(height: 4.0), + (hasStarted && !hasEnded) + ? RichText( + text: TextSpan( + text: 'Hike is ', + style: Style.commonTextStyle, + children: const [ + TextSpan( + text: 'Active', + style: TextStyle( + fontSize: 16.0, + color: Colors.white, + fontWeight: FontWeight.bold, + letterSpacing: 1.0), + ), + ], + ), + ) + : willStart + ? Row( + children: [ + RichText( + text: TextSpan( + text: 'Hike ', + style: Style.commonTextStyle, + children: const [ + TextSpan( + text: 'Starts ', + style: TextStyle( + fontSize: 16.0, + color: Colors.white, + fontWeight: FontWeight.bold, + letterSpacing: 1.0), + ), + TextSpan( + text: 'in ', + style: TextStyle( + color: const Color(0xffb6b2df), + fontSize: 14.0, + fontWeight: FontWeight.w400), + ), + ], + ), + ), + SizedBox( + width: 3.0, + ), + CountdownTimerPage( + dateTime: DateTime.fromMillisecondsSinceEpoch( + beacon.startsAt!), + name: beacon.title, + beacon: beacon, + ) + ], + ) + : Row( + children: [ + RichText( + text: TextSpan( + text: 'Hike ', + style: Style.commonTextStyle, + children: const [ + TextSpan( + text: 'is Ended', + style: TextStyle( + fontSize: 16.0, + color: Colors.white, + fontWeight: FontWeight.bold, + letterSpacing: 1.0), + ), + ], + ), + ), + ], + ), + SizedBox(height: 4.0), + Row( + children: [ + Text('Passkey: ${beacon.shortcode}', + style: Style.commonTextStyle), + Gap(10), + InkWell( + onTap: () { + Clipboard.setData( + ClipboardData(text: beacon.shortcode.toString())); + utils.showSnackBar('Shortcode copied!', context); + }, + child: Icon( + Icons.copy, + color: Colors.white, + size: 15, + )) + ], + ), + SizedBox(height: 4.0), + (beacon.startsAt != null) + ? Text( + willStart + ? 'Starting At: ${DateFormat("hh:mm a, d/M/y").format(DateTime.fromMillisecondsSinceEpoch(beacon.startsAt!)).toString()}' + : 'Started At: ${DateFormat("hh:mm a, d/M/y").format(DateTime.fromMillisecondsSinceEpoch(beacon.startsAt!)).toString()}', + style: Style.commonTextStyle) + : Container(), + SizedBox(height: 4.0), + (beacon.expiresAt != null) + ? willStart + ? Text( + 'Expiring At: ${DateFormat("hh:mm a, d/M/y").format(DateTime.fromMillisecondsSinceEpoch(beacon.expiresAt!)).toString()}', + style: Style.commonTextStyle) + : Text( + 'Expires At: ${DateFormat("hh:mm a, d/M/y").format(DateTime.fromMillisecondsSinceEpoch(beacon.expiresAt!)).toString()}', + style: Style.commonTextStyle) + : Container(), + ], + ) + ], + ), + decoration: BoxDecoration( + color: willStart + ? Color(0xFF141546) + : hasEnded + ? lightkBlue + : kBlue, + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(8.0), + boxShadow: [ + BoxShadow( + color: Colors.black26, + blurRadius: 10.0, + offset: Offset(0.0, 10.0), + ), + ], + ), + ), + ); + } + + Future deleteDialog(BuildContext context) async { + return showDialog( + context: context, + builder: (context) => AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + // actionsAlignment: MainAxisAlignment.spaceEvenly, + contentPadding: EdgeInsets.all(25.0), + content: Text( + 'Are you sure you want to delete this beacon?', + style: TextStyle(fontSize: 18, color: kBlack), + ), + actions: [ + HikeButton( + buttonHeight: 2.5.h, + buttonWidth: 8.w, + onTap: () => AutoRouter.of(context).maybePop(false), + text: 'No', + ), + HikeButton( + buttonHeight: 2.5.h, + buttonWidth: 8.w, + onTap: () => AutoRouter.of(context).maybePop(true), + text: 'Yes', + ), + ], + ), + ); + } +} diff --git a/lib/presentation/group/widgets/create_join_dialog.dart b/lib/presentation/group/widgets/create_join_dialog.dart new file mode 100644 index 00000000..fe4baa80 --- /dev/null +++ b/lib/presentation/group/widgets/create_join_dialog.dart @@ -0,0 +1,600 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:beacon/core/utils/validators.dart'; +import 'package:beacon/presentation/group/cubit/group_cubit/group_cubit.dart'; +import 'package:beacon/presentation/home/home_cubit/home_cubit.dart'; +import 'package:beacon/locator.dart'; +import 'package:beacon/presentation/widgets/hike_button.dart'; +import 'package:beacon/core/utils/constants.dart'; +import 'package:duration_picker/duration_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:gap/gap.dart'; +import 'package:intl/intl.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; + +class CreateJoinGroupDialog { + static GlobalKey _groupKey = GlobalKey(); + + static final TextEditingController _groupNameController = + TextEditingController(); + + static Future createGroupDialog( + BuildContext context, + ) { + bool isSmallSized = 100.h < 800; + return showDialog( + context: context, + builder: (context) => Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + child: SingleChildScrollView( + child: Form( + key: _groupKey, + child: Container( + height: isSmallSized ? 30.h : 25.h, + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 32, vertical: 16), + child: Column( + children: [ + Container( + height: isSmallSized ? 12.h : 10.h, + child: Padding( + padding: const EdgeInsets.all(4.0), + child: TextFormField( + controller: _groupNameController, + style: TextStyle(fontSize: 22.0), + validator: (value) => + Validator.validateBeaconTitle(value!), + onChanged: (name) {}, + decoration: InputDecoration( + border: InputBorder.none, + hintText: 'Enter Title Here', + labelStyle: TextStyle( + fontSize: labelsize, color: kYellow), + hintStyle: TextStyle( + fontSize: hintsize, color: hintColor), + labelText: 'Title', + alignLabelWithHint: true, + floatingLabelBehavior: + FloatingLabelBehavior.always, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none), + ), + ), + color: kLightBlue, + ), + SizedBox( + height: 2.h, + ), + Flexible( + flex: 2, + child: HikeButton( + text: 'Create Group', + textSize: 18.0, + textColor: Colors.white, + buttonColor: kYellow, + onTap: () { + if (!_groupKey.currentState!.validate()) return; + AutoRouter.of(context).maybePop(); + context + .read() + .createGroup(_groupNameController.text.trim()); + _groupNameController.clear(); + }), + ), + ], + ), + ), + ), + ), + ), + ), + ); + } + + static GlobalKey _joinGroupKey = GlobalKey(); + + static final TextEditingController _joinGroupController = + TextEditingController(); + + static Future joinGroupDialog(BuildContext context) { + bool isSmallSized = MediaQuery.of(context).size.height < 800; + return showDialog( + context: context, + builder: (context) => Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + child: Form( + key: _joinGroupKey, + child: Container( + height: isSmallSized ? 30.h : 25.h, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), + child: Column( + children: [ + Container( + height: isSmallSized ? 12.h : 10.h, + child: Padding( + padding: const EdgeInsets.all(4.0), + child: TextFormField( + controller: _joinGroupController, + keyboardType: TextInputType.text, + textCapitalization: TextCapitalization.characters, + style: TextStyle(fontSize: 22.0), + validator: (value) => Validator.validatePasskey(value!), + onChanged: (value) { + _joinGroupController.text = value.toUpperCase(); + }, + decoration: InputDecoration( + alignLabelWithHint: true, + floatingLabelBehavior: FloatingLabelBehavior.always, + hintText: 'Enter Group Code Here', + hintStyle: + TextStyle(fontSize: hintsize, color: hintColor), + labelText: 'Code', + labelStyle: + TextStyle(fontSize: labelsize, color: kYellow), + border: InputBorder.none, + ), + ), + ), + color: kLightBlue, + ), + SizedBox( + height: 2.h, + ), + Flexible( + child: HikeButton( + text: 'Join Group', + textSize: 18.0, + textColor: Colors.white, + buttonColor: kYellow, + onTap: () { + if (!_joinGroupKey.currentState!.validate()) return; + appRouter.maybePop(); + context + .read() + .joinGroup(_joinGroupController.text.trim()); + _joinGroupController.clear(); + }, + ), + ), + ], + ), + ), + ), + ), + ), + ); + } +} + +String title = ''; +DateTime? startDate = DateTime.now(); +TimeOfDay? startTime = + TimeOfDay(hour: TimeOfDay.now().hour, minute: TimeOfDay.now().minute + 1); +Duration? duration = Duration(minutes: 5); + +class CreateJoinBeaconDialog { + static Future createHikeDialog(BuildContext context, String groupId) { + bool isSmallSized = 100.h < 800; + return showDialog( + context: context, + builder: (context) => Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + child: Container( + height: isSmallSized ? 30.h : 25.h, + child: ListView( + padding: EdgeInsets.symmetric(horizontal: 15.w), + children: [ + Gap(15), + Text( + 'Create hike', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 25, + ), + ), + Gap(20), + HikeButton( + text: 'Start Hike', + buttonWidth: 2, + buttonHeight: 16, + buttonColor: kYellow, + onTap: () { + Navigator.of(context).pop(); + createHikeBox(context, groupId, true); + }, + ), + Gap(10), + HikeButton( + text: 'Schedule Hike', + buttonWidth: 5, + buttonHeight: 16, + buttonColor: kYellow, + onTap: () { + Navigator.of(context).pop(); + createHikeBox(context, groupId, false); + }, + ), + ], + ), + ), + ), + ); + } + + static Future createHikeBox( + BuildContext context, String? groupID, bool isInstant) { + bool isSmallSized = 100.h < 800; + + GlobalKey _createFormKey = GlobalKey(); + + FocusNode _titleNode = FocusNode(); + FocusNode _startDateNode = FocusNode(); + + TextEditingController _dateController = TextEditingController(); + TextEditingController _startTimeController = TextEditingController(); + TextEditingController _durationController = TextEditingController(); + String title = ''; + DateTime? startDate = DateTime.now(); + TimeOfDay? startTime = TimeOfDay( + hour: TimeOfDay.now().hour, minute: TimeOfDay.now().minute + 1); + Duration? duration = Duration(minutes: 5); + + return showDialog( + context: context, + builder: (context) => GestureDetector( + onTap: () => FocusManager.instance.primaryFocus?.unfocus(), + child: Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + child: SingleChildScrollView( + child: Form( + key: _createFormKey, + child: Container( + height: isInstant + ? isSmallSized + ? 45.h + : 40.h + : isSmallSized + ? 75.h + : 65.h, + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 32, vertical: 16), + child: Column( + children: [ + Container( + height: isSmallSized ? 14.h : 12.h, + child: Padding( + padding: const EdgeInsets.all(4.0), + child: TextFormField( + style: TextStyle(fontSize: 22.0), + validator: (value) => + Validator.validateBeaconTitle(value), + onChanged: (name) { + title = name; + }, + focusNode: _titleNode, + onEditingComplete: () { + _titleNode.unfocus(); + }, + decoration: InputDecoration( + border: InputBorder.none, + hintText: 'Enter Title Here', + labelStyle: TextStyle( + fontSize: labelsize, color: kYellow), + hintStyle: TextStyle( + fontSize: hintsize, color: hintColor), + labelText: 'Title', + alignLabelWithHint: true, + floatingLabelBehavior: + FloatingLabelBehavior.always, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none), + ), + ), + color: kLightBlue, + ), + isInstant ? Container() : SizedBox(height: 2.h), + // start date field + isInstant + ? Container() + : Container( + height: isSmallSized ? 12.h : 10.h, + child: Padding( + padding: const EdgeInsets.all(4.0), + child: InkWell( + onTap: () async { + startDate = await showDatePicker( + context: context, + initialDate: startDate ?? DateTime.now(), + firstDate: startDate ?? DateTime.now(), + lastDate: DateTime(2100), + // builder: (context, child) => Theme( + // // data: ThemeData().copyWith( + // // textTheme: + // // Theme.of(context).textTheme, + // // colorScheme: ColorScheme.light( + // // primary: kLightBlue, + // // onPrimary: Colors.grey, + // // surface: kBlue, + // // ), + // // ), + // child: child!), + ); + if (startDate == null) return; + _dateController.text = + DateFormat('yyyy-MM-dd') + .format(startDate!); + }, + child: TextFormField( + validator: (value) => + Validator.validateDate(value), + controller: _dateController, + enabled: false, + focusNode: _startDateNode, + onEditingComplete: () {}, + decoration: InputDecoration( + border: InputBorder.none, + hintText: 'Choose Start Date', + labelStyle: TextStyle( + fontSize: labelsize, + color: kYellow), + hintStyle: TextStyle( + fontSize: hintsize, + color: hintColor), + labelText: 'Start Date', + alignLabelWithHint: true, + floatingLabelBehavior: + FloatingLabelBehavior.always, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none), + ), + ), + ), + color: kLightBlue, + ), + isInstant ? Container() : SizedBox(height: 2.h), + // Start Time Field + isInstant + ? SizedBox.shrink() + : Container( + height: isSmallSized ? 12.h : 10.h, + child: Padding( + padding: const EdgeInsets.all(4.0), + child: InkWell( + onTap: () async { + startTime = await showTimePicker( + context: context, + initialTime: startTime ?? + TimeOfDay( + hour: DateTime.now().hour, + minute: + DateTime.now().minute + 1)); + if (startTime != null) { + if (startTime!.minute < 10) { + _startTimeController.text = + '${startTime!.hour}:0${startTime!.minute} ${startTime!.period == DayPeriod.am ? 'AM' : 'PM'}'; + } else { + _startTimeController.text = + '${startTime!.hour}:${startTime!.minute} ${startTime!.period == DayPeriod.am ? 'AM' : 'PM'}'; + } + } + }, + child: TextFormField( + validator: (value) => + Validator.validateStartTime( + value, _dateController.text), + controller: _startTimeController, + enabled: false, + onEditingComplete: () {}, + decoration: InputDecoration( + border: InputBorder.none, + alignLabelWithHint: true, + errorStyle: + TextStyle(color: Colors.red[800]), + floatingLabelBehavior: + FloatingLabelBehavior.always, + labelText: 'Start Time', + labelStyle: TextStyle( + fontSize: labelsize, color: kYellow), + hintStyle: TextStyle( + fontSize: hintsize, color: hintColor), + hintText: 'Choose start time', + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + ), + ), + ), + ), + color: kLightBlue, + ), + SizedBox(height: 2.h), + // Duration Field + Container( + height: isSmallSized ? 14.h : 12.h, + child: Padding( + padding: const EdgeInsets.all(4.0), + child: InkWell( + onTap: () async { + duration = await showDurationPicker( + context: context, + initialTime: duration ?? Duration(minutes: 5), + ); + if (duration == null) return; + if (duration!.inHours != 0 && + duration!.inMinutes != 0) { + _durationController.text = + '${duration!.inHours.toString()} hour ${(duration!.inMinutes % 60)} minutes'; + } else if (duration!.inMinutes != 0) { + _durationController.text = + '${duration!.inMinutes.toString()} minutes'; + } + }, + child: TextFormField( + enabled: false, + controller: _durationController, + validator: (value) => + Validator.validateDuration(value.toString()), + decoration: InputDecoration( + border: InputBorder.none, + alignLabelWithHint: true, + errorStyle: TextStyle(color: Colors.red[800]), + floatingLabelBehavior: + FloatingLabelBehavior.always, + labelText: 'Duration', + labelStyle: TextStyle( + fontSize: labelsize, color: kYellow), + hintStyle: TextStyle( + fontSize: hintsize, color: hintColor), + hintText: 'Enter duration of hike', + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none), + ), + ), + ), + color: kLightBlue, + ), + SizedBox(height: 2.h), + Flexible( + flex: 2, + child: HikeButton( + text: isInstant ? 'Start' : 'Create', + textSize: 18.0, + textColor: Colors.white, + buttonColor: kYellow, + onTap: () async { + if (_createFormKey.currentState!.validate()) { + var groupCubit = locator(); + if (!isInstant) { + DateTime start = DateTime( + startDate!.year, + startDate!.month, + startDate!.day, + startTime!.hour, + startTime!.minute); + + final startsAt = start.millisecondsSinceEpoch; + + final expiresAt = start + .add(duration!) + .millisecondsSinceEpoch; + + groupCubit.createHike(title, startsAt, + expiresAt, groupID!, isInstant); + _durationController.clear(); + _startTimeController.clear(); + _durationController.clear(); + + appRouter.maybePop(); + } else { + int startsAt = + DateTime.now().millisecondsSinceEpoch; + + int expiresAt = DateTime.now() + .add(duration!) + .millisecondsSinceEpoch; + + groupCubit.createHike(title, startsAt, + expiresAt, groupID!, isInstant); + + _durationController.clear(); + _startTimeController.clear(); + _durationController.clear(); + appRouter.maybePop(); + } + } + }), + ), + ], + ), + ), + ), + ), + ), + ), + ), + ); + } + + static GlobalKey _joinBeaconKey = GlobalKey(); + static TextEditingController _joinBeaconController = TextEditingController(); + static Future joinBeaconDialog(BuildContext context) { + bool isSmallSized = MediaQuery.of(context).size.height < 800; + return showDialog( + context: context, + builder: (context) => Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + child: Form( + key: _joinBeaconKey, + child: Container( + height: isSmallSized ? 30.h : 25.h, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), + child: Column( + children: [ + Container( + height: isSmallSized ? 14.h : 12.h, + child: Padding( + padding: const EdgeInsets.all(4.0), + child: TextFormField( + controller: _joinBeaconController, + keyboardType: TextInputType.text, + textCapitalization: TextCapitalization.characters, + style: TextStyle(fontSize: 22.0), + validator: (value) => Validator.validatePasskey(value!), + onChanged: (key) { + _joinBeaconController.text = key.toUpperCase(); + }, + decoration: InputDecoration( + alignLabelWithHint: true, + floatingLabelBehavior: FloatingLabelBehavior.always, + hintText: 'Enter Passkey Here', + hintStyle: + TextStyle(fontSize: hintsize, color: hintColor), + labelText: 'Passkey', + labelStyle: + TextStyle(fontSize: labelsize, color: kYellow), + border: InputBorder.none, + ), + ), + ), + color: kLightBlue, + ), + SizedBox( + height: 2.h, + ), + Flexible( + child: HikeButton( + text: 'Validate', + textSize: 18.0, + textColor: Colors.white, + buttonColor: kYellow, + onTap: () { + if (!_joinBeaconKey.currentState!.validate()) return; + locator().joinBeaconWithShortCode( + _joinBeaconController.text); + appRouter.maybePop(); + _joinBeaconController.clear(); + }, + ), + ), + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/presentation/group/widgets/group_widgets.dart b/lib/presentation/group/widgets/group_widgets.dart new file mode 100644 index 00000000..0ad93de4 --- /dev/null +++ b/lib/presentation/group/widgets/group_widgets.dart @@ -0,0 +1,548 @@ +import 'dart:developer'; + +import 'package:beacon/core/utils/validators.dart'; +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/presentation/group/cubit/members_cubit/members_cubit.dart'; +import 'package:beacon/presentation/group/cubit/members_cubit/members_state.dart'; +import 'package:beacon/locator.dart'; +import 'package:beacon/presentation/widgets/shimmer.dart'; +import 'package:duration_picker/duration_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:beacon/presentation/group/cubit/group_cubit/group_cubit.dart'; +import 'package:beacon/presentation/group/cubit/group_cubit/group_state.dart'; +import 'package:beacon/presentation/widgets/hike_button.dart'; +import 'package:beacon/core/utils/constants.dart'; +import 'package:gap/gap.dart'; +import 'package:intl/intl.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; + +class GroupWidgetUtils { + static Widget membersWidget(BuildContext context) { + return FloatingActionButton( + heroTag: 'members', + backgroundColor: kYellow, + onPressed: () { + _showMembers(context); + }, + child: Icon(Icons.person, size: 30), + ); + } + + static void _showMembers(BuildContext context) { + // Dialog for filtering beacons + locator().loadMembers(); + showDialog( + context: context, + builder: (context) { + bool isSmallSized = 100.h < 800; + return AlertDialog( + title: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.group), + Gap(5), + Text( + 'Members', + textAlign: TextAlign.center, + ) + ], + ), + content: Container( + height: isSmallSized ? 30.h : 25.h, + width: isSmallSized ? 200 : 300, + child: BlocConsumer( + listener: (context, state) { + if (state is LoadedMemberState && state.message != null) { + utils.showSnackBar(state.message!, context); + } + }, + builder: (context, state) { + if (state is LoadingMemberState) { + return ShimmerWidget.getPlaceholder(); + } else if (state is LoadedMemberState) { + var members = state.members; + return members!.isEmpty + ? Container( + child: + Text('Please check your internet connection'), + ) + : ListView.builder( + shrinkWrap: true, + itemCount: members.length, + itemBuilder: (context, index) { + bool isLeader = + localApi.userModel.id! == members[0].id!; + return Container( + margin: EdgeInsets.symmetric(vertical: 10), + decoration: BoxDecoration( + color: kLightBlue, + borderRadius: + BorderRadius.all(Radius.circular(10))), + child: ListTile( + leading: index == 0 + ? Icon( + Icons.star, + color: kYellow, + ) + : Icon(Icons.person), + trailing: index == 0 + ? Text('Leader') + : isLeader + ? IconButton( + onPressed: () { + context + .read() + .removeMember( + members[index].id ?? + ''); + }, + icon: Icon( + Icons.person_remove_alt_1, + weight: 20, + color: const Color.fromARGB( + 255, 215, 103, 95), + )) + : null, + subtitle: localApi.userModel.id! == + members[index].id! + ? Text( + '(YOU)', + style: TextStyle(fontSize: 12), + ) + : null, + title: + Text(members[index].name ?? 'Anonymous'), + ), + ); + }, + ); + } + return Container(); + }, + )), + ); + }, + ); + } + + static Future deleteDialog(BuildContext context) async { + return showDialog( + context: context, + builder: (context) => AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + // actionsAlignment: MainAxisAlignment.spaceEvenly, + contentPadding: EdgeInsets.all(25.0), + content: Text( + 'Are you sure you want to delete this beacon?', + style: TextStyle(fontSize: 18, color: kBlack), + ), + actions: [ + HikeButton( + buttonHeight: 2.5.h, + buttonWidth: 8.w, + onTap: () => appRouter.maybePop(false), + text: 'No', + ), + HikeButton( + buttonHeight: 2.5.h, + buttonWidth: 8.w, + onTap: () => appRouter.maybePop(true), + text: 'Yes', + ), + ], + ), + ); + } + + static Widget filterBeacons( + BuildContext context, String groupId, GroupCubit groupCubit) { + // Widget for filtering beacons + return FloatingActionButton( + heroTag: 'filter beacon', + backgroundColor: kYellow, + onPressed: () => _showFilterBeaconAlertBox(context, groupId, groupCubit), + child: ImageIcon( + AssetImage(AppConstants.filterIconPath), + size: 35, + semanticLabel: 'Filter', + color: Colors.black, + ), + ); + } + + static Future reScheduleHikeDialog( + BuildContext context, BeaconEntity beacon) { + var startsAt = beacon.startsAt!; + var expiresAt = beacon.expiresAt!; + var previousStartDate = DateTime.fromMillisecondsSinceEpoch(startsAt); + var previousExpireDate = DateTime.fromMillisecondsSinceEpoch(expiresAt); + + var previousDuration = previousExpireDate.difference(previousStartDate); + + DateTime? newstartDate = previousStartDate; + TextEditingController _dateController = TextEditingController( + text: DateFormat('yyyy-MM-dd').format(previousStartDate)); + + TimeOfDay? startTime = TimeOfDay( + hour: previousStartDate.hour, minute: previousStartDate.minute); + TextEditingController _startTimeController = TextEditingController( + text: DateFormat('HH:mm').format(previousStartDate)); + + Duration? duration = previousDuration; + TextEditingController _durationController = TextEditingController( + text: previousDuration.inMinutes < 60 + ? '${previousDuration.inMinutes} minutes' + : '${previousDuration.inHours} hours'); + + GlobalKey _createFormKey = GlobalKey(); + bool isSmallSized = 100.h < 800; + + bool isExpired = DateTime.now() + .isAfter(DateTime.fromMillisecondsSinceEpoch(beacon.expiresAt!)); + return showDialog( + context: context, + builder: (context) => GestureDetector( + onTap: () => FocusManager.instance.primaryFocus?.unfocus(), + child: Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + child: SingleChildScrollView( + child: Form( + key: _createFormKey, + child: Container( + height: isSmallSized ? 68.h : 62.h, + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 32, vertical: 16), + child: Column( + children: [ + Text( + isExpired ? 'Activate Hike' : 'Reschedule Hike', + style: TextStyle(fontSize: 30), + ), + SizedBox(height: 2.h), + // start date field + Container( + height: isSmallSized ? 14.h : 12.h, + child: Padding( + padding: const EdgeInsets.all(4.0), + child: InkWell( + onTap: () async { + newstartDate = await showDatePicker( + context: context, + initialDate: newstartDate ?? DateTime.now(), + firstDate: newstartDate ?? DateTime.now(), + lastDate: DateTime(2100), + // builder: (context, child) => Theme( + // data: ThemeData().copyWith( + // textTheme: Theme.of(context).textTheme, + // colorScheme: ColorScheme.light( + // primary: kLightBlue, + // onPrimary: Colors.grey, + // surface: kBlue, + // ), + // ), + // child: child!), + ); + if (newstartDate == null) return; + _dateController.text = DateFormat('yyyy-MM-dd') + .format(newstartDate!); + }, + child: TextFormField( + validator: (value) => + Validator.validateDate(value), + controller: _dateController, + enabled: false, + onEditingComplete: () {}, + decoration: InputDecoration( + border: InputBorder.none, + hintText: 'Choose Start Date', + labelStyle: TextStyle( + fontSize: labelsize, color: kYellow), + hintStyle: TextStyle( + fontSize: hintsize, color: hintColor), + labelText: 'Start Date', + alignLabelWithHint: true, + floatingLabelBehavior: + FloatingLabelBehavior.always, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none), + ), + ), + ), + color: kLightBlue, + ), + SizedBox(height: 2.h), + // Start Time Field + Container( + height: isSmallSized ? 14.h : 12.h, + child: Padding( + padding: const EdgeInsets.all(4.0), + child: InkWell( + onTap: () async { + startTime = await showTimePicker( + context: context, + initialTime: startTime ?? + TimeOfDay( + hour: DateTime.now().hour, + minute: DateTime.now().minute + 1)); + if (startTime != null) { + if (startTime!.minute < 10) { + _startTimeController.text = + '${startTime!.hour}:0${startTime!.minute} ${startTime!.period == DayPeriod.am ? 'AM' : 'PM'}'; + } else { + _startTimeController.text = + '${startTime!.hour}:${startTime!.minute} ${startTime!.period == DayPeriod.am ? 'AM' : 'PM'}'; + } + } + }, + child: TextFormField( + validator: (value) => Validator.validateStartTime( + value, _dateController.text), + controller: _startTimeController, + enabled: false, + onEditingComplete: () {}, + decoration: InputDecoration( + border: InputBorder.none, + alignLabelWithHint: true, + errorStyle: TextStyle(color: Colors.red[800]), + floatingLabelBehavior: + FloatingLabelBehavior.always, + labelText: 'Start Time', + labelStyle: TextStyle( + fontSize: labelsize, color: kYellow), + hintStyle: TextStyle( + fontSize: hintsize, color: hintColor), + hintText: 'Choose start time', + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + ), + ), + ), + ), + color: kLightBlue, + ), + SizedBox(height: 2.h), + // // Duration Field + Container( + height: isSmallSized ? 14.h : 12.h, + child: Padding( + padding: const EdgeInsets.all(4.0), + child: InkWell( + onTap: () async { + duration = await showDurationPicker( + context: context, + initialTime: duration ?? Duration(minutes: 5), + ); + if (duration == null) return; + if (duration!.inHours != 0 && + duration!.inMinutes != 0) { + _durationController.text = + '${duration!.inHours.toString()} hour ${(duration!.inMinutes % 60)} minutes'; + } else if (duration!.inMinutes != 0) { + _durationController.text = + '${duration!.inMinutes.toString()} minutes'; + } + }, + child: TextFormField( + enabled: false, + controller: _durationController, + validator: (value) => + Validator.validateDuration(value), + decoration: InputDecoration( + border: InputBorder.none, + alignLabelWithHint: true, + errorStyle: TextStyle(color: Colors.red[800]), + floatingLabelBehavior: + FloatingLabelBehavior.always, + labelText: 'Duration', + labelStyle: TextStyle( + fontSize: labelsize, color: kYellow), + hintStyle: TextStyle( + fontSize: hintsize, color: hintColor), + hintText: 'Enter duration of hike', + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none), + ), + ), + ), + color: kLightBlue, + ), + SizedBox(height: 2.h), + Flexible( + flex: 2, + child: HikeButton( + text: 'Update', + textSize: 18.0, + textColor: Colors.white, + buttonColor: kYellow, + onTap: () async { + if (!_createFormKey.currentState!.validate()) + return; + DateTime startsAt = DateTime( + newstartDate!.year, + newstartDate!.month, + newstartDate!.day, + startTime!.hour, + startTime!.minute); + + final newStartsAt = + startsAt.millisecondsSinceEpoch; + + final newExpiresAT = startsAt + .copyWith( + hour: startsAt.hour + duration!.inHours, + minute: + startsAt.minute + duration!.inMinutes) + .millisecondsSinceEpoch; + + context.read().rescheduleHike( + newExpiresAT, newStartsAt, beacon.id!); + _dateController.clear(); + _startTimeController.clear(); + _durationController.clear(); + appRouter.maybePop(); + // } + }), + ), + ], + ), + ), + ), + ), + ), + ), + ), + ); + } + + static void _showFilterBeaconAlertBox( + BuildContext context, String groupId, GroupCubit groupCubit) { + log(100.h.toString()); + // Dialog for filtering beacons + showDialog( + context: context, + builder: (context) { + bool isSmallSized = 100.h < 800; + return AlertDialog( + title: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ImageIcon( + AssetImage(AppConstants.filterIconPath), + size: 30, + semanticLabel: 'Filter', + color: Colors.black, + ), + Gap(10), + Text( + 'Filter', + textAlign: TextAlign.center, + ), + ], + ), + content: SizedBox( + width: 200, + height: isSmallSized ? 32.h : 30.h, + child: BlocBuilder( + builder: (context, state) => ListView.builder( + itemCount: filters.values.length, + itemBuilder: (context, index) { + String type = filters.values[index].name; + + return HikeButton( + buttonWidth: 2.w, + buttonHeight: 1.h, + text: type, + onTap: () { + Navigator.pop(context); + if (filters.values[index] == filters.NEARBY) { + _neabyFilterAlertBox(context, groupId, groupCubit); + } else { + locator() + .changeFilter(filters.values[index]); + } + }, + buttonColor: kYellow, + ); + }, + ), + ), + ), + ); + }, + ); + } + + static void _neabyFilterAlertBox( + BuildContext context, String groupId, GroupCubit groupCubit) { + GlobalKey _key = GlobalKey(); + showDialog( + context: context, + builder: (context) { + double value = 1000.0 / + 100000; // Default radius for range (100 km) // creating a 100.0 km range + bool isSmallSized = 100.h < 800; + return AlertDialog( + content: SizedBox( + height: isSmallSized ? 28.h : 25.h, + child: Form( + key: _key, + child: Column( + children: [ + Gap(5), + Container( + color: kLightBlue, + child: StatefulBuilder( + builder: (context, setState) => Stack( + children: [ + Container( + height: 14.h, + child: Slider( + activeColor: kYellow, + value: value, + onChanged: (double newValue) { + setState(() { + value = newValue; + }); + }, + ), + ), + Align( + alignment: Alignment(0, 0), + child: Text( + '${(value * 100).toStringAsFixed(2)} km', + style: TextStyle(fontSize: 20, color: hintColor), + ), + ), + ], + ), + ), + ), + Gap(10), + HikeButton( + text: 'Fetch', + buttonColor: kYellow, + onTap: () { + if (_key.currentState!.validate()) { + appRouter.maybePop(); + locator() + .nearbyHikes(groupId, radius: value * 100000); + } + }, + buttonWidth: 60, + borderColor: Colors.white, + ), + ], + ), + ), + ), + ); + }, + ); + } +} diff --git a/lib/presentation/group/widgets/timer.dart b/lib/presentation/group/widgets/timer.dart new file mode 100644 index 00000000..e2907faa --- /dev/null +++ b/lib/presentation/group/widgets/timer.dart @@ -0,0 +1,55 @@ +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_countdown_timer/index.dart'; + +class CountdownTimerPage extends StatefulWidget { + final String? name; + final DateTime dateTime; + final BeaconEntity beacon; + CountdownTimerPage( + {Key? key, + required this.dateTime, + required this.name, + required this.beacon}) + : super(key: key); + @override + _CountdownTimerPageState createState() => _CountdownTimerPageState(); +} + +class _CountdownTimerPageState extends State + with SingleTickerProviderStateMixin { + CountdownTimerController? controller; + int endTime = 0; + @override + void initState() { + super.initState(); + setState(() {}); + int timeDiff = widget.dateTime.difference(DateTime.now()).inSeconds; + setState(() { + endTime = DateTime.now().millisecondsSinceEpoch + 1000 * timeDiff; + }); + controller = CountdownTimerController(endTime: endTime, vsync: this); + } + + @override + Widget build(BuildContext context) { + return CountdownTimer( + controller: controller, + widgetBuilder: (_, CurrentRemainingTime? time) { + return Text( + '${time?.days ?? 0} : ${time?.hours ?? 0} : ${time?.min ?? 0} : ${time?.sec ?? 0}', + style: TextStyle( + color: const Color(0xffb6b2df), + fontSize: 14.0, + fontWeight: FontWeight.w400), + ); + }, + ); + } + + @override + void dispose() { + controller!.dispose(); + super.dispose(); + } +} diff --git a/lib/presentation/hike/cubit/hike_cubit/hike_cubit.dart b/lib/presentation/hike/cubit/hike_cubit/hike_cubit.dart new file mode 100644 index 00000000..9beada10 --- /dev/null +++ b/lib/presentation/hike/cubit/hike_cubit/hike_cubit.dart @@ -0,0 +1,43 @@ +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/domain/usecase/hike_usecase.dart'; +import 'package:beacon/locator.dart'; +import 'package:beacon/presentation/hike/cubit/hike_cubit/hike_state.dart'; +import 'package:beacon/presentation/hike/cubit/location_cubit/location_cubit.dart'; +import 'package:beacon/presentation/hike/cubit/panel_cubit/panel_cubit.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class HikeCubit extends Cubit { + final HikeUseCase _hikeUseCase; + HikeCubit._internal(this._hikeUseCase) : super(LoadedHikeState()); + + static HikeCubit? _instance; + + factory HikeCubit(HikeUseCase hikeUseCase) { + return _instance ?? HikeCubit._internal(hikeUseCase); + } + + BeaconEntity? _beacon; + + Future startHike( + String beaconId, TickerProvider vsync, BuildContext context) async { + emit(InitialHikeState()); + final dataState = await _hikeUseCase.fetchBeaconDetails(beaconId); + + if (dataState is DataSuccess && dataState.data != null) { + final beacon = dataState.data!; + _beacon = beacon; + + locator().loadBeaconData(beacon, vsync, context); + locator().loadBeaconData(beacon); + emit(LoadedHikeState(beacon: _beacon, message: 'Welcome to hike!')); + } else { + emit(ErrorHikeState(errmessage: dataState.error)); + } + } + + clear() { + _beacon = null; + } +} diff --git a/lib/presentation/hike/cubit/hike_cubit/hike_state.dart b/lib/presentation/hike/cubit/hike_cubit/hike_state.dart new file mode 100644 index 00000000..2ffb9995 --- /dev/null +++ b/lib/presentation/hike/cubit/hike_cubit/hike_state.dart @@ -0,0 +1,12 @@ +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +part 'hike_state.freezed.dart'; + +@freezed +class HikeState with _$HikeState { + factory HikeState.initial() = InitialHikeState; + factory HikeState.loaded({BeaconEntity? beacon, String? message}) = + LoadedHikeState; + + factory HikeState.error({String? errmessage}) = ErrorHikeState; +} diff --git a/lib/presentation/hike/cubit/hike_cubit/hike_state.freezed.dart b/lib/presentation/hike/cubit/hike_cubit/hike_state.freezed.dart new file mode 100644 index 00000000..94b72b12 --- /dev/null +++ b/lib/presentation/hike/cubit/hike_cubit/hike_state.freezed.dart @@ -0,0 +1,493 @@ +// 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 'hike_state.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 _$HikeState { + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function(BeaconEntity? beacon, String? message) loaded, + required TResult Function(String? errmessage) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function(BeaconEntity? beacon, String? message)? loaded, + TResult? Function(String? errmessage)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function(BeaconEntity? beacon, String? message)? loaded, + TResult Function(String? errmessage)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(InitialHikeState value) initial, + required TResult Function(LoadedHikeState value) loaded, + required TResult Function(ErrorHikeState value) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialHikeState value)? initial, + TResult? Function(LoadedHikeState value)? loaded, + TResult? Function(ErrorHikeState value)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialHikeState value)? initial, + TResult Function(LoadedHikeState value)? loaded, + TResult Function(ErrorHikeState value)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $HikeStateCopyWith<$Res> { + factory $HikeStateCopyWith(HikeState value, $Res Function(HikeState) then) = + _$HikeStateCopyWithImpl<$Res, HikeState>; +} + +/// @nodoc +class _$HikeStateCopyWithImpl<$Res, $Val extends HikeState> + implements $HikeStateCopyWith<$Res> { + _$HikeStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; +} + +/// @nodoc +abstract class _$$InitialHikeStateImplCopyWith<$Res> { + factory _$$InitialHikeStateImplCopyWith(_$InitialHikeStateImpl value, + $Res Function(_$InitialHikeStateImpl) then) = + __$$InitialHikeStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$InitialHikeStateImplCopyWithImpl<$Res> + extends _$HikeStateCopyWithImpl<$Res, _$InitialHikeStateImpl> + implements _$$InitialHikeStateImplCopyWith<$Res> { + __$$InitialHikeStateImplCopyWithImpl(_$InitialHikeStateImpl _value, + $Res Function(_$InitialHikeStateImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$InitialHikeStateImpl implements InitialHikeState { + _$InitialHikeStateImpl(); + + @override + String toString() { + return 'HikeState.initial()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$InitialHikeStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function(BeaconEntity? beacon, String? message) loaded, + required TResult Function(String? errmessage) error, + }) { + return initial(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function(BeaconEntity? beacon, String? message)? loaded, + TResult? Function(String? errmessage)? error, + }) { + return initial?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function(BeaconEntity? beacon, String? message)? loaded, + TResult Function(String? errmessage)? error, + required TResult orElse(), + }) { + if (initial != null) { + return initial(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialHikeState value) initial, + required TResult Function(LoadedHikeState value) loaded, + required TResult Function(ErrorHikeState value) error, + }) { + return initial(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialHikeState value)? initial, + TResult? Function(LoadedHikeState value)? loaded, + TResult? Function(ErrorHikeState value)? error, + }) { + return initial?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialHikeState value)? initial, + TResult Function(LoadedHikeState value)? loaded, + TResult Function(ErrorHikeState value)? error, + required TResult orElse(), + }) { + if (initial != null) { + return initial(this); + } + return orElse(); + } +} + +abstract class InitialHikeState implements HikeState { + factory InitialHikeState() = _$InitialHikeStateImpl; +} + +/// @nodoc +abstract class _$$LoadedHikeStateImplCopyWith<$Res> { + factory _$$LoadedHikeStateImplCopyWith(_$LoadedHikeStateImpl value, + $Res Function(_$LoadedHikeStateImpl) then) = + __$$LoadedHikeStateImplCopyWithImpl<$Res>; + @useResult + $Res call({BeaconEntity? beacon, String? message}); + + $BeaconEntityCopyWith<$Res>? get beacon; +} + +/// @nodoc +class __$$LoadedHikeStateImplCopyWithImpl<$Res> + extends _$HikeStateCopyWithImpl<$Res, _$LoadedHikeStateImpl> + implements _$$LoadedHikeStateImplCopyWith<$Res> { + __$$LoadedHikeStateImplCopyWithImpl( + _$LoadedHikeStateImpl _value, $Res Function(_$LoadedHikeStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? beacon = freezed, + Object? message = freezed, + }) { + return _then(_$LoadedHikeStateImpl( + beacon: freezed == beacon + ? _value.beacon + : beacon // ignore: cast_nullable_to_non_nullable + as BeaconEntity?, + message: freezed == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String?, + )); + } + + @override + @pragma('vm:prefer-inline') + $BeaconEntityCopyWith<$Res>? get beacon { + if (_value.beacon == null) { + return null; + } + + return $BeaconEntityCopyWith<$Res>(_value.beacon!, (value) { + return _then(_value.copyWith(beacon: value)); + }); + } +} + +/// @nodoc + +class _$LoadedHikeStateImpl implements LoadedHikeState { + _$LoadedHikeStateImpl({this.beacon, this.message}); + + @override + final BeaconEntity? beacon; + @override + final String? message; + + @override + String toString() { + return 'HikeState.loaded(beacon: $beacon, message: $message)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$LoadedHikeStateImpl && + (identical(other.beacon, beacon) || other.beacon == beacon) && + (identical(other.message, message) || other.message == message)); + } + + @override + int get hashCode => Object.hash(runtimeType, beacon, message); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$LoadedHikeStateImplCopyWith<_$LoadedHikeStateImpl> get copyWith => + __$$LoadedHikeStateImplCopyWithImpl<_$LoadedHikeStateImpl>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function(BeaconEntity? beacon, String? message) loaded, + required TResult Function(String? errmessage) error, + }) { + return loaded(beacon, message); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function(BeaconEntity? beacon, String? message)? loaded, + TResult? Function(String? errmessage)? error, + }) { + return loaded?.call(beacon, message); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function(BeaconEntity? beacon, String? message)? loaded, + TResult Function(String? errmessage)? error, + required TResult orElse(), + }) { + if (loaded != null) { + return loaded(beacon, message); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialHikeState value) initial, + required TResult Function(LoadedHikeState value) loaded, + required TResult Function(ErrorHikeState value) error, + }) { + return loaded(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialHikeState value)? initial, + TResult? Function(LoadedHikeState value)? loaded, + TResult? Function(ErrorHikeState value)? error, + }) { + return loaded?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialHikeState value)? initial, + TResult Function(LoadedHikeState value)? loaded, + TResult Function(ErrorHikeState value)? error, + required TResult orElse(), + }) { + if (loaded != null) { + return loaded(this); + } + return orElse(); + } +} + +abstract class LoadedHikeState implements HikeState { + factory LoadedHikeState({final BeaconEntity? beacon, final String? message}) = + _$LoadedHikeStateImpl; + + BeaconEntity? get beacon; + String? get message; + @JsonKey(ignore: true) + _$$LoadedHikeStateImplCopyWith<_$LoadedHikeStateImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$ErrorHikeStateImplCopyWith<$Res> { + factory _$$ErrorHikeStateImplCopyWith(_$ErrorHikeStateImpl value, + $Res Function(_$ErrorHikeStateImpl) then) = + __$$ErrorHikeStateImplCopyWithImpl<$Res>; + @useResult + $Res call({String? errmessage}); +} + +/// @nodoc +class __$$ErrorHikeStateImplCopyWithImpl<$Res> + extends _$HikeStateCopyWithImpl<$Res, _$ErrorHikeStateImpl> + implements _$$ErrorHikeStateImplCopyWith<$Res> { + __$$ErrorHikeStateImplCopyWithImpl( + _$ErrorHikeStateImpl _value, $Res Function(_$ErrorHikeStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? errmessage = freezed, + }) { + return _then(_$ErrorHikeStateImpl( + errmessage: freezed == errmessage + ? _value.errmessage + : errmessage // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc + +class _$ErrorHikeStateImpl implements ErrorHikeState { + _$ErrorHikeStateImpl({this.errmessage}); + + @override + final String? errmessage; + + @override + String toString() { + return 'HikeState.error(errmessage: $errmessage)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ErrorHikeStateImpl && + (identical(other.errmessage, errmessage) || + other.errmessage == errmessage)); + } + + @override + int get hashCode => Object.hash(runtimeType, errmessage); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$ErrorHikeStateImplCopyWith<_$ErrorHikeStateImpl> get copyWith => + __$$ErrorHikeStateImplCopyWithImpl<_$ErrorHikeStateImpl>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function(BeaconEntity? beacon, String? message) loaded, + required TResult Function(String? errmessage) error, + }) { + return error(errmessage); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function(BeaconEntity? beacon, String? message)? loaded, + TResult? Function(String? errmessage)? error, + }) { + return error?.call(errmessage); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function(BeaconEntity? beacon, String? message)? loaded, + TResult Function(String? errmessage)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(errmessage); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialHikeState value) initial, + required TResult Function(LoadedHikeState value) loaded, + required TResult Function(ErrorHikeState value) error, + }) { + return error(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialHikeState value)? initial, + TResult? Function(LoadedHikeState value)? loaded, + TResult? Function(ErrorHikeState value)? error, + }) { + return error?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialHikeState value)? initial, + TResult Function(LoadedHikeState value)? loaded, + TResult Function(ErrorHikeState value)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(this); + } + return orElse(); + } +} + +abstract class ErrorHikeState implements HikeState { + factory ErrorHikeState({final String? errmessage}) = _$ErrorHikeStateImpl; + + String? get errmessage; + @JsonKey(ignore: true) + _$$ErrorHikeStateImplCopyWith<_$ErrorHikeStateImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/presentation/hike/cubit/location_cubit/location_cubit.dart b/lib/presentation/hike/cubit/location_cubit/location_cubit.dart new file mode 100644 index 00000000..301afe1b --- /dev/null +++ b/lib/presentation/hike/cubit/location_cubit/location_cubit.dart @@ -0,0 +1,825 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:developer'; +import 'dart:ui'; +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/core/utils/constants.dart'; +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/domain/entities/landmark/landmark_entity.dart'; +import 'package:beacon/domain/entities/location/location_entity.dart'; +import 'package:beacon/domain/entities/subscriptions/beacon_locations_entity/beacon_locations_entity.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:beacon/domain/usecase/hike_usecase.dart'; +import 'package:beacon/locator.dart'; +import 'package:beacon/presentation/hike/cubit/location_cubit/location_state.dart'; +import 'package:beacon/presentation/hike/cubit/panel_cubit/panel_cubit.dart'; +import 'package:beacon/presentation/widgets/custom_label_marker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_animarker/core/ripple_marker.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_polyline_points/flutter_polyline_points.dart'; +import 'package:gap/gap.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:location/location.dart'; +import 'package:http/http.dart' as http; +import 'package:responsive_sizer/responsive_sizer.dart'; +import 'package:vibration/vibration.dart'; + +class LocationCubit extends Cubit { + final HikeUseCase _hikeUseCase; + LocationCubit._internal(this._hikeUseCase) : super(InitialLocationState()); + + static LocationCubit? _instance; + + factory LocationCubit(HikeUseCase hikeUseCase) { + return _instance ?? LocationCubit._internal(hikeUseCase); + } + + String? _beaconId; + BeaconEntity? _beacon; + GoogleMapController? mapController; + Set _hikeMarkers = {}; + UserEntity? _currentUser; + UserEntity? _leader; + List _followers = []; + Set _polyline = {}; + String? _currentUserId; + List _points = []; + String? address; + LocationData? _lastLocation; + Set _geofence = {}; + MapType _mapType = MapType.normal; + + StreamSubscription>? + _beaconlocationsSubscription; + StreamSubscription? _streamLocaitonData; + + late AnimationController? _controller; + late Animation? _animation; + BuildContext? context; + TickerProvider? vsync; + + void onMapCreated(GoogleMapController controller) { + mapController = controller; + } + + Future loadBeaconData( + BeaconEntity beacon, TickerProvider vsync, BuildContext context) async { + this.vsync = vsync; + this.context = context; + emit(InitialLocationState()); + _beaconId = beacon.id!; + _beacon = beacon; + + _currentUserId = localApi.userModel.id!; + + getLeaderAddress(locationToLatLng(beacon.leader!.location!)); + + // // adding leader location + if (beacon.leader != null) { + _leader = beacon.leader!; + + // creating leader location + + if (_currentUserId == _leader!.id) { + _currentUser = _leader; + } + if (_leader!.location != null) { + _hikeMarkers.add(RippleMarker( + markerId: MarkerId(_leader!.id!), + position: locationToLatLng(_leader!.location!), + ripple: false, + infoWindow: InfoWindow( + title: '${_beacon!.leader?.name ?? 'Anonymous'}}', + ), + onTap: () { + log('${beacon.leader?.name}'); + })); + } + } + // adding members location + if (beacon.followers != null) { + for (var follower in beacon.followers!) { + if (_currentUserId == follower!.id) { + _currentUser = follower; + } + _followers.add(follower); + if (follower.location != null) { + _createUserMarker(follower); + } + } + } + + if (beacon.route != null) { + var marker = Marker( + icon: + BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueAzure), + markerId: MarkerId('leader initial position'), + position: locationToLatLng(beacon.route!.first!)); + + _hikeMarkers.add(marker); + + // handling polyline here + for (var point in beacon.route!) { + _points.add(locationToLatLng(point!)); + } + _polyline.add(Polyline( + polylineId: PolylineId('leader path'), + color: kYellow, + width: 5, + points: _points)); + } + + // // adding landmarks + if (beacon.landmarks != null) { + for (var landmark in beacon.landmarks!) { + await _createLandMarkMarker(landmark!); + } + } + + await locationUpdateSubscription(beacon.id!); + await _getlocation(); + + emit(LoadedLocationState( + polyline: _polyline, + geofence: _geofence, + locationMarkers: _hikeMarkers, + mapType: _mapType, + message: 'Welcome to hike!')); + } + + Future _getlocation() async { + if (_streamLocaitonData != null) { + _streamLocaitonData!.cancel(); + } + + Location location = new Location(); + location.changeSettings( + interval: 5000, accuracy: LocationAccuracy.high, distanceFilter: 0); + + _streamLocaitonData = + location.onLocationChanged.listen((LocationData newPosition) async { + var latLng = locationDataToLatLng(newPosition); + + if (_lastLocation == null) { + _lastLocation = newPosition; + _points.add(latLng); + } else { + final distance = await locationService.calculateDistance( + latLng, + locationDataToLatLng(_lastLocation!), + ); + + if (distance < 10) { + return; + } + // is Leader + + if (_beacon!.leader!.id == localApi.userModel.id) { + _lastLocation = newPosition; + var updatedUser = _currentUser! + .copywith(location: locationDataToLocationEntity(newPosition)); + _currentUser = updatedUser; + _points.add(latLng); + var newPolyline = await setPolyline(); + log('existes: $newPolyline'); + if (newPolyline == false) return; + + getLeaderAddress(latLng); + _hikeUseCase.changeUserLocation(_beaconId!, latLng); + } + // is follower + else { + _lastLocation = newPosition; + var updatedUser = _currentUser! + .copywith(location: locationDataToLocationEntity(newPosition)); + _currentUser = updatedUser; + // updating location of marker + _createUserMarker(_currentUser!); + + _hikeUseCase.changeUserLocation(_beaconId!, latLng); + } + + emit(LoadedLocationState( + geofence: _geofence, + locationMarkers: _hikeMarkers, + polyline: _polyline, + version: DateTime.now().millisecondsSinceEpoch, + mapType: _mapType, + )); + } + }); + } + + Future setPolyline() async { + PolylinePoints polylinePoints = PolylinePoints(); + try { + PolylineResult result = await polylinePoints.getRouteBetweenCoordinates( + 'AIzaSyBdIpiEfBE5DohHgBvwPTljZQAcNWcKwCs', + PointLatLng(_points.first.latitude, _points.first.longitude), + PointLatLng(_points.last.longitude, _points.last.longitude)); + + log(result.toString()); + + if (result.points.isNotEmpty) { + _polyline.clear(); + _polyline.add(Polyline( + polylineId: PolylineId('leader path'), + points: pointLatLngToLatLng(result.points), + width: 5, + color: kYellow)); + + emit(LoadedLocationState( + geofence: _geofence, + locationMarkers: _hikeMarkers, + polyline: _polyline, + version: DateTime.now().millisecondsSinceEpoch, + mapType: _mapType, + )); + return true; + } else { + return false; + } + } catch (e) { + log('plyresult: $e'); + return false; + } + } + + List pointLatLngToLatLng(List pointLatLngs) { + List newpoints = []; + + for (var pointLatLng in pointLatLngs) { + newpoints.add(LatLng(pointLatLng.latitude, pointLatLng.longitude)); + } + + return newpoints; + } + + void changeCameraPosition(String id) { + var marker = _hikeMarkers.where((marker) => marker.markerId.value == id); + if (marker.isEmpty) return; + mapController!.moveCamera(CameraUpdate.newCameraPosition( + CameraPosition(target: marker.first.position, zoom: 15))); + } + + void focusUser(String userId) { + LatLng? latlng; + if (userId == _leader!.id) { + latlng = locationToLatLng(_leader!.location!); + } else { + _followers.forEach((element) { + if (element!.id == userId) { + latlng = locationToLatLng(element.location!); + } + }); + } + + mapController!.animateCamera(CameraUpdate.newCameraPosition( + CameraPosition(target: latlng!, zoom: 100))); + } + + LatLngBounds calculateMapBoundsFromListOfLatLng(List pointsList, + {double padding = 0.0005}) { + double southWestLatitude = 90; + double southWestLongitude = 90; + double northEastLatitude = -180; + double northEastLongitude = -180; + pointsList.forEach((point) { + if (point.latitude < southWestLatitude) { + southWestLatitude = point.latitude; + } + if (point.longitude < southWestLongitude) { + southWestLongitude = point.longitude; + } + if (point.latitude > northEastLatitude) { + northEastLatitude = point.latitude; + } + if (point.longitude > northEastLongitude) { + northEastLongitude = point.longitude; + } + }); + southWestLatitude = southWestLatitude - padding; + southWestLongitude = southWestLongitude - padding; + northEastLatitude = northEastLatitude + padding; + northEastLongitude = northEastLongitude + padding; + LatLngBounds bound = LatLngBounds( + southwest: LatLng(southWestLatitude, southWestLongitude), + northeast: LatLng(northEastLatitude, northEastLongitude)); + return bound; + } + + Future locationUpdateSubscription(String beaconId) async { + _beaconlocationsSubscription?.cancel(); + + _beaconlocationsSubscription = _hikeUseCase + .beaconlocationsSubscription(beaconId) + .listen((dataState) async { + if (dataState is DataSuccess && dataState.data != null) { + BeaconLocationsEntity beaconLocationsEntity = dataState.data!; + + // when new landmark is created + if (beaconLocationsEntity.landmark != null) { + LandMarkEntity newLandMark = beaconLocationsEntity.landmark!; + + await _createLandMarkMarker(newLandMark); + + emit(LoadedLocationState( + polyline: _polyline, + locationMarkers: _hikeMarkers, + mapType: _mapType, + geofence: _geofence, + version: DateTime.now().millisecond, + message: + 'A landmark is created by ${beaconLocationsEntity.landmark!.createdBy!.name ?? 'Anonymous'}')); + } + // when new position of user detected + else if (beaconLocationsEntity.user != null) { + // location of follower + UserEntity userlocation = beaconLocationsEntity.user!; + + _createUserMarker(userlocation); + + emit(LoadedLocationState( + polyline: _polyline, + geofence: _geofence, + locationMarkers: _hikeMarkers, + mapType: _mapType, + version: DateTime.now().microsecond)); + // add marker for user + } + + // when new route recieved + + else if (beaconLocationsEntity.route != null) { + log('getting new route'); + _points.clear(); + for (var route in beaconLocationsEntity.route!) { + if (route == null) { + log('route is null'); + } else { + _points.add(locationToLatLng(route)); + } + } + + log('points len: ${_points.length.toString()}'); + + _polyline.add(Polyline( + polylineId: PolylineId('leader path'), + points: _points, + width: 5, + color: kYellow)); + + var markers = _hikeMarkers + .where((marker) => marker.markerId.value == _leader!.id) + .toList(); + + if (markers.isEmpty) { + _hikeMarkers.add(Marker( + markerId: MarkerId(_beacon!.leader!.id.toString()), + position: _points.last)); + } + var leaderRipplingMarker = markers.first; + + _hikeMarkers.remove(leaderRipplingMarker); + + var newMarker = + leaderRipplingMarker.copyWith(positionParam: _points.last); + + _hikeMarkers.add(newMarker); + + // finding initial position marker of route + + var initialMarkers = _hikeMarkers.where( + (marker) => marker.markerId.value == 'leader initial position'); + + if (initialMarkers.isEmpty) { + _hikeMarkers.add(RippleMarker( + markerId: MarkerId('leader initial position'), + ripple: false, + infoWindow: InfoWindow(title: 'Leader initial position'), + position: _points.first)); + } + + mapController!.animateCamera(CameraUpdate.newLatLngBounds( + calculateMapBoundsFromListOfLatLng(_points), 50)); + + emit(LoadedLocationState( + geofence: _geofence, + locationMarkers: _hikeMarkers, + mapType: _mapType, + polyline: _polyline, + version: DateTime.now().millisecondsSinceEpoch)); + } else if (beaconLocationsEntity.userSOS != null) { + var user = beaconLocationsEntity.userSOS!; + + ScaffoldMessenger.of(context!).showSnackBar( + SnackBar( + duration: Duration(seconds: 5), + content: Row( + children: [ + Image.asset( + 'images/male_avatar.png', + height: 35, + ), + Gap(10), + Text( + '${user.name ?? 'Anonymous'} might be in trouble!', + style: TextStyle(color: Colors.black, fontSize: 15), + ), + Spacer(), + IconButton( + onPressed: () { + mapController?.animateCamera( + CameraUpdate.newCameraPosition(CameraPosition( + target: locationToLatLng(user.location!), + zoom: 15))); + }, + icon: Icon( + Icons.location_on, + color: Colors.red, + )) + ], + ), + backgroundColor: kLightBlue.withOpacity(0.8), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(10), + ), + ), + margin: + EdgeInsets.only(top: 0, right: 10, left: 10, bottom: 85.h), + behavior: SnackBarBehavior.floating, + elevation: 5, + ), + ); + var sosUser = beaconLocationsEntity.userSOS!; + startAnimation(sosUser); + + vibrateWithPattern(); + + // var marker = _hikeMarkers + // .firstWhere((marker) => marker.markerId.value == user!.id); + + // _hikeMarkers + // .removeWhere((hmarker) => hmarker.markerId == marker.markerId); + } + } + }); + } + + void vibrateWithPattern() async { + if (await Vibration.hasVibrator() == true) { + Vibration.vibrate( + pattern: [500, 1000, 500, 2000], // Vibration pattern + intensities: [128, 255, 128, 255], // Optional intensities + ); + } + } + + void startAnimation(UserEntity user) { + _controller = AnimationController( + duration: const Duration(seconds: 4), + vsync: vsync!, + ); + + // Define the radius animation with CurvedAnimation + _animation = Tween(begin: 10, end: 100).animate( + CurvedAnimation( + parent: _controller!, + curve: Curves.easeInOut, + ), + )..addListener(() { + _updateCircleRadius(user); + + emit(LoadedLocationState( + geofence: circles, + locationMarkers: _hikeMarkers, + mapType: _mapType, + polyline: _polyline, + version: DateTime.now().microsecondsSinceEpoch)); + }); + + // Start the animation + _controller!.repeat(reverse: true); + Future.delayed((Duration(seconds: 10)), () { + circles.clear(); + _controller?.stop(); + emit(LoadedLocationState( + geofence: circles, + locationMarkers: _hikeMarkers, + mapType: _mapType, + polyline: _polyline, + version: DateTime.now().microsecond)); + }); + } + + void _updateCircleRadius(UserEntity user) { + circles = _buildRipplingCircles(user).toSet(); + } + + Set circles = {}; + List _buildRipplingCircles(UserEntity user) { + var circleList = List.generate(3, (index) { + final double radius = _animation!.value - (index * 8); + return Circle( + circleId: CircleId('rippleCircle$index'), + center: locationToLatLng(user.location!), + radius: radius < 0 ? 0 : radius, + fillColor: Colors.red.withOpacity((0.5).clamp(0.0, 1.0)), + strokeColor: Colors.red.withOpacity(0.5), + strokeWidth: 2, + ); + }); + + circleList.add(Circle( + circleId: CircleId('rippleCirclex'), + center: locationToLatLng(user.location!), + radius: 10, + fillColor: Colors.red, + strokeColor: Colors.red, + strokeWidth: 2, + )); + return circleList; + } + + stopAnimation() { + _controller?.stop(); + circles.clear(); + emit(LoadedLocationState( + geofence: _geofence, + locationMarkers: _hikeMarkers, + mapType: _mapType, + polyline: _polyline, + version: DateTime.now().microsecondsSinceEpoch)); + } + + Future getLeaderAddress(LatLng latlng) async { + try { + log('leader func'); + var headers = { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Credentials': 'true', + 'Access-Control-Allow-Headers': 'Content-Type', + 'Access-Control-Allow-Methods': 'GET,PUT,POST,DELETE' + }; + var response = await http.post( + Uri.parse( + 'https://geocode.maps.co/reverse?lat=${latlng.latitude}&lon=${latlng.longitude}&api_key=6696ae9d4ebc2317438148rjq134731'), + headers: headers); + + log(response.toString()); + + if (response.statusCode == 200) { + final data = jsonDecode(response.body); + + final addressString = data['address']; + final city = addressString['city']; + final county = addressString['county']; + final stateDistrict = addressString['state_district']; + final state = addressString['state']; + final postcode = addressString['postcode']; + final country = addressString['country']; + + address = + cleanAddress(city, county, stateDistrict, state, postcode, country); + + if (address == null) return; + + locator().emitAddressState(address!); + } + } catch (e) { + log(e.toString()); + } + } + + String? cleanAddress(String? city, String? county, String? stateDistrict, + String? state, String? postcode, String? country) { + List components = [ + city, + county, + stateDistrict, + state, + postcode, + country + ]; + + List filteredComponents = components + .where((component) => component != null && component.isNotEmpty) + .toList() + .cast(); + String _address = filteredComponents.join(', '); + + return _address.isNotEmpty ? _address : null; + } + + Future createLandmark( + String beaconId, String title, LatLng latlng) async { + var dataState = await _hikeUseCase.createLandMark(beaconId, title, + latlng.latitude.toString(), latlng.longitude.toString()); + + if (dataState is DataSuccess && dataState.data != null) { + await _createLandMarkMarker(dataState.data!); + emit(LoadedLocationState( + polyline: _polyline, + geofence: _geofence, + mapType: _mapType, + locationMarkers: Set.from(_hikeMarkers), + message: 'New marker created by ${dataState.data!.createdBy!.name}')); + } + } + + Future sendSOS(String id, BuildContext context) async { + final dataState = await _hikeUseCase.sos(id); + + if (dataState is DataSuccess) { + log('data coming from sos: ${dataState.data.toString()}'); + // // Ensure _hikeMarkers is a Set of marker objects + + var userId = localApi.userModel.id; + var marker = + _hikeMarkers.firstWhere((marker) => marker.markerId.value == userId); + + _hikeMarkers.removeWhere( + (hmarker) => hmarker.markerId.value == marker.markerId.value, + ); + + _hikeMarkers.add(RippleMarker( + markerId: marker.mapsId, + position: marker.position, + infoWindow: marker.infoWindow, + ripple: true, + )); + + startAnimation(dataState.data!); + + emit(LoadedLocationState( + geofence: _geofence, + locationMarkers: _hikeMarkers, + mapType: _mapType, + message: 'SOS is send with your current\ location!', + polyline: _polyline, + version: DateTime.now().millisecond, + )); + } else { + utils.showSnackBar('Beacon is not active anymore!', context); + } + } + + Future _createLandMarkMarker(LandMarkEntity landMark) async { + final markerId = MarkerId(landMark.id!); + final markerPosition = locationToLatLng(landMark.location!); + + final existingMarkers = + _hikeMarkers.where((element) => element.markerId == markerId); + + if (existingMarkers.isEmpty) { + var newMarker = await createMarker(landMark); + _hikeMarkers.add(newMarker); + } else { + // If the marker exists, update its position + final updatedMarker = existingMarkers.first.copyWith( + positionParam: markerPosition, + ); + _hikeMarkers + ..remove(existingMarkers.first) + ..add(updatedMarker); + } + } + + void _createUserMarker(UserEntity user, {bool isLeader = false}) async { + final markerId = MarkerId(user.id!); + final markerPosition = locationToLatLng(user.location!); + + // final bitmap = await _createCustomMarkerBitmap(); + + final existingMarkers = + _hikeMarkers.where((element) => element.markerId == markerId); + + if (existingMarkers.isEmpty) { + // If the marker does not exist, create and add a new one + final newMarker = Marker( + markerId: markerId, + position: markerPosition, + infoWindow: InfoWindow(title: user.name ?? 'Anonymous'), + icon: BitmapDescriptor.defaultMarkerWithHue( + isLeader ? BitmapDescriptor.hueRed : BitmapDescriptor.hueOrange)); + _hikeMarkers.add(newMarker); + } else { + // If the marker exists, update its position + final updatedMarker = existingMarkers.first.copyWith( + positionParam: markerPosition, + ); + _hikeMarkers + ..remove(existingMarkers.first) + ..add(updatedMarker); + } + } + + Future createMarker(LandMarkEntity landmark) async { + final pictureRecorder = PictureRecorder(); + final canvas = Canvas(pictureRecorder); + final customMarker = CustomMarker(text: landmark.title!); + customMarker.paint(canvas, Size(100, 100)); + final picture = pictureRecorder.endRecording(); + final image = await picture.toImage(100, 100); + final bytes = await image.toByteData(format: ImageByteFormat.png); + + return Marker( + markerId: MarkerId(landmark.id!.toString()), + position: locationToLatLng(landmark.location!), + icon: BitmapDescriptor.bytes(bytes!.buffer.asUint8List()), + infoWindow: InfoWindow( + title: 'Created by: ${landmark.createdBy?.name ?? 'Anonymous'}', + ), + ); + } + + void changeGeofenceRadius(double radius, LatLng center) { + var index = _geofence + .toList() + .indexWhere((element) => element.circleId.value == 'geofence'); + + if (index >= 0) { + var newGeofence = _geofence.toList()[index].copyWith(radiusParam: radius); + _geofence.removeWhere((element) => element.circleId.value == 'geofence'); + _geofence.add(newGeofence); + } else { + _geofence.add(Circle( + circleId: CircleId('geofence'), + center: center, + radius: radius, + strokeColor: Colors.blue, + strokeWidth: 2, + fillColor: Colors.blue.withOpacity(0.1), + )); + } + emit(LoadedLocationState( + polyline: _polyline, + locationMarkers: _hikeMarkers, + geofence: _geofence, + mapType: _mapType, + version: DateTime.now().microsecond)); + } + + void removeUncreatedGeofence() { + _geofence.removeWhere((geofence) { + return geofence.circleId.value == 'geofence'; + }); + + emit(LoadedLocationState( + polyline: _polyline, + locationMarkers: _hikeMarkers, + geofence: _geofence, + mapType: _mapType, + version: DateTime.now().microsecond)); + } + + LatLng locationToLatLng(LocationEntity location) { + return LatLng(stringTodouble(location.lat!), stringTodouble(location.lon!)); + } + + double stringTodouble(String coord) { + return double.parse(coord); + } + + void changeMap(MapType mapType) { + if (mapType == _mapType) return; + _mapType = mapType; + + emit(LoadedLocationState( + polyline: _polyline, + locationMarkers: _hikeMarkers, + geofence: _geofence, + mapType: mapType, + )); + } + + LatLng locationDataToLatLng(LocationData locationData) { + return LatLng(locationData.latitude!, locationData.longitude!); + } + + LocationEntity locationDataToLocationEntity(LocationData locationData) { + return LocationEntity( + lat: locationData.latitude.toString(), + lon: locationData.longitude.toString()); + } + + clear() { + _points.clear(); + _polyline.clear(); + _geofence.clear(); + _followers.clear(); + context = null; + vsync = null; + _leader = null; + _beacon = null; + _beaconId = null; + _mapType = MapType.normal; + _beaconlocationsSubscription?.cancel(); + _streamLocaitonData?.cancel(); + mapController?.dispose(); + _hikeMarkers.clear(); + } +} diff --git a/lib/presentation/hike/cubit/location_cubit/location_state.dart b/lib/presentation/hike/cubit/location_cubit/location_state.dart new file mode 100644 index 00000000..a977d92a --- /dev/null +++ b/lib/presentation/hike/cubit/location_cubit/location_state.dart @@ -0,0 +1,18 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +part 'location_state.freezed.dart'; + +@freezed +class LocationState with _$LocationState { + factory LocationState.initial() = InitialLocationState; + factory LocationState.loaded({ + @Default(MapType.normal) MapType mapType, + @Default({}) Set geofence, + @Default({}) Set locationMarkers, + @Default({}) Set polyline, + String? message, + @Default(0) int version, + }) = LoadedLocationState; + + factory LocationState.error({String? message}) = LocationErrorState; +} diff --git a/lib/presentation/hike/cubit/location_cubit/location_state.freezed.dart b/lib/presentation/hike/cubit/location_cubit/location_state.freezed.dart new file mode 100644 index 00000000..f9cdabef --- /dev/null +++ b/lib/presentation/hike/cubit/location_cubit/location_state.freezed.dart @@ -0,0 +1,655 @@ +// 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 'location_state.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 _$LocationState { + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function( + MapType mapType, + Set geofence, + Set locationMarkers, + Set polyline, + String? message, + int version) + loaded, + required TResult Function(String? message) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function( + MapType mapType, + Set geofence, + Set locationMarkers, + Set polyline, + String? message, + int version)? + loaded, + TResult? Function(String? message)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function( + MapType mapType, + Set geofence, + Set locationMarkers, + Set polyline, + String? message, + int version)? + loaded, + TResult Function(String? message)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(InitialLocationState value) initial, + required TResult Function(LoadedLocationState value) loaded, + required TResult Function(LocationErrorState value) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialLocationState value)? initial, + TResult? Function(LoadedLocationState value)? loaded, + TResult? Function(LocationErrorState value)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialLocationState value)? initial, + TResult Function(LoadedLocationState value)? loaded, + TResult Function(LocationErrorState value)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $LocationStateCopyWith<$Res> { + factory $LocationStateCopyWith( + LocationState value, $Res Function(LocationState) then) = + _$LocationStateCopyWithImpl<$Res, LocationState>; +} + +/// @nodoc +class _$LocationStateCopyWithImpl<$Res, $Val extends LocationState> + implements $LocationStateCopyWith<$Res> { + _$LocationStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; +} + +/// @nodoc +abstract class _$$InitialLocationStateImplCopyWith<$Res> { + factory _$$InitialLocationStateImplCopyWith(_$InitialLocationStateImpl value, + $Res Function(_$InitialLocationStateImpl) then) = + __$$InitialLocationStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$InitialLocationStateImplCopyWithImpl<$Res> + extends _$LocationStateCopyWithImpl<$Res, _$InitialLocationStateImpl> + implements _$$InitialLocationStateImplCopyWith<$Res> { + __$$InitialLocationStateImplCopyWithImpl(_$InitialLocationStateImpl _value, + $Res Function(_$InitialLocationStateImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$InitialLocationStateImpl implements InitialLocationState { + _$InitialLocationStateImpl(); + + @override + String toString() { + return 'LocationState.initial()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$InitialLocationStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function( + MapType mapType, + Set geofence, + Set locationMarkers, + Set polyline, + String? message, + int version) + loaded, + required TResult Function(String? message) error, + }) { + return initial(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function( + MapType mapType, + Set geofence, + Set locationMarkers, + Set polyline, + String? message, + int version)? + loaded, + TResult? Function(String? message)? error, + }) { + return initial?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function( + MapType mapType, + Set geofence, + Set locationMarkers, + Set polyline, + String? message, + int version)? + loaded, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (initial != null) { + return initial(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialLocationState value) initial, + required TResult Function(LoadedLocationState value) loaded, + required TResult Function(LocationErrorState value) error, + }) { + return initial(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialLocationState value)? initial, + TResult? Function(LoadedLocationState value)? loaded, + TResult? Function(LocationErrorState value)? error, + }) { + return initial?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialLocationState value)? initial, + TResult Function(LoadedLocationState value)? loaded, + TResult Function(LocationErrorState value)? error, + required TResult orElse(), + }) { + if (initial != null) { + return initial(this); + } + return orElse(); + } +} + +abstract class InitialLocationState implements LocationState { + factory InitialLocationState() = _$InitialLocationStateImpl; +} + +/// @nodoc +abstract class _$$LoadedLocationStateImplCopyWith<$Res> { + factory _$$LoadedLocationStateImplCopyWith(_$LoadedLocationStateImpl value, + $Res Function(_$LoadedLocationStateImpl) then) = + __$$LoadedLocationStateImplCopyWithImpl<$Res>; + @useResult + $Res call( + {MapType mapType, + Set geofence, + Set locationMarkers, + Set polyline, + String? message, + int version}); +} + +/// @nodoc +class __$$LoadedLocationStateImplCopyWithImpl<$Res> + extends _$LocationStateCopyWithImpl<$Res, _$LoadedLocationStateImpl> + implements _$$LoadedLocationStateImplCopyWith<$Res> { + __$$LoadedLocationStateImplCopyWithImpl(_$LoadedLocationStateImpl _value, + $Res Function(_$LoadedLocationStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? mapType = null, + Object? geofence = null, + Object? locationMarkers = null, + Object? polyline = null, + Object? message = freezed, + Object? version = null, + }) { + return _then(_$LoadedLocationStateImpl( + mapType: null == mapType + ? _value.mapType + : mapType // ignore: cast_nullable_to_non_nullable + as MapType, + geofence: null == geofence + ? _value._geofence + : geofence // ignore: cast_nullable_to_non_nullable + as Set, + locationMarkers: null == locationMarkers + ? _value._locationMarkers + : locationMarkers // ignore: cast_nullable_to_non_nullable + as Set, + polyline: null == polyline + ? _value._polyline + : polyline // ignore: cast_nullable_to_non_nullable + as Set, + message: freezed == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String?, + version: null == version + ? _value.version + : version // ignore: cast_nullable_to_non_nullable + as int, + )); + } +} + +/// @nodoc + +class _$LoadedLocationStateImpl implements LoadedLocationState { + _$LoadedLocationStateImpl( + {this.mapType = MapType.normal, + final Set geofence = const {}, + final Set locationMarkers = const {}, + final Set polyline = const {}, + this.message, + this.version = 0}) + : _geofence = geofence, + _locationMarkers = locationMarkers, + _polyline = polyline; + + @override + @JsonKey() + final MapType mapType; + final Set _geofence; + @override + @JsonKey() + Set get geofence { + if (_geofence is EqualUnmodifiableSetView) return _geofence; + // ignore: implicit_dynamic_type + return EqualUnmodifiableSetView(_geofence); + } + + final Set _locationMarkers; + @override + @JsonKey() + Set get locationMarkers { + if (_locationMarkers is EqualUnmodifiableSetView) return _locationMarkers; + // ignore: implicit_dynamic_type + return EqualUnmodifiableSetView(_locationMarkers); + } + + final Set _polyline; + @override + @JsonKey() + Set get polyline { + if (_polyline is EqualUnmodifiableSetView) return _polyline; + // ignore: implicit_dynamic_type + return EqualUnmodifiableSetView(_polyline); + } + + @override + final String? message; + @override + @JsonKey() + final int version; + + @override + String toString() { + return 'LocationState.loaded(mapType: $mapType, geofence: $geofence, locationMarkers: $locationMarkers, polyline: $polyline, message: $message, version: $version)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$LoadedLocationStateImpl && + (identical(other.mapType, mapType) || other.mapType == mapType) && + const DeepCollectionEquality().equals(other._geofence, _geofence) && + const DeepCollectionEquality() + .equals(other._locationMarkers, _locationMarkers) && + const DeepCollectionEquality().equals(other._polyline, _polyline) && + (identical(other.message, message) || other.message == message) && + (identical(other.version, version) || other.version == version)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + mapType, + const DeepCollectionEquality().hash(_geofence), + const DeepCollectionEquality().hash(_locationMarkers), + const DeepCollectionEquality().hash(_polyline), + message, + version); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$LoadedLocationStateImplCopyWith<_$LoadedLocationStateImpl> get copyWith => + __$$LoadedLocationStateImplCopyWithImpl<_$LoadedLocationStateImpl>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function( + MapType mapType, + Set geofence, + Set locationMarkers, + Set polyline, + String? message, + int version) + loaded, + required TResult Function(String? message) error, + }) { + return loaded( + mapType, geofence, locationMarkers, polyline, message, version); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function( + MapType mapType, + Set geofence, + Set locationMarkers, + Set polyline, + String? message, + int version)? + loaded, + TResult? Function(String? message)? error, + }) { + return loaded?.call( + mapType, geofence, locationMarkers, polyline, message, version); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function( + MapType mapType, + Set geofence, + Set locationMarkers, + Set polyline, + String? message, + int version)? + loaded, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (loaded != null) { + return loaded( + mapType, geofence, locationMarkers, polyline, message, version); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialLocationState value) initial, + required TResult Function(LoadedLocationState value) loaded, + required TResult Function(LocationErrorState value) error, + }) { + return loaded(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialLocationState value)? initial, + TResult? Function(LoadedLocationState value)? loaded, + TResult? Function(LocationErrorState value)? error, + }) { + return loaded?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialLocationState value)? initial, + TResult Function(LoadedLocationState value)? loaded, + TResult Function(LocationErrorState value)? error, + required TResult orElse(), + }) { + if (loaded != null) { + return loaded(this); + } + return orElse(); + } +} + +abstract class LoadedLocationState implements LocationState { + factory LoadedLocationState( + {final MapType mapType, + final Set geofence, + final Set locationMarkers, + final Set polyline, + final String? message, + final int version}) = _$LoadedLocationStateImpl; + + MapType get mapType; + Set get geofence; + Set get locationMarkers; + Set get polyline; + String? get message; + int get version; + @JsonKey(ignore: true) + _$$LoadedLocationStateImplCopyWith<_$LoadedLocationStateImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$LocationErrorStateImplCopyWith<$Res> { + factory _$$LocationErrorStateImplCopyWith(_$LocationErrorStateImpl value, + $Res Function(_$LocationErrorStateImpl) then) = + __$$LocationErrorStateImplCopyWithImpl<$Res>; + @useResult + $Res call({String? message}); +} + +/// @nodoc +class __$$LocationErrorStateImplCopyWithImpl<$Res> + extends _$LocationStateCopyWithImpl<$Res, _$LocationErrorStateImpl> + implements _$$LocationErrorStateImplCopyWith<$Res> { + __$$LocationErrorStateImplCopyWithImpl(_$LocationErrorStateImpl _value, + $Res Function(_$LocationErrorStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? message = freezed, + }) { + return _then(_$LocationErrorStateImpl( + message: freezed == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc + +class _$LocationErrorStateImpl implements LocationErrorState { + _$LocationErrorStateImpl({this.message}); + + @override + final String? message; + + @override + String toString() { + return 'LocationState.error(message: $message)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$LocationErrorStateImpl && + (identical(other.message, message) || other.message == message)); + } + + @override + int get hashCode => Object.hash(runtimeType, message); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$LocationErrorStateImplCopyWith<_$LocationErrorStateImpl> get copyWith => + __$$LocationErrorStateImplCopyWithImpl<_$LocationErrorStateImpl>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function( + MapType mapType, + Set geofence, + Set locationMarkers, + Set polyline, + String? message, + int version) + loaded, + required TResult Function(String? message) error, + }) { + return error(message); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function( + MapType mapType, + Set geofence, + Set locationMarkers, + Set polyline, + String? message, + int version)? + loaded, + TResult? Function(String? message)? error, + }) { + return error?.call(message); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function( + MapType mapType, + Set geofence, + Set locationMarkers, + Set polyline, + String? message, + int version)? + loaded, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(message); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialLocationState value) initial, + required TResult Function(LoadedLocationState value) loaded, + required TResult Function(LocationErrorState value) error, + }) { + return error(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialLocationState value)? initial, + TResult? Function(LoadedLocationState value)? loaded, + TResult? Function(LocationErrorState value)? error, + }) { + return error?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialLocationState value)? initial, + TResult Function(LoadedLocationState value)? loaded, + TResult Function(LocationErrorState value)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(this); + } + return orElse(); + } +} + +abstract class LocationErrorState implements LocationState { + factory LocationErrorState({final String? message}) = + _$LocationErrorStateImpl; + + String? get message; + @JsonKey(ignore: true) + _$$LocationErrorStateImplCopyWith<_$LocationErrorStateImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/presentation/hike/cubit/panel_cubit/panel_cubit.dart b/lib/presentation/hike/cubit/panel_cubit/panel_cubit.dart new file mode 100644 index 00000000..75225c57 --- /dev/null +++ b/lib/presentation/hike/cubit/panel_cubit/panel_cubit.dart @@ -0,0 +1,166 @@ +import 'dart:convert'; +import 'dart:developer'; + +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/domain/entities/subscriptions/join_leave_beacon_entity/join_leave_beacon_entity.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:beacon/domain/usecase/hike_usecase.dart'; +import 'package:beacon/presentation/hike/cubit/panel_cubit/panel_state.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:intl/intl.dart'; +import 'package:http/http.dart' as http; + +class PanelCubit extends Cubit { + final HikeUseCase _hikeUseCase; + static PanelCubit? _instance; + + PanelCubit._internal(this._hikeUseCase) : super(SlidingPanelState.initial()); + + factory PanelCubit(HikeUseCase hikeUseCase) { + return _instance ??= PanelCubit._internal(hikeUseCase); + } + + BeaconEntity? _beacon; + String? _beaconId; + List _followers = []; + UserEntity? _leader; + + loadCollapsedPanel() { + _beacon!.startsAt; + } + + void loadBeaconData(BeaconEntity beacon) async { + _beacon = beacon; + _beaconId = beacon.id!; + + _followers = beacon.followers ?? []; + _leader = beacon.leader; + + beaconJoinLeaveSubscription(); + + var expiresAT = DateTime.fromMillisecondsSinceEpoch(_beacon!.expiresAt!); + var isBeaconActive = expiresAT.isAfter(DateTime.now()); + + String? expiringTime; + if (isBeaconActive) { + var expireTime = DateFormat('hh:mm a').format(expiresAT); // 02:37 PM + var expireDate = DateFormat('dd/MM/yyyy').format(expiresAT); // 24/03/2023 + expiringTime = '$expireTime, $expireDate'; + } + + emit(SlidingPanelState.loaded( + expiringTime: expiringTime, + isActive: isBeaconActive, + followers: _followers, + leader: _leader, + )); + } + + Future beaconJoinLeaveSubscription() async { + await _hikeUseCase + .joinleavebeaconSubscription(_beaconId!) + .listen((dataState) { + if (dataState is DataSuccess) { + JoinLeaveBeaconEntity joinLeaveBeacon = dataState.data!; + if (joinLeaveBeacon.newfollower != null) { + var newFollower = joinLeaveBeacon.newfollower!; + addUser(newFollower); + emit(SlidingPanelState.loaded( + followers: _followers, + leader: _leader, + message: '${newFollower.name} is now following the beacon!')); + } else if (joinLeaveBeacon.inactiveuser != null) {} + } + }); + } + + void addUser(UserEntity user) { + if (user.id == _beacon!.leader!.id) { + _leader = user; + } else { + _followers.removeWhere((element) => element!.id == user.id); + _followers.add(user); + } + } + + String? _address; + + Future getLeaderAddress(LatLng latlng) async { + try { + var headers = { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Credentials': 'true', + 'Access-Control-Allow-Headers': 'Content-Type', + 'Access-Control-Allow-Methods': 'GET,PUT,POST,DELETE' + }; + var response = await http.post( + Uri.parse( + 'https://geocode.maps.co/reverse?lat=${latlng.latitude}&lon=${latlng.longitude}&api_key=6696ae9d4ebc2317438148rjq134731'), + headers: headers); + + log(response.toString()); + + if (response.statusCode == 200) { + final data = jsonDecode(response.body); + + final addressString = data['address']; + final city = addressString['city']; + final county = addressString['county']; + final stateDistrict = addressString['state_district']; + final state = addressString['state']; + final postcode = addressString['postcode']; + final country = addressString['country']; + + _address = + cleanAddress(city, county, stateDistrict, state, postcode, country); + + if (_address == null) return; + + emitAddressState(_address!); + } + } catch (e) { + log(e.toString()); + } + } + + String? cleanAddress(String? city, String? county, String? stateDistrict, + String? state, String? postcode, String? country) { + List components = [ + city, + county, + stateDistrict, + state, + postcode, + country + ]; + + List filteredComponents = components + .where((component) => component != null && component.isNotEmpty) + .toList() + .cast(); + String _address = filteredComponents.join(', '); + + return _address.isNotEmpty ? _address : null; + } + + emitAddressState(String leaderAddress) { + var expiresAT = DateTime.fromMillisecondsSinceEpoch(_beacon!.expiresAt!); + var isBeaconActive = expiresAT.isAfter(DateTime.now()); + String? expiringTime; + if (isBeaconActive) { + var expireTime = DateFormat('hh:mm a').format(expiresAT); // 02:37 PM + var expireDate = DateFormat('dd/MM/yyyy').format(expiresAT); // 24/03/2023 + expiringTime = '$expireTime, $expireDate'; + } + emit(LoadedPanelState( + expiringTime: expiringTime, + followers: _followers, + isActive: isBeaconActive, + leader: _beacon!.leader, + leaderAddress: leaderAddress, + message: 'leader address changed!')); + } +} diff --git a/lib/presentation/hike/cubit/panel_cubit/panel_state.dart b/lib/presentation/hike/cubit/panel_cubit/panel_state.dart new file mode 100644 index 00000000..482e3570 --- /dev/null +++ b/lib/presentation/hike/cubit/panel_cubit/panel_state.dart @@ -0,0 +1,19 @@ +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'panel_state.freezed.dart'; + +@freezed +class SlidingPanelState with _$SlidingPanelState { + factory SlidingPanelState.initial() = InitialPanelState; + factory SlidingPanelState.loaded({ + bool? isActive, + String? expiringTime, + String? leaderAddress, + UserEntity? leader, + List? followers, + String? message, + }) = LoadedPanelState; + + factory SlidingPanelState.error({String? message}) = ErrorPanelState; +} diff --git a/lib/presentation/hike/cubit/panel_cubit/panel_state.freezed.dart b/lib/presentation/hike/cubit/panel_cubit/panel_state.freezed.dart new file mode 100644 index 00000000..ff5fb374 --- /dev/null +++ b/lib/presentation/hike/cubit/panel_cubit/panel_state.freezed.dart @@ -0,0 +1,653 @@ +// 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 'panel_state.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 _$SlidingPanelState { + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function( + bool? isActive, + String? expiringTime, + String? leaderAddress, + UserEntity? leader, + List? followers, + String? message) + loaded, + required TResult Function(String? message) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function( + bool? isActive, + String? expiringTime, + String? leaderAddress, + UserEntity? leader, + List? followers, + String? message)? + loaded, + TResult? Function(String? message)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function( + bool? isActive, + String? expiringTime, + String? leaderAddress, + UserEntity? leader, + List? followers, + String? message)? + loaded, + TResult Function(String? message)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(InitialPanelState value) initial, + required TResult Function(LoadedPanelState value) loaded, + required TResult Function(ErrorPanelState value) error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialPanelState value)? initial, + TResult? Function(LoadedPanelState value)? loaded, + TResult? Function(ErrorPanelState value)? error, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialPanelState value)? initial, + TResult Function(LoadedPanelState value)? loaded, + TResult Function(ErrorPanelState value)? error, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $SlidingPanelStateCopyWith<$Res> { + factory $SlidingPanelStateCopyWith( + SlidingPanelState value, $Res Function(SlidingPanelState) then) = + _$SlidingPanelStateCopyWithImpl<$Res, SlidingPanelState>; +} + +/// @nodoc +class _$SlidingPanelStateCopyWithImpl<$Res, $Val extends SlidingPanelState> + implements $SlidingPanelStateCopyWith<$Res> { + _$SlidingPanelStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; +} + +/// @nodoc +abstract class _$$InitialPanelStateImplCopyWith<$Res> { + factory _$$InitialPanelStateImplCopyWith(_$InitialPanelStateImpl value, + $Res Function(_$InitialPanelStateImpl) then) = + __$$InitialPanelStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$InitialPanelStateImplCopyWithImpl<$Res> + extends _$SlidingPanelStateCopyWithImpl<$Res, _$InitialPanelStateImpl> + implements _$$InitialPanelStateImplCopyWith<$Res> { + __$$InitialPanelStateImplCopyWithImpl(_$InitialPanelStateImpl _value, + $Res Function(_$InitialPanelStateImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$InitialPanelStateImpl implements InitialPanelState { + _$InitialPanelStateImpl(); + + @override + String toString() { + return 'SlidingPanelState.initial()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$InitialPanelStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function( + bool? isActive, + String? expiringTime, + String? leaderAddress, + UserEntity? leader, + List? followers, + String? message) + loaded, + required TResult Function(String? message) error, + }) { + return initial(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function( + bool? isActive, + String? expiringTime, + String? leaderAddress, + UserEntity? leader, + List? followers, + String? message)? + loaded, + TResult? Function(String? message)? error, + }) { + return initial?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function( + bool? isActive, + String? expiringTime, + String? leaderAddress, + UserEntity? leader, + List? followers, + String? message)? + loaded, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (initial != null) { + return initial(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialPanelState value) initial, + required TResult Function(LoadedPanelState value) loaded, + required TResult Function(ErrorPanelState value) error, + }) { + return initial(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialPanelState value)? initial, + TResult? Function(LoadedPanelState value)? loaded, + TResult? Function(ErrorPanelState value)? error, + }) { + return initial?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialPanelState value)? initial, + TResult Function(LoadedPanelState value)? loaded, + TResult Function(ErrorPanelState value)? error, + required TResult orElse(), + }) { + if (initial != null) { + return initial(this); + } + return orElse(); + } +} + +abstract class InitialPanelState implements SlidingPanelState { + factory InitialPanelState() = _$InitialPanelStateImpl; +} + +/// @nodoc +abstract class _$$LoadedPanelStateImplCopyWith<$Res> { + factory _$$LoadedPanelStateImplCopyWith(_$LoadedPanelStateImpl value, + $Res Function(_$LoadedPanelStateImpl) then) = + __$$LoadedPanelStateImplCopyWithImpl<$Res>; + @useResult + $Res call( + {bool? isActive, + String? expiringTime, + String? leaderAddress, + UserEntity? leader, + List? followers, + String? message}); + + $UserEntityCopyWith<$Res>? get leader; +} + +/// @nodoc +class __$$LoadedPanelStateImplCopyWithImpl<$Res> + extends _$SlidingPanelStateCopyWithImpl<$Res, _$LoadedPanelStateImpl> + implements _$$LoadedPanelStateImplCopyWith<$Res> { + __$$LoadedPanelStateImplCopyWithImpl(_$LoadedPanelStateImpl _value, + $Res Function(_$LoadedPanelStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? isActive = freezed, + Object? expiringTime = freezed, + Object? leaderAddress = freezed, + Object? leader = freezed, + Object? followers = freezed, + Object? message = freezed, + }) { + return _then(_$LoadedPanelStateImpl( + isActive: freezed == isActive + ? _value.isActive + : isActive // ignore: cast_nullable_to_non_nullable + as bool?, + expiringTime: freezed == expiringTime + ? _value.expiringTime + : expiringTime // ignore: cast_nullable_to_non_nullable + as String?, + leaderAddress: freezed == leaderAddress + ? _value.leaderAddress + : leaderAddress // ignore: cast_nullable_to_non_nullable + as String?, + leader: freezed == leader + ? _value.leader + : leader // ignore: cast_nullable_to_non_nullable + as UserEntity?, + followers: freezed == followers + ? _value._followers + : followers // ignore: cast_nullable_to_non_nullable + as List?, + message: freezed == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String?, + )); + } + + @override + @pragma('vm:prefer-inline') + $UserEntityCopyWith<$Res>? get leader { + if (_value.leader == null) { + return null; + } + + return $UserEntityCopyWith<$Res>(_value.leader!, (value) { + return _then(_value.copyWith(leader: value)); + }); + } +} + +/// @nodoc + +class _$LoadedPanelStateImpl implements LoadedPanelState { + _$LoadedPanelStateImpl( + {this.isActive, + this.expiringTime, + this.leaderAddress, + this.leader, + final List? followers, + this.message}) + : _followers = followers; + + @override + final bool? isActive; + @override + final String? expiringTime; + @override + final String? leaderAddress; + @override + final UserEntity? leader; + final List? _followers; + @override + List? get followers { + final value = _followers; + if (value == null) return null; + if (_followers is EqualUnmodifiableListView) return _followers; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + @override + final String? message; + + @override + String toString() { + return 'SlidingPanelState.loaded(isActive: $isActive, expiringTime: $expiringTime, leaderAddress: $leaderAddress, leader: $leader, followers: $followers, message: $message)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$LoadedPanelStateImpl && + (identical(other.isActive, isActive) || + other.isActive == isActive) && + (identical(other.expiringTime, expiringTime) || + other.expiringTime == expiringTime) && + (identical(other.leaderAddress, leaderAddress) || + other.leaderAddress == leaderAddress) && + (identical(other.leader, leader) || other.leader == leader) && + const DeepCollectionEquality() + .equals(other._followers, _followers) && + (identical(other.message, message) || other.message == message)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + isActive, + expiringTime, + leaderAddress, + leader, + const DeepCollectionEquality().hash(_followers), + message); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$LoadedPanelStateImplCopyWith<_$LoadedPanelStateImpl> get copyWith => + __$$LoadedPanelStateImplCopyWithImpl<_$LoadedPanelStateImpl>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function( + bool? isActive, + String? expiringTime, + String? leaderAddress, + UserEntity? leader, + List? followers, + String? message) + loaded, + required TResult Function(String? message) error, + }) { + return loaded( + isActive, expiringTime, leaderAddress, leader, followers, message); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function( + bool? isActive, + String? expiringTime, + String? leaderAddress, + UserEntity? leader, + List? followers, + String? message)? + loaded, + TResult? Function(String? message)? error, + }) { + return loaded?.call( + isActive, expiringTime, leaderAddress, leader, followers, message); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function( + bool? isActive, + String? expiringTime, + String? leaderAddress, + UserEntity? leader, + List? followers, + String? message)? + loaded, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (loaded != null) { + return loaded( + isActive, expiringTime, leaderAddress, leader, followers, message); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialPanelState value) initial, + required TResult Function(LoadedPanelState value) loaded, + required TResult Function(ErrorPanelState value) error, + }) { + return loaded(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialPanelState value)? initial, + TResult? Function(LoadedPanelState value)? loaded, + TResult? Function(ErrorPanelState value)? error, + }) { + return loaded?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialPanelState value)? initial, + TResult Function(LoadedPanelState value)? loaded, + TResult Function(ErrorPanelState value)? error, + required TResult orElse(), + }) { + if (loaded != null) { + return loaded(this); + } + return orElse(); + } +} + +abstract class LoadedPanelState implements SlidingPanelState { + factory LoadedPanelState( + {final bool? isActive, + final String? expiringTime, + final String? leaderAddress, + final UserEntity? leader, + final List? followers, + final String? message}) = _$LoadedPanelStateImpl; + + bool? get isActive; + String? get expiringTime; + String? get leaderAddress; + UserEntity? get leader; + List? get followers; + String? get message; + @JsonKey(ignore: true) + _$$LoadedPanelStateImplCopyWith<_$LoadedPanelStateImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$ErrorPanelStateImplCopyWith<$Res> { + factory _$$ErrorPanelStateImplCopyWith(_$ErrorPanelStateImpl value, + $Res Function(_$ErrorPanelStateImpl) then) = + __$$ErrorPanelStateImplCopyWithImpl<$Res>; + @useResult + $Res call({String? message}); +} + +/// @nodoc +class __$$ErrorPanelStateImplCopyWithImpl<$Res> + extends _$SlidingPanelStateCopyWithImpl<$Res, _$ErrorPanelStateImpl> + implements _$$ErrorPanelStateImplCopyWith<$Res> { + __$$ErrorPanelStateImplCopyWithImpl( + _$ErrorPanelStateImpl _value, $Res Function(_$ErrorPanelStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? message = freezed, + }) { + return _then(_$ErrorPanelStateImpl( + message: freezed == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc + +class _$ErrorPanelStateImpl implements ErrorPanelState { + _$ErrorPanelStateImpl({this.message}); + + @override + final String? message; + + @override + String toString() { + return 'SlidingPanelState.error(message: $message)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ErrorPanelStateImpl && + (identical(other.message, message) || other.message == message)); + } + + @override + int get hashCode => Object.hash(runtimeType, message); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$ErrorPanelStateImplCopyWith<_$ErrorPanelStateImpl> get copyWith => + __$$ErrorPanelStateImplCopyWithImpl<_$ErrorPanelStateImpl>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function( + bool? isActive, + String? expiringTime, + String? leaderAddress, + UserEntity? leader, + List? followers, + String? message) + loaded, + required TResult Function(String? message) error, + }) { + return error(message); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function( + bool? isActive, + String? expiringTime, + String? leaderAddress, + UserEntity? leader, + List? followers, + String? message)? + loaded, + TResult? Function(String? message)? error, + }) { + return error?.call(message); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function( + bool? isActive, + String? expiringTime, + String? leaderAddress, + UserEntity? leader, + List? followers, + String? message)? + loaded, + TResult Function(String? message)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(message); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialPanelState value) initial, + required TResult Function(LoadedPanelState value) loaded, + required TResult Function(ErrorPanelState value) error, + }) { + return error(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialPanelState value)? initial, + TResult? Function(LoadedPanelState value)? loaded, + TResult? Function(ErrorPanelState value)? error, + }) { + return error?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialPanelState value)? initial, + TResult Function(LoadedPanelState value)? loaded, + TResult Function(ErrorPanelState value)? error, + required TResult orElse(), + }) { + if (error != null) { + return error(this); + } + return orElse(); + } +} + +abstract class ErrorPanelState implements SlidingPanelState { + factory ErrorPanelState({final String? message}) = _$ErrorPanelStateImpl; + + String? get message; + @JsonKey(ignore: true) + _$$ErrorPanelStateImplCopyWith<_$ErrorPanelStateImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/presentation/hike/hike_screen.dart b/lib/presentation/hike/hike_screen.dart new file mode 100644 index 00000000..bb8076a5 --- /dev/null +++ b/lib/presentation/hike/hike_screen.dart @@ -0,0 +1,342 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:beacon/config/pip_manager.dart'; +import 'package:beacon/core/utils/constants.dart'; +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:beacon/locator.dart'; +import 'package:beacon/presentation/hike/cubit/hike_cubit/hike_cubit.dart'; +import 'package:beacon/presentation/hike/cubit/hike_cubit/hike_state.dart'; +import 'package:beacon/presentation/hike/cubit/location_cubit/location_cubit.dart'; +import 'package:beacon/presentation/hike/cubit/location_cubit/location_state.dart'; +import 'package:beacon/presentation/hike/cubit/panel_cubit/panel_cubit.dart'; +import 'package:beacon/presentation/hike/cubit/panel_cubit/panel_state.dart'; +import 'package:beacon/presentation/hike/widgets/hike_screen_widget.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:gap/gap.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; +import 'package:simple_pip_mode/pip_widget.dart'; +import 'package:simple_pip_mode/simple_pip.dart'; +import 'package:sliding_up_panel/sliding_up_panel.dart'; + +@RoutePage() +class HikeScreen extends StatefulWidget { + final BeaconEntity beacon; + final bool? isLeader; + const HikeScreen({super.key, required this.beacon, required this.isLeader}); + + @override + State createState() => _HikeScreenState(); +} + +class _HikeScreenState extends State + with TickerProviderStateMixin, WidgetsBindingObserver { + HikeCubit _hikeCubit = locator(); + LocationCubit _locationCubit = locator(); + + int value = 0; + @override + void initState() { + WidgetsBinding.instance.addObserver(this); + _hikeCubit.startHike(widget.beacon.id!, this, context); + SimplePip().setAutoPipMode(aspectRatio: [2, 3]); + super.initState(); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + PIPMode.disablePIPMode(); + _hikeCubit.clear(); + _locationCubit.clear(); + super.dispose(); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + super.didChangeAppLifecycleState(state); + if (state == AppLifecycleState.paused) {} + } + + bool isSmallsized = 100.h < 800; + PanelController _panelController = PanelController(); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: PipWidget( + onPipExited: () { + _panelController.open(); + }, + builder: (context) { + return BlocBuilder( + builder: (context, state) { + if (state is InitialHikeState) { + return Center( + child: SpinKitWave( + color: kYellow, + )); + } else if (state is ErrorHikeState) { + return Container( + child: Center(child: Text('Restart beacon')), + ); + } else { + return Scaffold( + body: Stack( + children: [ + SlidingUpPanel( + onPanelOpened: () { + setState(() {}); + }, + borderRadius: BorderRadius.only( + topRight: Radius.circular(15), + topLeft: Radius.circular(15), + ), + controller: _panelController, + maxHeight: 60.h, + minHeight: isSmallsized ? 22.h : 18.h, + panel: _SlidingPanelWidget(), + collapsed: _collapsedWidget(), + body: _mapScreen()), + Align( + alignment: Alignment(-0.9, -0.9), + child: FloatingActionButton( + heroTag: 'BackButton', + backgroundColor: kYellow, + onPressed: () { + SimplePip().enterPipMode(); + }, + child: Icon( + CupertinoIcons.back, + color: kBlue, + ), + ), + ), + Align( + alignment: Alignment(0.85, -0.9), + child: HikeScreenWidget.shareButton( + context, widget.beacon.shortcode, widget.beacon)), + Align( + alignment: Alignment(1, -0.7), + child: HikeScreenWidget.showMapViewSelector(context)), + Align( + alignment: Alignment(0.85, -0.5), + child: HikeScreenWidget.sosButton( + widget.beacon.id!, context)), + ], + )); + } + }, + ); + }, + pipChild: _mapScreen()), + ); + } + + Widget _mapScreen() { + return BlocConsumer( + listener: (context, state) { + if (state is LoadedLocationState) { + state.message != null + ? utils.showSnackBar(state.message!, context) + : null; + } + }, + builder: (context, state) { + if (state is InitialLocationState) { + return SpinKitPianoWave( + color: kYellow, + ); + } else if (state is LoadedLocationState) { + return GoogleMap( + circles: state.geofence, + polylines: state.polyline, + mapType: state.mapType, + compassEnabled: true, + onTap: (latlng) { + _panelController.close(); + HikeScreenWidget.showCreateLandMarkDialogueDialog( + context, widget.beacon.id!, latlng); + }, + zoomControlsEnabled: true, + onMapCreated: _locationCubit.onMapCreated, + markers: state.locationMarkers, + initialCameraPosition: CameraPosition( + zoom: 15, target: state.locationMarkers.first.position)); + } + return Center( + child: GestureDetector( + onTap: () { + appRouter.maybePop(); + }, + child: Text('Something went wrong please try again!'), + ), + ); + }, + ); + } + + Widget _collapsedWidget() { + var beacon = widget.beacon; + return Container( + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), + decoration: BoxDecoration( + color: kBlue, + borderRadius: BorderRadius.only( + topRight: Radius.circular(10), + topLeft: Radius.circular(10), + ), + ), + child: BlocBuilder( + builder: (context, state) { + return state.when( + initial: () { + return SpinKitCircle(color: kYellow); + }, + loaded: ( + isActive, + expiringTime, + leaderAddress, + leader, + followers, + message, + ) { + followers = followers ?? []; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + alignment: Alignment.center, + child: Container( + alignment: Alignment.center, + height: 0.5.h, + width: 18.w, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: + BorderRadius.all(Radius.circular(10))), + ), + ), + Gap(10), + Text( + isActive == true + ? 'Beacon expiring at ${expiringTime ?? '<>'}' + : 'Beacon is expired', + style: TextStyle( + fontSize: 18, + color: Colors.white, + fontFamily: '', + fontWeight: FontWeight.w700)), + Gap(2), + Text('Beacon leader at: ${leaderAddress ?? '<>'}', + maxLines: 2, + style: TextStyle( + fontSize: 16, + color: Colors.white, + fontFamily: '', + fontWeight: FontWeight.w600)), + Gap(1.5), + Text('Total followers: ${followers.length} ', + style: TextStyle( + fontSize: 16, + color: Colors.white, + fontFamily: '', + fontWeight: FontWeight.w500)), + Gap(1), + Text('Share the pass key to join user: ${beacon.shortcode}', + style: TextStyle( + fontSize: 15, + color: Colors.white, + fontFamily: '', + fontWeight: FontWeight.w500)) + ], + ); + }, + error: (message) { + return Text(message.toString()); + }, + ); + }, + )); + } + + Widget _SlidingPanelWidget() { + // return Container(); + return BlocBuilder( + builder: (context, state) { + return state.when( + initial: () { + return CircularProgressIndicator(); + }, + loaded: ( + isActive, + expiringTime, + leaderAddress, + leader, + followers, + message, + ) { + List members = []; + members.add(leader!); + if (followers != null) { + followers.forEach((element) { + members.add(element!); + }); + } + + return ListView.builder( + physics: NeverScrollableScrollPhysics(), + itemCount: members.length, + itemBuilder: (context, index) { + var member = members[index]; + return Container( + padding: EdgeInsets.symmetric(vertical: 5), + child: Row( + children: [ + Gap(10), + CircleAvatar( + radius: 25, + backgroundColor: kYellow, + child: Icon( + Icons.person_2_rounded, + color: Colors.white, + size: 40, + ), + ), + Gap(10), + Text( + member.name ?? 'Anonymous', + style: TextStyle(fontSize: 19), + ), + Spacer(), + Container( + height: 40, + width: 40, + child: FloatingActionButton( + backgroundColor: kYellow, + onPressed: () async { + _locationCubit + .changeCameraPosition(member.id ?? ''); + _panelController.close(); + }, + child: Icon(Icons.location_city), + ), + ), + Gap(10), + ], + ), + ); + }, + ); + }, + error: (message) { + return Text(message.toString()); + }, + ); + }, + ); + } +} diff --git a/lib/presentation/hike/widgets/active_beacon.dart b/lib/presentation/hike/widgets/active_beacon.dart new file mode 100644 index 00000000..1fe59811 --- /dev/null +++ b/lib/presentation/hike/widgets/active_beacon.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; + +class BlinkIcon extends StatefulWidget { + @override + _BlinkIconState createState() => _BlinkIconState(); +} + +class _BlinkIconState extends State with TickerProviderStateMixin { + late AnimationController _controller; + late Animation _colorAnimation; + @override + void initState() { + _controller = + AnimationController(vsync: this, duration: Duration(milliseconds: 700)); + _colorAnimation = ColorTween( + begin: Color(0xFF30c295), end: Colors.transparent) + .animate(CurvedAnimation(parent: _controller, curve: Curves.linear)); + _controller.addStatusListener((status) { + if (status == AnimationStatus.completed) { + _controller.reverse(); + } else if (status == AnimationStatus.dismissed) { + _controller.forward(); + } + setState(() {}); + }); + _controller.forward(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: _controller, + builder: (context, child) { + return Icon( + Icons.circle, + size: 10, + color: _colorAnimation.value, + ); + }, + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } +} diff --git a/lib/presentation/hike/widgets/hike_screen_widget.dart b/lib/presentation/hike/widgets/hike_screen_widget.dart new file mode 100644 index 00000000..5afd9e7d --- /dev/null +++ b/lib/presentation/hike/widgets/hike_screen_widget.dart @@ -0,0 +1,415 @@ +import 'dart:io'; +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/locator.dart'; +import 'package:beacon/presentation/hike/cubit/location_cubit/location_cubit.dart'; +import 'package:beacon/presentation/widgets/hike_button.dart'; +import 'package:beacon/core/utils/constants.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_geocoder_alternative/flutter_geocoder_alternative.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:gap/gap.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; +import 'package:share_plus/share_plus.dart'; + +class HikeScreenWidget { + static copyPasskey(String? passkey) { + Clipboard.setData(ClipboardData(text: passkey!)); + Fluttertoast.showToast(msg: 'PASSKEY: $passkey COPIED'); + } + + static Geocoder geocoder = Geocoder(); + + static generateUrl(String? shortcode) async { + Uri url = Uri.parse('https://beacon.aadibajpai.com/?shortcode=$shortcode'); + Share.share('To join beacon follow this link: $url'); + } + + static Widget sosButton(String id, BuildContext context) { + return FloatingActionButton( + heroTag: 'sos', + backgroundColor: kYellow, + onPressed: () { + locator().sendSOS(id, context); + }, + child: Icon(Icons.sos), + ); + } + + static Widget shareButton( + BuildContext context, String? passkey, BeaconEntity beacon) { + return FloatingActionButton( + heroTag: + 'shareRouteTag', //had to pass this tag else we would get error since there will be two FAB in the same subtree with the same tag. + onPressed: () { + showDialog( + context: context, + builder: (context) => Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + child: Container( + height: 100.h < 800 ? 40.h : 35.h, + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 32, vertical: 16), + child: Column( + children: [ + Container( + child: Padding( + padding: const EdgeInsets.all(4.0), + child: Text( + 'Invite Friends', + style: TextStyle(fontSize: 24), + ), + ), + ), + SizedBox( + height: 3.5.h, + ), + Flexible( + child: HikeButton( + buttonHeight: optbheight - 4, + textSize: 16, + text: 'Generate URL', + textColor: Colors.white, + buttonColor: kYellow, + onTap: () async { + generateUrl(passkey); + }), + ), + SizedBox( + height: 2.h, + ), + Flexible( + child: HikeButton( + buttonHeight: optbheight - 4, + textSize: 16, + text: 'Copy Passkey', + textColor: Colors.white, + buttonColor: kYellow, + onTap: () { + copyPasskey(passkey); + }, + ), + ), + Gap(2.h), + Flexible( + child: HikeButton( + buttonHeight: optbheight - 4, + textSize: 16, + text: 'Share Image', + textColor: Colors.white, + buttonColor: kYellow, + onTap: () async { + appRouter.maybePop(); + var locationCubit = locator(); + var controller = locationCubit.mapController!; + + if (!await utils.checkInternetConnectivity()) { + utils.showSnackBar( + 'Cannot share the route, please check your internet connection.', + context); + return; + } + + // await mapController.showMarkerInfoWindow(MarkerId("1")); + + final image = await (controller.takeSnapshot()); + final appDir = + await getApplicationDocumentsDirectory(); + File imageFile = + await File('${appDir.path}/shareImage.png') + .create(); + imageFile.writeAsBytesSync(image!); + await Share.shareXFiles([XFile(imageFile.path)], + text: 'Location of ${beacon.leader}', + subject: + 'Current coordinated: ( ${beacon.location?.lat!} ,${beacon.location?.lon} ) \n Address: ${locationCubit.address ?? ''}'); + //hide after sharing. + // await controller.hideMarkerInfoWindow(MarkerId("1")); + return; + }, + ), + ) + ], + ), + ), + ), + ), + ); + }, + backgroundColor: kYellow, + child: Icon(Icons.share), + ); + } + + static Widget shareRouteButton( + BuildContext context, + BeaconEntity? beacon, + GoogleMapController mapController, + List beaconRoute, + ) { + return FloatingActionButton( + heroTag: 'shareRouteTag1', + onPressed: () async {}, + backgroundColor: kYellow, + child: Icon( + Icons.share, + ), + ); + } + + static final Map mapTypeNames = { + MapType.hybrid: 'Hybrid', + MapType.normal: 'Normal', + MapType.satellite: 'Satellite', + MapType.terrain: 'Terrain', + }; + + static MapType _selectedMapType = MapType.normal; + + static Widget showMapViewSelector(BuildContext context) { + return DropdownButton( + icon: null, + value: _selectedMapType, + items: mapTypeNames.entries.map((entry) { + return DropdownMenuItem( + onTap: () { + locator().changeMap(entry.key); + }, + value: entry.key, + child: Text(entry.value), + ); + }).toList(), + onChanged: (newValue) {}, + selectedItemBuilder: (BuildContext context) { + return mapTypeNames.entries.map((entry) { + return FloatingActionButton( + backgroundColor: kYellow, + onPressed: null, + child: Icon(CupertinoIcons.map), + ); + }).toList(); + }, + ); + } + + static TextEditingController _landMarkeController = TextEditingController(); + static GlobalKey _landmarkFormKey = GlobalKey(); + + static void selectionButton( + BuildContext context, String beaconId, LatLng loc) { + showDialog( + context: context, + builder: (context) => Dialog( + child: Container( + height: 100.h < 800 ? 33.h : 22.h, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), + child: Column( + children: [ + Gap(10), + Text( + 'Create Landmark', + style: TextStyle(fontSize: 25), + ), + Gap(20), + HikeButton( + text: 'Create Landmark', + onTap: () { + Navigator.pop(context); + showCreateLandMarkDialogueDialog(context, beaconId, loc); + }, + ), + ], + ), + ), + ), + ), + ); + } + + // static GlobalKey _geofenceKey = GlobalKey(); + // static double _value = 0.1; + + // static void showCreateGeofenceDialogueDialog( + // BuildContext context, + // String beaconId, + // LatLng loc, + // ) { + // bool isSmallSized = 100.h < 800; + // bool isGeofenceCreated = false; + + // showModalBottomSheet( + // context: context, + // isDismissible: false, + // builder: (context) { + // return Stack( + // children: [ + // Container( + // height: isSmallSized ? 30.h : 25.h, + // decoration: BoxDecoration( + // color: Colors.white, + // borderRadius: BorderRadius.only( + // topLeft: Radius.circular(20), + // topRight: Radius.circular(20), + // ), + // ), + // child: Padding( + // padding: + // const EdgeInsets.symmetric(horizontal: 32, vertical: 16), + // child: Form( + // key: _geofenceKey, + // child: Column( + // mainAxisSize: MainAxisSize.min, + // children: [ + // Container( + // alignment: Alignment.topRight, + // child: IconButton( + // onPressed: () { + // appRouter.maybePop().then((value) { + // locator() + // .removeUncreatedGeofence(); + // }); + // }, + // icon: Icon( + // Icons.cancel, + // color: kBlue, + // )), + // ), + // StatefulBuilder( + // builder: (context, setState) { + // return Stack( + // children: [ + // Slider( + // activeColor: kBlue, + // inactiveColor: kYellow, + // value: _value, + // onChanged: (value) { + // setState(() { + // _value = value; + // }); + // locator() + // .changeGeofenceRadius(_value * 1000, loc); + // }, + // ), + // Align( + // alignment: Alignment(1, -1), + // child: Text( + // '${(_value * 1000).toStringAsFixed(0)} meters', + // ), + // ), + // ], + // ); + // }, + // ), + // Gap(10), + // Flexible( + // child: HikeButton( + // buttonHeight: 15, + // onTap: () async { + // if (!_geofenceKey.currentState!.validate()) return; + // locator() + // .createGeofence(beaconId, loc, _value) + // .then((onvalue) { + // isGeofenceCreated = true; + // }); + // appRouter.maybePop().then((onValue) { + // if (isGeofenceCreated) { + // locator() + // .removeUncreatedGeofence(); + // } + // }); + // }, + // text: 'Create Geofence', + // ), + // ), + // ], + // ), + // ), + // ), + // ), + // ], + // ); + // }, + // ); + // } + + static void showCreateLandMarkDialogueDialog( + BuildContext context, + String beaconId, + LatLng loc, + ) { + showDialog( + context: context, + builder: (context) => Dialog( + child: Container( + height: MediaQuery.of(context).size.height < 800 ? 33.h : 25.h, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), + child: Form( + key: _landmarkFormKey, + child: Column( + children: [ + Container( + height: + MediaQuery.of(context).size.height < 800 ? 14.h : 12.h, + child: Padding( + padding: const EdgeInsets.all(4.0), + child: TextFormField( + controller: _landMarkeController, + style: TextStyle(fontSize: 20.0), + onChanged: (key) {}, + validator: (value) { + if (value == null || value.isEmpty) { + return "Please enter title for landmark"; + } else { + return null; + } + }, + decoration: InputDecoration( + border: InputBorder.none, + alignLabelWithHint: true, + floatingLabelBehavior: FloatingLabelBehavior.always, + hintText: 'Add title for the landmark', + hintStyle: + TextStyle(fontSize: hintsize, color: hintColor), + labelText: 'Title', + labelStyle: + TextStyle(fontSize: labelsize, color: kYellow), + ), + ), + ), + color: kLightBlue, + ), + SizedBox( + height: 2.h, + ), + Flexible( + child: HikeButton( + text: 'Create Landmark', + textSize: 14.0, + textColor: Colors.white, + buttonColor: kYellow, + onTap: () { + if (!_landmarkFormKey.currentState!.validate()) return; + appRouter.maybePop(); + locator().createLandmark( + beaconId, _landMarkeController.text, loc); + _landMarkeController.clear(); + }, + ), + ), + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/presentation/home/home_cubit/home_cubit.dart b/lib/presentation/home/home_cubit/home_cubit.dart new file mode 100644 index 00000000..9ec802aa --- /dev/null +++ b/lib/presentation/home/home_cubit/home_cubit.dart @@ -0,0 +1,385 @@ +import 'dart:async'; +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/domain/entities/beacon/beacon_entity.dart'; +import 'package:beacon/domain/entities/group/group_entity.dart'; +import 'package:beacon/domain/entities/subscriptions/updated_group_entity/updated_group_entity.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:beacon/domain/usecase/home_usecase.dart'; +import 'package:beacon/presentation/group/cubit/group_cubit/group_cubit.dart'; +import 'package:beacon/presentation/home/home_cubit/home_state.dart'; +import 'package:beacon/presentation/group/cubit/members_cubit/members_cubit.dart'; +import 'package:beacon/locator.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class HomeCubit extends Cubit { + final HomeUseCase homeUseCase; + static HomeCubit? _instance; + + HomeCubit._internal({required this.homeUseCase}) : super(InitialHomeState()) { + _loadedhomeState = const LoadedHomeState(groups: [], message: null); + _shimmerhomeState = const ShimmerHomeState(); + _loadinghomeState = const LoadingHomeState(); + } + + factory HomeCubit(HomeUseCase homeUseCase) { + if (_instance != null) { + return _instance!; + } else { + _instance = HomeCubit._internal(homeUseCase: homeUseCase); + return _instance!; + } + } + + int _page = 1; + int _pageSize = 4; + bool _hasReachedEnd = false; + List _totalGroups = []; + late List _groupIds; + StreamSubscription>? _groupUpdateSubscription; + bool _isLoadingMore = false; + // this will store the group that user is currently present + String? _currentGroupId; + + late LoadedHomeState _loadedhomeState; + late ShimmerHomeState _shimmerhomeState; + late LoadingHomeState _loadinghomeState; + + Future createGroup(String title) async { + emit(_loadinghomeState); + final dataState = await homeUseCase.createGroup(title); + + if (dataState is DataSuccess && dataState.data != null) { + GroupEntity group = dataState.data!; + List updatedGroups = List.from(_totalGroups) + ..removeWhere((element) => element.id == group.id) + ..insert(0, group); + + _totalGroups = updatedGroups; + + emit(_loadedhomeState.copyWith( + groups: updatedGroups, message: 'New group created!')); + + // adding new id for setting subscription + _groupIds.add(group.id!); + + _groupUpdateSubscription?.cancel(); + _groupSubscription(); + } else { + emit(_loadedhomeState.copyWith( + groups: _totalGroups, message: dataState.error)); + } + } + + Future joinGroup(String shortCode) async { + emit(_loadinghomeState); + DataState dataState = await homeUseCase.joinGroup(shortCode); + + if (dataState is DataSuccess && dataState.data != null) { + GroupEntity? newGroup = dataState.data; + + if (newGroup != null) { + _totalGroups.removeWhere((group) => group.id == newGroup.id); + _totalGroups.insert(0, newGroup); + _groupIds.add(newGroup.id!); + + emit(_loadedhomeState.copyWith( + groups: _totalGroups, + message: 'You are member of ${newGroup.title ?? 'group'}')); + + _groupUpdateSubscription?.cancel(); + _groupSubscription(); + } + } else { + emit(_loadedhomeState.copyWith( + groups: _totalGroups, message: dataState.error)); + } + } + + Future changeShortCode(GroupEntity group) async { + if (group.leader!.id! != localApi.userModel.id) { + emit(_loadinghomeState); + Future.delayed(Duration(milliseconds: 200), () { + emit(_loadedhomeState.copyWith( + groups: _totalGroups, message: 'You are not the leader of group')); + }); + return; + } + emit(_loadinghomeState); + + final dataState = await homeUseCase.changeShortcode(group.id!); + + if (dataState is DataSuccess && dataState.data != null) { + var updatedGroup = dataState.data!; + int index = + _totalGroups.indexWhere((group) => group.id == updatedGroup.id); + + if (index != -1) { + _totalGroups[index] = updatedGroup; + } + emit(_loadedhomeState.copyWith( + groups: _totalGroups, message: 'Short code changed!')); + } else { + emit(_loadedhomeState.copyWith( + groups: _totalGroups, message: dataState.error)); + } + } + + Future fetchUserGroups() async { + if (_hasReachedEnd || _isLoadingMore) return; + + if (_page == 1) { + emit(_shimmerhomeState); + } else { + _isLoadingMore = true; + emit(_loadedhomeState.copyWith( + groups: _totalGroups, isLoadingmore: _isLoadingMore)); + } + + DataState> state = + await homeUseCase.groups(_page, _pageSize); + + if (state is DataFailed) { + emit(_loadedhomeState.copyWith( + groups: _totalGroups, message: state.error!)); + } else if (state is DataSuccess && state.data != null) { + List newGroups = state.data!; + + Map groupMap = { + for (var group in _totalGroups) group.id!: group + }; + + for (var newGroup in newGroups) { + groupMap[newGroup.id!] = newGroup; + } + _totalGroups = groupMap.values.toList(); + _page++; + _isLoadingMore = false; + if (newGroups.length < _pageSize) { + _hasReachedEnd = true; + } + emit(_loadedhomeState.copyWith( + groups: _totalGroups, + hasReachedEnd: _hasReachedEnd, + isLoadingmore: _isLoadingMore)); + } + } + + Future _groupSubscription() async { + // getting all group ids for subscription + if (_groupIds.isEmpty) return; + + var membersCubit = locator(); + var groupCubit = locator(); + + // creating subscription + _groupUpdateSubscription = await homeUseCase + .groupUpdateSubscription(_groupIds) + .listen((dataState) async { + if (dataState is DataSuccess) { + UpdatedGroupEntity updatedGroup = dataState.data!; + String groupId = updatedGroup.id!; + + // first taking the group from list of total groups + var group = await _getGroup(groupId); + + // something went wrong or maybe group don't exist now // + + if (group == null) { + return; + } + + if (updatedGroup.newUser != null) { + UserEntity newUser = updatedGroup.newUser!; + + // _currentGroupId is the id of the Group that the user has opened + // if it is null then it is in homeScreen + // groupId is the group for which Group update has come + + var groups = addNewMember(groupId, updatedGroup.newUser!); + if (_currentGroupId != groupId) { + String message = + '${newUser.name ?? 'Anonymous'} has joined the ${group.title ?? 'title'}'; + emit(LoadedHomeState(groups: groups, message: message)); + showNotification(message, ''); + } else { + membersCubit.addMember(updatedGroup.newUser!); + } + } else if (updatedGroup.newBeacon != null) { + var newBeacon = updatedGroup.newBeacon!; + + String message = + 'A new beacon is created in ${updatedGroup.newBeacon!.group!.title ?? 'group'}'; + var updatedgroups = await addBeaconInGroup(newBeacon, groupId); + if (_currentGroupId != groupId) { + emit(_loadedhomeState.copyWith( + groups: updatedgroups, message: message)); + showNotification(message, ''); + } else { + groupCubit.addBeacon(newBeacon, message); + } + } else if (updatedGroup.updatedBeacon != null) { + BeaconEntity updatedBeacon = updatedGroup.updatedBeacon!; + + String message = + '${updatedBeacon.title ?? 'Beacon'} is rescheduled by ${updatedBeacon.leader!.name ?? 'Anonymous'}'; + var updatedGroups = addBeaconInGroup(updatedBeacon, groupId); + + if (_currentGroupId != groupId) { + emit(_loadedhomeState.copyWith( + groups: updatedGroups, message: message)); + showNotification(message, ''); + } else { + groupCubit.addBeacon(updatedBeacon, message); + } + + // a new beacon is rescheduled .... + } else if (updatedGroup.deletedBeacon != null) { + BeaconEntity deletedBeacon = updatedGroup.deletedBeacon!; + + String message = + '${deletedBeacon.title ?? 'Beacon'} is deleted by ${deletedBeacon.leader!.name ?? 'Anonymous'}'; + var updatedGroups = + deleteBeaconFromGroup(updatedGroup.deletedBeacon!, groupId); + if (_currentGroupId != groupId) { + emit(_loadedhomeState.copyWith( + groups: updatedGroups, message: message)); + showNotification(message, ''); + } else { + groupCubit.removeBeacon(deletedBeacon, message); + } + } + } + }); + } + + Future _getGroup(String groupId) async { + var index = _totalGroups.indexWhere((group) => group.id == groupId); + if (index == -1) { + var dataState = await homeUseCase.group(groupId); + if (dataState is DataSuccess) { + _totalGroups.insert(0, dataState.data!); + return dataState.data!; + } + return null; + } + return _totalGroups[index]; + } + + // this function is used for emitting state or reload the sate + void resetGroupActivity({String? groupId}) { + if (groupId != null) { + for (int i = 0; i < _totalGroups.length; i++) { + if (_totalGroups[i].id == groupId) { + var group = _totalGroups[i]; + _totalGroups.removeAt(i); + group = + group.copywith(hasBeaconActiby: false, hasMemberActivity: false); + _totalGroups.insert(i, group); + } + } + } + emit(_loadedhomeState.copyWith(groups: List.from(_totalGroups))); + } + + void showNotification(String title, String body) { + localNotif.showInstantNotification(title, body); + } + + List addBeaconInGroup(BeaconEntity newBeacon, String groupId) { + var groups = _totalGroups.where((group) => group.id! == groupId).toList(); + + if (groups.isEmpty) return List.from(_totalGroups); + + var group = groups.first; + + // while adding we'll be already checking for duplicate groups + var beacons = List.from(group.beacons ?? []); + beacons.removeWhere((beacon) => beacon.id! == newBeacon.id!); + beacons.add(newBeacon); + + var updatedGroup = group.copywith(beacons: beacons, hasBeaconActiby: true); + + var updatedGroups = List.from(_totalGroups) + ..removeWhere((group) => group.id == groupId); + + updatedGroups.insert(0, updatedGroup); + _totalGroups = updatedGroups; + + return updatedGroups; + } + + List deleteBeaconFromGroup( + BeaconEntity deletedBeacon, String groupId) { + var groups = _totalGroups.where((group) => group.id! == groupId).toList(); + + if (groups.isEmpty) return List.from(_totalGroups); + + var updatedGroups = List.from(_totalGroups) + ..removeWhere((group) => group.id! == groupId); + + var beacons = List.from(groups.first.beacons ?? []); + beacons.removeWhere((beacon) => beacon.id! == deletedBeacon.id!); + var group = groups.first.copywith(beacons: beacons, hasBeaconActiby: true); + + updatedGroups.insert(0, group); + _totalGroups = updatedGroups; + + return updatedGroups; + } + + List addNewMember(String groupId, UserEntity newUser) { + var updatedGroups = List.from(_totalGroups); + int groupIndex = updatedGroups.indexWhere((group) => group.id == groupId); + if (groupIndex == -1) { + return updatedGroups; + } + var group = updatedGroups[groupIndex]; + var updatedMembers = List.from(group.members ?? []); + updatedMembers.removeWhere((member) => member.id == newUser.id); + updatedMembers.add(newUser); + var updatedGroup = group.copywith( + members: updatedMembers, + hasMemberActivity: true, + ); + updatedGroups[groupIndex] = updatedGroup; + _totalGroups = updatedGroups; + return updatedGroups; + } + + List removeMember(String groupId, UserEntity userToRemove) { + var updatedGroups = List.from(_totalGroups); + int groupIndex = updatedGroups.indexWhere((group) => group.id == groupId); + if (groupIndex == -1) { + return updatedGroups; + } + var group = updatedGroups[groupIndex]; + var updatedMembers = List.from(group.members ?? []); + updatedMembers.removeWhere((member) => member.id == userToRemove.id); + var updatedGroup = group.copywith( + members: updatedMembers, + hasMemberActivity: true, + ); + updatedGroups[groupIndex] = updatedGroup; + _totalGroups = updatedGroups; + return updatedGroups; + } + + void updateCurrentGroupId(String? groupId) { + _currentGroupId = groupId; + } + + void init() async { + var groups = localApi.userModel.groups ?? []; + _groupIds = List.generate(groups.length, (index) => groups[index]!.id!); + await _groupSubscription(); + } + + void clear() { + _groupIds.clear(); + _groupUpdateSubscription?.cancel(); + _page = 1; + _hasReachedEnd = false; + _totalGroups.clear(); + emit(InitialHomeState()); + } +} diff --git a/lib/presentation/home/home_cubit/home_state.dart b/lib/presentation/home/home_cubit/home_state.dart new file mode 100644 index 00000000..764a9002 --- /dev/null +++ b/lib/presentation/home/home_cubit/home_state.dart @@ -0,0 +1,17 @@ +import 'package:beacon/domain/entities/group/group_entity.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'home_state.freezed.dart'; + +@freezed +abstract class HomeState with _$HomeState { + const factory HomeState.initial() = InitialHomeState; + const factory HomeState.shimmer() = ShimmerHomeState; + const factory HomeState.loading() = LoadingHomeState; + const factory HomeState.loaded({ + required List groups, + String? message, + @Default(false) bool isLoadingmore, + @Default(false) bool hasReachedEnd, + }) = LoadedHomeState; +} diff --git a/lib/presentation/home/home_cubit/home_state.freezed.dart b/lib/presentation/home/home_cubit/home_state.freezed.dart new file mode 100644 index 00000000..506b8d75 --- /dev/null +++ b/lib/presentation/home/home_cubit/home_state.freezed.dart @@ -0,0 +1,659 @@ +// 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 'home_state.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 _$HomeState { + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() shimmer, + required TResult Function() loading, + required TResult Function(List groups, String? message, + bool isLoadingmore, bool hasReachedEnd) + loaded, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? shimmer, + TResult? Function()? loading, + TResult? Function(List groups, String? message, + bool isLoadingmore, bool hasReachedEnd)? + loaded, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? shimmer, + TResult Function()? loading, + TResult Function(List groups, String? message, + bool isLoadingmore, bool hasReachedEnd)? + loaded, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(InitialHomeState value) initial, + required TResult Function(ShimmerHomeState value) shimmer, + required TResult Function(LoadingHomeState value) loading, + required TResult Function(LoadedHomeState value) loaded, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialHomeState value)? initial, + TResult? Function(ShimmerHomeState value)? shimmer, + TResult? Function(LoadingHomeState value)? loading, + TResult? Function(LoadedHomeState value)? loaded, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialHomeState value)? initial, + TResult Function(ShimmerHomeState value)? shimmer, + TResult Function(LoadingHomeState value)? loading, + TResult Function(LoadedHomeState value)? loaded, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $HomeStateCopyWith<$Res> { + factory $HomeStateCopyWith(HomeState value, $Res Function(HomeState) then) = + _$HomeStateCopyWithImpl<$Res, HomeState>; +} + +/// @nodoc +class _$HomeStateCopyWithImpl<$Res, $Val extends HomeState> + implements $HomeStateCopyWith<$Res> { + _$HomeStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; +} + +/// @nodoc +abstract class _$$InitialHomeStateImplCopyWith<$Res> { + factory _$$InitialHomeStateImplCopyWith(_$InitialHomeStateImpl value, + $Res Function(_$InitialHomeStateImpl) then) = + __$$InitialHomeStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$InitialHomeStateImplCopyWithImpl<$Res> + extends _$HomeStateCopyWithImpl<$Res, _$InitialHomeStateImpl> + implements _$$InitialHomeStateImplCopyWith<$Res> { + __$$InitialHomeStateImplCopyWithImpl(_$InitialHomeStateImpl _value, + $Res Function(_$InitialHomeStateImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$InitialHomeStateImpl implements InitialHomeState { + const _$InitialHomeStateImpl(); + + @override + String toString() { + return 'HomeState.initial()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$InitialHomeStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() shimmer, + required TResult Function() loading, + required TResult Function(List groups, String? message, + bool isLoadingmore, bool hasReachedEnd) + loaded, + }) { + return initial(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? shimmer, + TResult? Function()? loading, + TResult? Function(List groups, String? message, + bool isLoadingmore, bool hasReachedEnd)? + loaded, + }) { + return initial?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? shimmer, + TResult Function()? loading, + TResult Function(List groups, String? message, + bool isLoadingmore, bool hasReachedEnd)? + loaded, + required TResult orElse(), + }) { + if (initial != null) { + return initial(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialHomeState value) initial, + required TResult Function(ShimmerHomeState value) shimmer, + required TResult Function(LoadingHomeState value) loading, + required TResult Function(LoadedHomeState value) loaded, + }) { + return initial(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialHomeState value)? initial, + TResult? Function(ShimmerHomeState value)? shimmer, + TResult? Function(LoadingHomeState value)? loading, + TResult? Function(LoadedHomeState value)? loaded, + }) { + return initial?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialHomeState value)? initial, + TResult Function(ShimmerHomeState value)? shimmer, + TResult Function(LoadingHomeState value)? loading, + TResult Function(LoadedHomeState value)? loaded, + required TResult orElse(), + }) { + if (initial != null) { + return initial(this); + } + return orElse(); + } +} + +abstract class InitialHomeState implements HomeState { + const factory InitialHomeState() = _$InitialHomeStateImpl; +} + +/// @nodoc +abstract class _$$ShimmerHomeStateImplCopyWith<$Res> { + factory _$$ShimmerHomeStateImplCopyWith(_$ShimmerHomeStateImpl value, + $Res Function(_$ShimmerHomeStateImpl) then) = + __$$ShimmerHomeStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$ShimmerHomeStateImplCopyWithImpl<$Res> + extends _$HomeStateCopyWithImpl<$Res, _$ShimmerHomeStateImpl> + implements _$$ShimmerHomeStateImplCopyWith<$Res> { + __$$ShimmerHomeStateImplCopyWithImpl(_$ShimmerHomeStateImpl _value, + $Res Function(_$ShimmerHomeStateImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$ShimmerHomeStateImpl implements ShimmerHomeState { + const _$ShimmerHomeStateImpl(); + + @override + String toString() { + return 'HomeState.shimmer()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$ShimmerHomeStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() shimmer, + required TResult Function() loading, + required TResult Function(List groups, String? message, + bool isLoadingmore, bool hasReachedEnd) + loaded, + }) { + return shimmer(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? shimmer, + TResult? Function()? loading, + TResult? Function(List groups, String? message, + bool isLoadingmore, bool hasReachedEnd)? + loaded, + }) { + return shimmer?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? shimmer, + TResult Function()? loading, + TResult Function(List groups, String? message, + bool isLoadingmore, bool hasReachedEnd)? + loaded, + required TResult orElse(), + }) { + if (shimmer != null) { + return shimmer(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialHomeState value) initial, + required TResult Function(ShimmerHomeState value) shimmer, + required TResult Function(LoadingHomeState value) loading, + required TResult Function(LoadedHomeState value) loaded, + }) { + return shimmer(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialHomeState value)? initial, + TResult? Function(ShimmerHomeState value)? shimmer, + TResult? Function(LoadingHomeState value)? loading, + TResult? Function(LoadedHomeState value)? loaded, + }) { + return shimmer?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialHomeState value)? initial, + TResult Function(ShimmerHomeState value)? shimmer, + TResult Function(LoadingHomeState value)? loading, + TResult Function(LoadedHomeState value)? loaded, + required TResult orElse(), + }) { + if (shimmer != null) { + return shimmer(this); + } + return orElse(); + } +} + +abstract class ShimmerHomeState implements HomeState { + const factory ShimmerHomeState() = _$ShimmerHomeStateImpl; +} + +/// @nodoc +abstract class _$$LoadingHomeStateImplCopyWith<$Res> { + factory _$$LoadingHomeStateImplCopyWith(_$LoadingHomeStateImpl value, + $Res Function(_$LoadingHomeStateImpl) then) = + __$$LoadingHomeStateImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$LoadingHomeStateImplCopyWithImpl<$Res> + extends _$HomeStateCopyWithImpl<$Res, _$LoadingHomeStateImpl> + implements _$$LoadingHomeStateImplCopyWith<$Res> { + __$$LoadingHomeStateImplCopyWithImpl(_$LoadingHomeStateImpl _value, + $Res Function(_$LoadingHomeStateImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$LoadingHomeStateImpl implements LoadingHomeState { + const _$LoadingHomeStateImpl(); + + @override + String toString() { + return 'HomeState.loading()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$LoadingHomeStateImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() shimmer, + required TResult Function() loading, + required TResult Function(List groups, String? message, + bool isLoadingmore, bool hasReachedEnd) + loaded, + }) { + return loading(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? shimmer, + TResult? Function()? loading, + TResult? Function(List groups, String? message, + bool isLoadingmore, bool hasReachedEnd)? + loaded, + }) { + return loading?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? shimmer, + TResult Function()? loading, + TResult Function(List groups, String? message, + bool isLoadingmore, bool hasReachedEnd)? + loaded, + required TResult orElse(), + }) { + if (loading != null) { + return loading(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialHomeState value) initial, + required TResult Function(ShimmerHomeState value) shimmer, + required TResult Function(LoadingHomeState value) loading, + required TResult Function(LoadedHomeState value) loaded, + }) { + return loading(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialHomeState value)? initial, + TResult? Function(ShimmerHomeState value)? shimmer, + TResult? Function(LoadingHomeState value)? loading, + TResult? Function(LoadedHomeState value)? loaded, + }) { + return loading?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialHomeState value)? initial, + TResult Function(ShimmerHomeState value)? shimmer, + TResult Function(LoadingHomeState value)? loading, + TResult Function(LoadedHomeState value)? loaded, + required TResult orElse(), + }) { + if (loading != null) { + return loading(this); + } + return orElse(); + } +} + +abstract class LoadingHomeState implements HomeState { + const factory LoadingHomeState() = _$LoadingHomeStateImpl; +} + +/// @nodoc +abstract class _$$LoadedHomeStateImplCopyWith<$Res> { + factory _$$LoadedHomeStateImplCopyWith(_$LoadedHomeStateImpl value, + $Res Function(_$LoadedHomeStateImpl) then) = + __$$LoadedHomeStateImplCopyWithImpl<$Res>; + @useResult + $Res call( + {List groups, + String? message, + bool isLoadingmore, + bool hasReachedEnd}); +} + +/// @nodoc +class __$$LoadedHomeStateImplCopyWithImpl<$Res> + extends _$HomeStateCopyWithImpl<$Res, _$LoadedHomeStateImpl> + implements _$$LoadedHomeStateImplCopyWith<$Res> { + __$$LoadedHomeStateImplCopyWithImpl( + _$LoadedHomeStateImpl _value, $Res Function(_$LoadedHomeStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? groups = null, + Object? message = freezed, + Object? isLoadingmore = null, + Object? hasReachedEnd = null, + }) { + return _then(_$LoadedHomeStateImpl( + groups: null == groups + ? _value._groups + : groups // ignore: cast_nullable_to_non_nullable + as List, + message: freezed == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable + as String?, + isLoadingmore: null == isLoadingmore + ? _value.isLoadingmore + : isLoadingmore // ignore: cast_nullable_to_non_nullable + as bool, + hasReachedEnd: null == hasReachedEnd + ? _value.hasReachedEnd + : hasReachedEnd // ignore: cast_nullable_to_non_nullable + as bool, + )); + } +} + +/// @nodoc + +class _$LoadedHomeStateImpl implements LoadedHomeState { + const _$LoadedHomeStateImpl( + {required final List groups, + this.message, + this.isLoadingmore = false, + this.hasReachedEnd = false}) + : _groups = groups; + + final List _groups; + @override + List get groups { + if (_groups is EqualUnmodifiableListView) return _groups; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_groups); + } + + @override + final String? message; + @override + @JsonKey() + final bool isLoadingmore; + @override + @JsonKey() + final bool hasReachedEnd; + + @override + String toString() { + return 'HomeState.loaded(groups: $groups, message: $message, isLoadingmore: $isLoadingmore, hasReachedEnd: $hasReachedEnd)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$LoadedHomeStateImpl && + const DeepCollectionEquality().equals(other._groups, _groups) && + (identical(other.message, message) || other.message == message) && + (identical(other.isLoadingmore, isLoadingmore) || + other.isLoadingmore == isLoadingmore) && + (identical(other.hasReachedEnd, hasReachedEnd) || + other.hasReachedEnd == hasReachedEnd)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(_groups), + message, + isLoadingmore, + hasReachedEnd); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$LoadedHomeStateImplCopyWith<_$LoadedHomeStateImpl> get copyWith => + __$$LoadedHomeStateImplCopyWithImpl<_$LoadedHomeStateImpl>( + this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() shimmer, + required TResult Function() loading, + required TResult Function(List groups, String? message, + bool isLoadingmore, bool hasReachedEnd) + loaded, + }) { + return loaded(groups, message, isLoadingmore, hasReachedEnd); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? shimmer, + TResult? Function()? loading, + TResult? Function(List groups, String? message, + bool isLoadingmore, bool hasReachedEnd)? + loaded, + }) { + return loaded?.call(groups, message, isLoadingmore, hasReachedEnd); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? shimmer, + TResult Function()? loading, + TResult Function(List groups, String? message, + bool isLoadingmore, bool hasReachedEnd)? + loaded, + required TResult orElse(), + }) { + if (loaded != null) { + return loaded(groups, message, isLoadingmore, hasReachedEnd); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(InitialHomeState value) initial, + required TResult Function(ShimmerHomeState value) shimmer, + required TResult Function(LoadingHomeState value) loading, + required TResult Function(LoadedHomeState value) loaded, + }) { + return loaded(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(InitialHomeState value)? initial, + TResult? Function(ShimmerHomeState value)? shimmer, + TResult? Function(LoadingHomeState value)? loading, + TResult? Function(LoadedHomeState value)? loaded, + }) { + return loaded?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(InitialHomeState value)? initial, + TResult Function(ShimmerHomeState value)? shimmer, + TResult Function(LoadingHomeState value)? loading, + TResult Function(LoadedHomeState value)? loaded, + required TResult orElse(), + }) { + if (loaded != null) { + return loaded(this); + } + return orElse(); + } +} + +abstract class LoadedHomeState implements HomeState { + const factory LoadedHomeState( + {required final List groups, + final String? message, + final bool isLoadingmore, + final bool hasReachedEnd}) = _$LoadedHomeStateImpl; + + List get groups; + String? get message; + bool get isLoadingmore; + bool get hasReachedEnd; + @JsonKey(ignore: true) + _$$LoadedHomeStateImplCopyWith<_$LoadedHomeStateImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/presentation/home/home_screen.dart b/lib/presentation/home/home_screen.dart new file mode 100644 index 00000000..f1018b85 --- /dev/null +++ b/lib/presentation/home/home_screen.dart @@ -0,0 +1,375 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:beacon/domain/entities/group/group_entity.dart'; +import 'package:beacon/presentation/home/home_cubit/home_cubit.dart'; +import 'package:beacon/presentation/home/home_cubit/home_state.dart'; +import 'package:beacon/presentation/group/widgets/create_join_dialog.dart'; +import 'package:beacon/presentation/widgets/shimmer.dart'; +import 'package:beacon/presentation/home/widgets/group_card.dart'; +import 'package:beacon/presentation/widgets/hike_button.dart'; +import 'package:beacon/presentation/widgets/loading_screen.dart'; +import 'package:beacon/presentation/widgets/shape_painter.dart'; +import 'package:beacon/locator.dart'; +import 'package:beacon/core/utils/constants.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; + +@RoutePage() +class HomeScreen extends StatefulWidget { + const HomeScreen({super.key}); + + @override + State createState() => _HomeScreenState(); +} + +class _HomeScreenState extends State { + Future _onPopHome(BuildContext context) async { + return showDialog( + context: context, + builder: (context) => AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + // actionsAlignment: MainAxisAlignment.spaceEvenly, + contentPadding: EdgeInsets.all(25.0), + title: Text( + 'Confirm Exit', + style: TextStyle(fontSize: 25, color: kYellow), + ), + content: Text( + 'Do you really want to exit?', + style: TextStyle(fontSize: 18, color: kBlack), + ), + actions: [ + HikeButton( + buttonHeight: 2.5.h, + buttonWidth: 8.w, + onTap: () => AutoRouter.of(context).maybePop(false), + text: 'No', + ), + SizedBox( + height: 5, + ), + HikeButton( + buttonHeight: 2.5.h, + buttonWidth: 8.w, + onTap: () => AutoRouter.of(context).maybePop(true), + text: 'Yes', + ), + ], + ), + ); + } + + late ScrollController _scrollController; + late HomeCubit _homeCubit; + + @override + void initState() { + _scrollController = ScrollController(); + if (localApi.userModel.isGuest == false) { + locationService.getCurrentLocation(); + _homeCubit = BlocProvider.of(context); + _homeCubit.init(); + _homeCubit.fetchUserGroups(); + _scrollController.addListener(_onScroll); + } + super.initState(); + } + + void _onScroll() { + if (_scrollController.position.pixels == + _scrollController.position.maxScrollExtent) { + _homeCubit.fetchUserGroups(); + } + } + + @override + void dispose() { + _scrollController.dispose(); + if (localApi.userModel.isGuest == false) { + _homeCubit.clear(); + } + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return PopScope( + canPop: false, + onPopInvoked: (didPop) async { + bool? popped = await _onPopHome(context); + + if (popped == true) { + await SystemNavigator.pop(); + } + }, + child: BlocConsumer( + listener: (context, state) { + if (state is LoadedHomeState) { + state.message != null + ? utils.showSnackBar(state.message!, context) + : null; + } + }, + builder: (context, state) { + return Scaffold( + resizeToAvoidBottomInset: false, + body: SafeArea( + child: ModalProgressHUD( + inAsyncCall: state is LoadingHomeState ? true : false, + progressIndicator: LoadingScreen(), + child: Stack( + children: [ + CustomPaint( + size: Size(100.w, 100.h - 200), + painter: ShapePainter(), + ), + // CustomPaint( + // size: Size(100.w, 100.h), + // painter: DrawCircle(), + // ), + Align( + alignment: Alignment(0.9, -0.8), + child: FloatingActionButton( + onPressed: () => showDialog( + context: context, + builder: (context) => AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + title: Text( + localApi.userModel.isGuest == true + ? 'Create Account' + : 'Logout', + style: TextStyle( + fontSize: 25, color: kYellow), + ), + content: Text( + localApi.userModel.isGuest == true + ? 'Would you like to create an account?' + : 'Are you sure you want to logout?', + style: TextStyle( + fontSize: 16, color: kBlack), + ), + actions: [ + HikeButton( + buttonHeight: 2.5.h, + buttonWidth: 8.w, + onTap: () => AutoRouter.of(context) + .maybePop(false), + text: 'No', + textSize: 18.0, + ), + SizedBox( + height: 5, + ), + HikeButton( + buttonHeight: 2.5.h, + buttonWidth: 8.w, + onTap: () async { + appRouter.replaceNamed('/auth'); + localApi.deleteUser(); + }, + text: 'Yes', + textSize: 18.0, + ), + ], + )), + backgroundColor: kYellow, + child: localApi.userModel.isGuest == true + ? Icon(Icons.person) + : Icon(Icons.logout)), + ), + Padding( + padding: EdgeInsets.fromLTRB(4.w, 23.h, 4.w, 0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: 45.w, + child: HikeButton( + buttonWidth: homebwidth - 10, + buttonHeight: homebheight - 2, + text: 'Create Group', + textColor: Colors.white, + borderColor: Colors.white, + buttonColor: kYellow, + onTap: () async { + CreateJoinGroupDialog.createGroupDialog( + context); + }, + ), + ), + SizedBox( + width: 1.w, + ), + Container( + width: 45.w, + child: HikeButton( + buttonWidth: homebwidth, + buttonHeight: homebheight - 2, + text: 'Join a Group', + textColor: kYellow, + borderColor: kYellow, + buttonColor: Colors.white, + onTap: () async { + CreateJoinGroupDialog.joinGroupDialog(context); + }, + ), + ), + ], + ), + ), + Positioned( + bottom: 0, + child: Container( + decoration: BoxDecoration( + color: kLightBlue, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30), + topRight: Radius.circular(30))), + height: 56.h, + width: 100.w, + child: Column( + children: [ + Tab(text: 'Your Groups'), + Container( + height: 0.2.h, + color: kBlack, + ), + localApi.userModel.isGuest == true + ? Expanded( + child: Center( + child: SingleChildScrollView( + physics: + AlwaysScrollableScrollPhysics(), + child: Column( + children: [ + Text( + 'You haven\'t joined or created any group yet', + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 20), + ), + SizedBox( + height: 20, + ), + RichText( + text: TextSpan( + style: TextStyle( + color: Colors.black, + fontSize: 20), + children: [ + TextSpan( + text: 'Join', + style: TextStyle( + fontWeight: + FontWeight + .bold)), + TextSpan( + text: ' a Group or '), + TextSpan( + text: 'Create', + style: TextStyle( + fontWeight: + FontWeight + .bold)), + TextSpan( + text: ' a new one!'), + ], + ), + ), + ], + ), + )), + ) + : _buildList() + ], + ))) + ], + ), + )), + ); + }, + )); + } + + Widget _buildList() { + return Expanded( + child: BlocBuilder( + buildWhen: (previous, current) { + return true; + }, + builder: (context, state) { + if (state is ShimmerHomeState) { + return Center( + child: ShimmerWidget.getPlaceholder(), + ); + } else if (state is LoadedHomeState) { + List groups = state.groups; + if (groups.isEmpty) { + return Center( + child: SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + child: Column( + children: [ + Text( + 'You haven\'t joined or created any group yet', + textAlign: TextAlign.center, + style: TextStyle(color: Colors.black, fontSize: 20), + ), + SizedBox( + height: 20, + ), + RichText( + text: TextSpan( + style: TextStyle(color: Colors.black, fontSize: 20), + children: [ + TextSpan( + text: 'Join', + style: TextStyle(fontWeight: FontWeight.bold)), + TextSpan(text: ' a Group or '), + TextSpan( + text: 'Create', + style: TextStyle(fontWeight: FontWeight.bold)), + TextSpan(text: ' a new one!'), + ], + ), + ), + ], + ), + )); + } else { + return ListView.builder( + controller: _scrollController, + physics: AlwaysScrollableScrollPhysics(), + scrollDirection: Axis.vertical, + itemCount: groups.length + + (state.isLoadingmore && !state.hasReachedEnd ? 1 : 0), + padding: EdgeInsets.all(8), + itemBuilder: (context, index) { + if (index == groups.length) { + return Center(child: LinearProgressIndicator()); + } else { + return GroupCustomWidgets.getGroupCard( + context, groups[index]); + } + }, + ); + } + } + + return Center( + child: Text(''), + ); + }, + ), + ); + } +} diff --git a/lib/presentation/home/widgets/group_card.dart b/lib/presentation/home/widgets/group_card.dart new file mode 100644 index 00000000..4e369cbe --- /dev/null +++ b/lib/presentation/home/widgets/group_card.dart @@ -0,0 +1,264 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/domain/entities/group/group_entity.dart'; +import 'package:beacon/domain/usecase/home_usecase.dart'; +import 'package:beacon/presentation/home/home_cubit/home_cubit.dart'; +import 'package:beacon/locator.dart'; +import 'package:beacon/core/utils/constants.dart'; +import 'package:beacon/config/router/router.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; +import 'package:gap/gap.dart'; +import 'package:responsive_sizer/responsive_sizer.dart'; +import 'package:skeleton_text/skeleton_text.dart'; + +class GroupCustomWidgets { + static final Color textColor = Color(0xFFAFAFAF); + + static Widget getGroupCard(BuildContext context, GroupEntity group) { + String noMembers = group.members!.length.toString(); + String noBeacons = group.beacons!.length.toString(); + return GestureDetector( + onTap: () async { + bool isMember = false; + for (var member in group.members!) { + if (member!.id == localApi.userModel.id) { + isMember = true; + } + } + if (group.leader!.id == localApi.userModel.id || isMember) { + var homeCubit = locator(); + homeCubit.updateCurrentGroupId(group.id!); + appRouter.push(GroupScreenRoute(group: group)).then((value) { + homeCubit.resetGroupActivity(groupId: group.id!); + homeCubit.updateCurrentGroupId(null); + }); + } else { + HomeUseCase _homeUseCase = locator(); + DataState state = + await _homeUseCase.joinGroup(group.shortcode!); + if (state is DataSuccess && state.data != null) { + var homeCubit = locator(); + homeCubit.updateCurrentGroupId(group.id!); + appRouter.push(GroupScreenRoute(group: state.data!)).then((value) { + homeCubit.resetGroupActivity(groupId: group.id); + homeCubit.updateCurrentGroupId(null); + }); + } + } + }, + child: Slidable( + key: ValueKey(group.id!.toString()), + endActionPane: ActionPane( + motion: ScrollMotion(), + children: [ + SlidableAction( + padding: EdgeInsets.symmetric(horizontal: 10), + onPressed: (context) { + context.read().changeShortCode(group); + }, + backgroundColor: kCupertinoModalBarrierColor, + foregroundColor: kDefaultIconDarkColor, + icon: Icons.code, + label: 'Change Code', + ), + ], + ), + child: Container( + margin: const EdgeInsets.symmetric( + vertical: 10.0, + horizontal: 10.0, + ), + padding: EdgeInsets.only(left: 16.0, right: 16.0, bottom: 8, top: 8), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 70.w, + child: Text( + '${group.title} by ${group.leader!.name} ', + style: Style.titleTextStyle, + ), + ), + ], + ), + SizedBox(height: 4.0), + Row( + children: [ + Text( + 'Group has $noMembers members ', + style: Style.commonTextStyle, + ), + group.hasMemberActivity + ? Align( + alignment: Alignment.topRight, + child: Icon( + Icons.circle, + color: kYellow, + size: 10, + ), + ) + : Container(), + ], + ), + SizedBox(height: 4.0), + Row( + children: [ + Text( + 'Group has $noBeacons beacons ', + style: Style.commonTextStyle, + ), + Gap(5), + group.hasBeaconActivity + ? Align( + alignment: Alignment.topRight, + child: Icon( + Icons.circle, + color: kYellow, + size: 10, + ), + ) + : Container(), + ], + ), + SizedBox(height: 4.0), + Row( + children: [ + Text('Passkey: ${group.shortcode}', + style: Style.commonTextStyle), + Gap(10), + InkWell( + onTap: () { + Clipboard.setData(ClipboardData( + text: group.shortcode.toString())); + utils.showSnackBar('Shortcode copied!', context); + }, + child: Icon( + Icons.copy, + size: 17, + color: Colors.white, + )) + ], + ) + ], + ), + ], + ), + decoration: BoxDecoration( + color: (group.hasMemberActivity == true || + group.hasBeaconActivity == true) + ? Color(0xFF141546) + : kBlue, + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(8.0), + boxShadow: [ + BoxShadow( + color: Colors.black26, + blurRadius: 10.0, + offset: Offset(0.0, 10.0), + ), + ], + ), + ), + ), + ); + } + + static ListView getPlaceholder() { + final BorderRadius borderRadius = BorderRadius.circular(10.0); + return ListView.builder( + scrollDirection: Axis.vertical, + physics: BouncingScrollPhysics(), + itemCount: 3, + padding: const EdgeInsets.all(8.0), + itemBuilder: (BuildContext context, int index) { + return Container( + margin: const EdgeInsets.symmetric( + vertical: 10.0, + horizontal: 10.0, + ), + height: 110, + decoration: BoxDecoration( + color: kBlue, + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(8.0), + boxShadow: [ + BoxShadow( + color: Colors.black26, + blurRadius: 10.0, + offset: Offset(0.0, 10.0), + ), + ], + ), + padding: + EdgeInsets.only(left: 16.0, right: 16.0, bottom: 10, top: 10), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: const EdgeInsets.only( + left: 15.0, bottom: 10.0, right: 15.0), + child: ClipRRect( + borderRadius: borderRadius, + child: SkeletonAnimation( + child: Container( + height: 15.0, + decoration: BoxDecoration(color: shimmerSkeletonColor), + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.only( + left: 15.0, right: 30.0, bottom: 10.0), + child: ClipRRect( + borderRadius: borderRadius, + child: SkeletonAnimation( + child: Container( + height: 10.0, + decoration: BoxDecoration(color: shimmerSkeletonColor), + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.only( + left: 15.0, right: 45.0, bottom: 10.0), + child: ClipRRect( + borderRadius: BorderRadius.circular(10.0), + child: SkeletonAnimation( + child: Container( + height: 10.0, + decoration: BoxDecoration(color: shimmerSkeletonColor), + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.only(left: 15.0, right: 60.0), + child: ClipRRect( + borderRadius: BorderRadius.circular(10.0), + child: SkeletonAnimation( + child: Container( + height: 10.0, + decoration: BoxDecoration(color: shimmerSkeletonColor), + ), + ), + ), + ), + ], + ), + ); + }); + } +} diff --git a/lib/presentation/splash/splash_screen.dart b/lib/presentation/splash/splash_screen.dart new file mode 100644 index 00000000..ad8aa205 --- /dev/null +++ b/lib/presentation/splash/splash_screen.dart @@ -0,0 +1,140 @@ +import 'dart:async'; + +import 'package:auto_route/auto_route.dart'; +import 'package:beacon/config/router/router.dart'; +import 'package:beacon/core/resources/data_state.dart'; +import 'package:beacon/domain/entities/user/user_entity.dart'; +import 'package:beacon/domain/usecase/auth_usecase.dart'; +import 'package:beacon/domain/usecase/group_usecase.dart'; +import 'package:beacon/locator.dart'; +import 'package:beacon/presentation/auth/verification_cubit/verification_cubit.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:uni_links/uni_links.dart'; + +import '../widgets/loading_screen.dart'; + +@RoutePage() +class SplashScreen extends StatefulWidget { + const SplashScreen({super.key}); + + @override + _SplashScreenState createState() => _SplashScreenState(); +} + +class _SplashScreenState extends State { + bool isCheckingUrl = false; + + @override + void initState() { + handleLinks(); + super.initState(); + } + + StreamSubscription? _sub; + Uri? _latestUri; + Uri? _initialUri; + + handleLinks() async { + _sub = uriLinkStream.listen((Uri? uri) { + if (!mounted) return; + setState(() { + _latestUri = uri; + }); + }, onError: (Object err) { + if (!mounted) return; + setState(() { + _latestUri = null; + }); + }); + + try { + final uri = await getInitialUri(); + if (!mounted) return; + setState(() => _initialUri = uri); + } on PlatformException { + if (!mounted) return; + setState(() => _initialUri = null); + } on FormatException catch (err) { + debugPrint(err.toString()); + if (!mounted) return; + setState(() => _initialUri = null); + } + + await sp.init(); + await localApi.init(); + final authUseCase = locator(); + + await localApi.userloggedIn().then((value) async { + if (_latestUri == null && _initialUri == null) { + bool isConnected = await utils.checkInternetConnectivity(); + if (isConnected) { + final userInfo = await authUseCase.getUserInfoUseCase(); + if (userInfo.data != null) { + await func(userInfo.data!); + } else { + appRouter.replaceNamed('/auth'); + } + } else { + appRouter.replaceNamed('/home'); + utils.showSnackBar( + 'Please connect to your internet connection!', context); + } + } else { + if (_initialUri != null) { + var shortcode = _initialUri!.queryParameters['shortcode']; + if (value == true && shortcode != null) { + await locator().joinHike(shortcode).then((dataState) { + if (dataState is DataSuccess) { + appRouter.push(HikeScreenRoute( + beacon: dataState.data!, + isLeader: dataState.data!.id == localApi.userModel.id)); + } else { + appRouter.push(HomeScreenRoute()); + } + }); + } + } + } + }); + } + + Future func(UserEntity user) async { + var time = await sp.loadData('time'); + var otp = await sp.loadData('otp'); + if (user.isVerified == true) { + await sp.deleteData('time'); + await sp.deleteData('otp'); + appRouter.pushNamed('/home'); + } else { + if (time != null && otp != null) { + if (DateTime.now().difference(DateTime.parse(time)).inMinutes < 2) { + locator().emitVerificationSentstate(otp); + appRouter.push(VerificationScreenRoute()); + utils.showSnackBar('Please verify your email', context); + } else { + await sp.deleteData('time'); + await sp.deleteData('otp'); + appRouter.pushNamed('/auth'); + } + } else { + await sp.deleteData('time'); + await sp.deleteData('otp'); + appRouter.replaceNamed('/auth'); + } + } + } + + @override + void dispose() { + _sub?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: LoadingScreen(), + ); + } +} diff --git a/lib/presentation/widgets/custom_label_marker.dart b/lib/presentation/widgets/custom_label_marker.dart new file mode 100644 index 00000000..a0c3f5f1 --- /dev/null +++ b/lib/presentation/widgets/custom_label_marker.dart @@ -0,0 +1,48 @@ +import 'package:beacon/core/utils/constants.dart'; +import 'package:flutter/material.dart'; + +class CustomMarker extends CustomPainter { + final String text; + CustomMarker({required this.text}); + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = kYellow + ..style = PaintingStyle.fill; + + // Draw the bottom part (triangle) + final path = Path() + ..moveTo(size.width / 2, size.height) + ..lineTo(size.width / 2 - 10, size.height - 10) + ..lineTo(size.width / 2 + 10, size.height - 10) + ..close(); + canvas.drawPath(path, paint); + + // Draw the top part (rounded rectangle) + final rect = Rect.fromLTWH( + 0, size.height * 0.5 + 10, size.width, size.height * .5 - 20); + final rRect = RRect.fromRectAndRadius(rect, Radius.circular(8)); + canvas.drawRRect(rRect, paint); + + // Draw the text + final textPainter = TextPainter( + text: TextSpan( + text: text, + style: TextStyle( + color: Colors.white, fontSize: 14, fontWeight: FontWeight.w800), + ), + textDirection: TextDirection.ltr, + ); + textPainter.layout(minWidth: 0, maxWidth: size.width); + final offset = Offset( + (size.width - textPainter.width) / 2, + (size.height * 0.55 + 10), + ); + textPainter.paint(canvas, offset); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return true; + } +} diff --git a/lib/components/hike_button.dart b/lib/presentation/widgets/hike_button.dart similarity index 64% rename from lib/components/hike_button.dart rename to lib/presentation/widgets/hike_button.dart index 7e0a361c..e22a2f3f 100644 --- a/lib/components/hike_button.dart +++ b/lib/presentation/widgets/hike_button.dart @@ -1,30 +1,32 @@ -import 'package:beacon/utilities/constants.dart'; +import 'package:beacon/core/utils/constants.dart'; import 'package:flutter/material.dart'; class HikeButton extends StatelessWidget { - final Function onTap; - final String text; + final Function? onTap; + final String? text; final double textSize; final Color textColor; final Color borderColor; final Color buttonColor; final double buttonWidth; final double buttonHeight; + final Widget? widget; HikeButton( {this.onTap, this.borderColor = Colors.white, this.buttonColor = kYellow, this.text, this.textColor = Colors.white, - this.buttonWidth = 32, - this.buttonHeight = 32, - this.textSize = 18}); + this.buttonWidth = 15, + this.buttonHeight = 20, + this.textSize = 18, + this.widget}); @override Widget build(BuildContext context) { return ElevatedButton( style: ElevatedButton.styleFrom( - primary: buttonColor, + backgroundColor: buttonColor, shape: RoundedRectangleBorder( borderRadius: new BorderRadius.circular(50.0), side: BorderSide(color: borderColor)), @@ -32,12 +34,13 @@ class HikeButton extends StatelessWidget { child: Padding( padding: EdgeInsets.symmetric( horizontal: buttonWidth, vertical: buttonHeight), - child: Text( - text, - style: TextStyle(color: textColor, fontSize: textSize), - ), + child: widget ?? + Text( + text!, + style: TextStyle(color: textColor, fontSize: textSize), + ), ), - onPressed: onTap, + onPressed: onTap as void Function()?, ); } } diff --git a/lib/utilities/indication_painter.dart b/lib/presentation/widgets/indication_painter.dart similarity index 90% rename from lib/utilities/indication_painter.dart rename to lib/presentation/widgets/indication_painter.dart index a82219a5..e647ef51 100644 --- a/lib/utilities/indication_painter.dart +++ b/lib/presentation/widgets/indication_painter.dart @@ -1,16 +1,16 @@ import 'dart:math'; -import 'package:beacon/utilities/constants.dart'; +import 'package:beacon/core/utils/constants.dart'; import 'package:flutter/material.dart'; class TabIndicationPainter extends CustomPainter { - Paint painter; + late Paint painter; final double dxTarget; final double dxEntry; final double radius; final double dy; - final PageController pageController; + final PageController? pageController; TabIndicationPainter( {this.dxTarget = 125.0, @@ -26,7 +26,7 @@ class TabIndicationPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { - final pos = pageController.position; + final pos = pageController!.position; double fullExtent = (pos.maxScrollExtent - pos.minScrollExtent + pos.viewportDimension); diff --git a/lib/presentation/widgets/label_marker.dart b/lib/presentation/widgets/label_marker.dart new file mode 100644 index 00000000..8d471e0f --- /dev/null +++ b/lib/presentation/widgets/label_marker.dart @@ -0,0 +1,219 @@ +/// A widget to create markers with text label on Google Maps +library label_marker; + +import 'dart:typed_data'; +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'dart:ui' as ui; + +extension AddExtension on Set { + /// Add a LabelMarker to existing set of Markers + /// + /// * Pass the [LabelMarker] object to add to the set + /// * !!! IMPORTANT!!! + /// * Call setstate after calling this function, as shown in the example + /// + /// Example + /// + /// markers.addLabelMarker(LabelMarker( + /// label: "makerLabel", + /// markerId: MarkerId("markerIdString"), + /// position: LatLng(11.1203, 45.33),), + /// ).then((_) { + /// setState(() {}); + /// }); + Future addLabelMarker(LabelMarker labelMarker) async { + bool result = false; + await createCustomMarkerBitmap( + labelMarker.label, + backgroundColor: labelMarker.backgroundColor, + textStyle: labelMarker.textStyle, + removePointyTriangle: labelMarker.removePointyTriangle, + ).then((value) { + add(Marker( + markerId: labelMarker.markerId, + position: labelMarker.position, + icon: value, + alpha: labelMarker.alpha, + anchor: labelMarker.anchor, + consumeTapEvents: labelMarker.consumeTapEvents, + draggable: labelMarker.draggable, + flat: labelMarker.flat, + infoWindow: labelMarker.infoWindow, + rotation: labelMarker.rotation, + visible: labelMarker.visible, + zIndex: labelMarker.zIndex, + onTap: labelMarker.onTap, + onDragStart: labelMarker.onDragStart, + onDrag: labelMarker.onDrag, + onDragEnd: labelMarker.onDragEnd)); + result = true; + }); + return (result); + } +} + +Future createCustomMarkerBitmap( + String title, { + required TextStyle textStyle, + Color backgroundColor = Colors.blueAccent, + bool removePointyTriangle = false, +}) async { + TextSpan span = TextSpan( + style: textStyle, + text: title, + ); + TextPainter painter = TextPainter( + text: span, + textAlign: TextAlign.center, + textDirection: ui.TextDirection.ltr, + ); + painter.text = TextSpan( + text: title.toString(), + style: textStyle, + ); + ui.PictureRecorder pictureRecorder = ui.PictureRecorder(); + Canvas canvas = Canvas(pictureRecorder); + painter.layout(); + painter.paint(canvas, const Offset(20.0, 10.0)); + int textWidth = painter.width.toInt(); + int textHeight = painter.height.toInt(); + canvas.drawRRect( + RRect.fromLTRBAndCorners(0, 0, textWidth + 40, textHeight + 20, + bottomLeft: const Radius.circular(10), + bottomRight: const Radius.circular(10), + topLeft: const Radius.circular(10), + topRight: const Radius.circular(10)), + Paint()..color = backgroundColor); + if (!removePointyTriangle) { + var arrowPath = Path(); + arrowPath.moveTo((textWidth + 40) / 2 - 15, textHeight + 20); + arrowPath.lineTo((textWidth + 40) / 2, textHeight + 40); + arrowPath.lineTo((textWidth + 40) / 2 + 15, textHeight + 20); + arrowPath.close(); + canvas.drawPath(arrowPath, Paint()..color = backgroundColor); + } + painter.layout(); + painter.paint(canvas, const Offset(20.0, 10.0)); + ui.Picture p = pictureRecorder.endRecording(); + ByteData? pngBytes = await (await p.toImage( + painter.width.toInt() + 40, painter.height.toInt() + 50)) + .toByteData(format: ui.ImageByteFormat.png); + Uint8List data = Uint8List.view(pngBytes!.buffer); + // ignore: deprecated_member_use + return BitmapDescriptor.fromBytes(data); +} + +class LabelMarker { + /// The text to be displayed on the marker + final String label; + + /// Uniquely identifies a [Marker]. + final MarkerId markerId; + + /// Geographical location of the marker. + final LatLng position; + + /// Background color of the label marker. + final Color backgroundColor; + + /// TextStyle for the text to be displayed in the label marker. + final TextStyle textStyle; + + /// The opacity of the marker, between 0.0 and 1.0 inclusive. + /// + /// 0.0 means fully transparent, 1.0 means fully opaque. + final double alpha; + + /// The icon image point that will be placed at the [position] of the marker. + /// + /// The image point is specified in normalized coordinates: An anchor of + /// (0.0, 0.0) means the top left corner of the image. An anchor + /// of (1.0, 1.0) means the bottom right corner of the image. + final Offset anchor; + + /// True if the marker icon consumes tap events. If not, the map will perform + /// default tap handling by centering the map on the marker and displaying its + /// info window. + final bool consumeTapEvents; + + /// True if the marker is draggable by user touch events. + final bool draggable; + + /// True if the marker is rendered flatly against the surface of the Earth, so + /// that it will rotate and tilt along with map camera movements. + final bool flat; + + /// A description of the bitmap used to draw the marker icon. + final BitmapDescriptor icon; + + /// A Google Maps InfoWindow. + /// + /// The window is displayed when the marker is tapped. + final InfoWindow infoWindow; + + /// Rotation of the marker image in degrees clockwise from the [anchor] point. + final double rotation; + + /// True if the marker is visible. + final bool visible; + + /// The z-index of the marker, used to determine relative drawing order of + /// map overlays. + /// + /// Overlays are drawn in order of z-index, so that lower values means drawn + /// earlier, and thus appearing to be closer to the surface of the Earth. + final double zIndex; + + /// Callbacks to receive tap events for markers placed on this map. + final VoidCallback? onTap; + + /// Signature reporting the new [LatLng] at the start of a drag event. + final ValueChanged? onDragStart; + + /// Signature reporting the new [LatLng] at the end of a drag event. + final ValueChanged? onDragEnd; + + /// Signature reporting the new [LatLng] during the drag event. + final ValueChanged? onDrag; + + /// An option to remove pointy from label + final bool removePointyTriangle; + + /// Creates a marker with text label + /// + /// * Pass the [label] to be displayed on the marker + /// * Pass the [markerId] to be used as a key for the marker + /// * Pass the [position] to be used as the marker's position + /// * Optionally pass the [backgroundColor] to be used as the marker's background color + /// * Optionally pass the [textStyle] to be used as the marker's text style + /// * Optionally you could pass all the other parameters passed for a normal marker + /// + LabelMarker({ + required this.label, + required this.markerId, + required this.position, + this.backgroundColor = Colors.blueAccent, + this.textStyle = const TextStyle( + fontSize: 27.0, + color: Colors.white, + letterSpacing: 1.0, + fontFamily: 'Roboto Bold', + ), + this.alpha = 1.0, + this.anchor = const Offset(0.5, 1.0), + this.consumeTapEvents = false, + this.draggable = false, + this.flat = false, + this.icon = BitmapDescriptor.defaultMarker, + this.infoWindow = InfoWindow.noText, + this.rotation = 0.0, + this.visible = true, + this.zIndex = 0.0, + this.onTap, + this.onDrag, + this.onDragStart, + this.onDragEnd, + this.removePointyTriangle = false, + }); +} diff --git a/lib/presentation/widgets/loading_screen.dart b/lib/presentation/widgets/loading_screen.dart new file mode 100644 index 00000000..c2927d9c --- /dev/null +++ b/lib/presentation/widgets/loading_screen.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; + +import '../../core/utils/constants.dart'; + +class LoadingScreen extends StatelessWidget { + const LoadingScreen({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('images/hikers_group.png'), + fit: BoxFit.cover, + ), + ), + child: Column( + children: [ + Expanded(flex: 3, child: SizedBox()), + Expanded( + flex: 1, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: SpinKitThreeBounce( + color: kYellow, + size: 40, + ), + )), + ], + )), + ), + ); + } +} diff --git a/lib/components/shape_painter.dart b/lib/presentation/widgets/shape_painter.dart similarity index 93% rename from lib/components/shape_painter.dart rename to lib/presentation/widgets/shape_painter.dart index 9b17fe4a..c5b7df91 100644 --- a/lib/components/shape_painter.dart +++ b/lib/presentation/widgets/shape_painter.dart @@ -1,5 +1,5 @@ import 'package:flutter/cupertino.dart'; -import '../utilities/constants.dart'; +import '../../core/utils/constants.dart'; class ShapePainter extends CustomPainter { @override @@ -26,6 +26,6 @@ class ShapePainter extends CustomPainter { @override bool shouldRepaint(CustomPainter oldDelegate) { - return true; + return false; } } diff --git a/lib/components/beacon_card.dart b/lib/presentation/widgets/shimmer.dart similarity index 52% rename from lib/components/beacon_card.dart rename to lib/presentation/widgets/shimmer.dart index 3151e0f6..f50c7172 100644 --- a/lib/components/beacon_card.dart +++ b/lib/presentation/widgets/shimmer.dart @@ -1,91 +1,8 @@ -import 'dart:io'; - -import 'package:beacon/locator.dart'; -import 'package:beacon/models/beacon/beacon.dart'; -import 'package:beacon/utilities/constants.dart'; -import 'package:beacon/view_model/home_view_model.dart'; -import 'package:beacon/views/hike_screen.dart'; +import 'package:beacon/core/utils/constants.dart'; import 'package:flutter/material.dart'; -import 'package:geocoder/geocoder.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:skeleton_text/skeleton_text.dart'; -import 'package:intl/intl.dart'; - -class BeaconCustomWidgets { - static final Color textColor = Color(0xFFAFAFAF); - - static Widget getBeaconCard(BuildContext context, Beacon beacon) { - return GestureDetector( - onTap: () async { - bool isJoinee = false; - for (var i in beacon.followers) { - if (i.id == userConfig.currentUser.id) { - isJoinee = true; - } - } - if (beacon.leader.id == userConfig.currentUser.id || isJoinee) { - navigationService.pushScreen('/hikeScreen', - arguments: HikeScreen( - beacon, - isLeader: (beacon.leader.id == userConfig.currentUser.id), - )); - } else { - databaseFunctions.init(); - final Beacon _beacon = - await databaseFunctions.joinBeacon(beacon.shortcode); - if (_beacon != null) { - navigationService.pushScreen('/hikeScreen', - arguments: HikeScreen(beacon, isLeader: false)); - } else { - navigationService.showSnackBar('Something went wrong'); - } - } - }, - child: Container( - margin: const EdgeInsets.symmetric( - vertical: 10.0, - horizontal: 10.0, - ), - height: 110, - padding: EdgeInsets.only(left: 16.0, right: 16.0, bottom: 8, top: 8), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('${beacon?.title} by ${beacon.leader.name}', - style: Style.titleTextStyle), - SizedBox(height: 4.0), - Text('Passkey: ${beacon?.shortcode}', style: Style.commonTextStyle), - SizedBox(height: 4.0), - (beacon.startsAt != null) - ? Text( - 'Starts At: ${DateFormat("hh:mm a, d/M/y").format(DateTime.fromMillisecondsSinceEpoch(beacon.startsAt)).toString()}', - style: Style.commonTextStyle) - : Container(), - SizedBox(height: 4.0), - (beacon.expiresAt != null) - ? Text( - 'Expires At: ${DateFormat("hh:mm a, d/M/y").format(DateTime.fromMillisecondsSinceEpoch(beacon.expiresAt)).toString()}', - style: Style.commonTextStyle) - : Container(), - ], - ), - decoration: BoxDecoration( - color: kBlue, - shape: BoxShape.rectangle, - borderRadius: BorderRadius.circular(8.0), - boxShadow: [ - BoxShadow( - color: Colors.black26, - blurRadius: 10.0, - offset: Offset(0.0, 10.0), - ), - ], - ), - ), - ); - } +class ShimmerWidget { static ListView getPlaceholder() { final BorderRadius borderRadius = BorderRadius.circular(10.0); return ListView.builder( diff --git a/lib/presentation/widgets/text_field.dart b/lib/presentation/widgets/text_field.dart new file mode 100644 index 00000000..32945050 --- /dev/null +++ b/lib/presentation/widgets/text_field.dart @@ -0,0 +1,68 @@ +import 'package:beacon/core/utils/constants.dart'; +import 'package:flutter/material.dart'; + +class CustomTextField extends StatefulWidget { + final IconData iconData; + final String hintText; + final bool showTrailing; + final TextEditingController controller; + final String? Function(String?)? validator; + final FocusNode? focusNode; + final FocusNode? nextFocusNode; + + CustomTextField( + {super.key, + required this.iconData, + required this.hintText, + required this.controller, + this.showTrailing = false, + this.validator, + this.focusNode, + this.nextFocusNode}); + + @override + State createState() => _CustomTextFieldState(); +} + +class _CustomTextFieldState extends State { + bool obscureText = true; + + @override + Widget build(BuildContext context) { + return TextFormField( + validator: widget.validator, + controller: widget.controller, + focusNode: widget.focusNode, + decoration: InputDecoration( + border: InputBorder.none, + icon: Icon( + widget.iconData, + color: Colors.black, + size: 24.0, + ), + hintText: widget.hintText, + hintStyle: TextStyle(fontSize: hintsize - 2, color: hintColor), + suffixIcon: widget.showTrailing == true + ? IconButton( + onPressed: () { + setState(() { + obscureText = !obscureText; + }); + }, + icon: Icon( + obscureText ? Icons.visibility_off : Icons.visibility, + color: Colors.black, + )) + : null), + style: TextStyle(color: Colors.black), + obscureText: widget.showTrailing == false ? false : obscureText, + onEditingComplete: () { + if (widget.nextFocusNode != null) { + FocusScope.of(context).requestFocus(widget.nextFocusNode); + } else { + FocusScope.of(context).unfocus(); + } + }, + ); + } +} diff --git a/lib/queries/auth.dart b/lib/queries/auth.dart deleted file mode 100644 index 85731230..00000000 --- a/lib/queries/auth.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'dart:ffi'; - -class AuthQueries { - String registerUser(String name, String email, String password) { - return ''' - mutation{ - register(user: {name: "$name", credentials: {email: "$email", password: "$password"}}) - { - _id - name - email - } - } - '''; - } - - String loginAsGuest(String name) { - return ''' - mutation{ - register(user: {name: "$name"}) - { - _id - name - } - } - '''; - } - - String loginUser(String email, String password) { - return ''' - mutation{ - login(credentials: {email: "$email", password: "$password"}) - } - '''; - } - - String loginUsingID(String id) { - return ''' - mutation{ - login(id: "$id") - } - '''; - } - - String fetchUserInfo() { - return ''' - query{ - me{ - _id - email - name - beacons{ - _id - title - shortcode - leader { - _id - name - } - followers{ - _id - name - } - location { - lat - lon - } - startsAt - expiresAt - } - } - } - '''; - } -} diff --git a/lib/queries/beacon.dart b/lib/queries/beacon.dart deleted file mode 100644 index f093c564..00000000 --- a/lib/queries/beacon.dart +++ /dev/null @@ -1,232 +0,0 @@ -import 'dart:ffi'; - -class BeaconQueries { - String createBeacon(String title, int expiresAt, String lat, String lon) { - return ''' - mutation{ - createBeacon(beacon: { - title: "$title", - expiresAt: $expiresAt, - startLocation: { - lat: "$lat", lon: "$lon" - } - }) - { - _id - title - shortcode - leader { - _id - name - } - location{ - lat - lon - } - followers { - _id - name - } - startsAt - expiresAt - landmarks { - title - location { - lat - lon - } - } - } - } - '''; - } - - String updateLeaderLoc(String id, String lat, String lon) { - return ''' - mutation { - updateBeaconLocation(id: "$id", location: {lat: "$lat", lon:"$lon"}){ - location{ - lat - lon - } - } - } - '''; - } - - String addLandmark(String title, String lat, String lon, String id) { - return ''' - mutation{ - createLandmark( - landmark: { - title: "$title", - location: { - lat:"$lat", - lon:"$lon" - } - } - beaconID:"$id" - ){ - location{ - lat - lon - } - } - } - '''; - } - - String joinBeacon(String shortcode) { - return ''' - mutation { - joinBeacon(shortcode: "$shortcode"){ - _id - title - shortcode - leader { - _id - name - } - location { - lat - lon - } - followers { - _id - name - } - startsAt - expiresAt - } - } - '''; - } - - String fetchBeaconDetail(String id) { - return ''' - query{ - beacon(id:"$id"){ - _id - title - leader{ - name - } - followers { - _id - name - } - landmarks{ - title - location{ - lat - lon - } - } - location{ - lat - lon - } - startsAt - expiresAt - shortcode - } - } - '''; - } - - String fetchNearbyBeacons(String lat, String lon) { - return ''' - query { - nearbyBeacons(location:{ - lat: "$lat", - lon: "$lon" - }){ - _id - title - shortcode - leader { - name - location { - lat - lon - } - } - location { - lat - lon - } - followers { - _id - name - } - startsAt - expiresAt - landmarks { - title - location { - lat - lon - } - } - } - } - '''; - } - - String fetchLocationUpdates(String id) { - return ''' - subscription { - beaconLocation (id: "$id") { - lat - lon - } - } - '''; - } - - String fetchUserLocation(String id) { - return ''' - subscription { - userLocation (id: "$id") { - _id - name - location{ - lat - lon - } - } - } - '''; - } - - String fetchFollowerUpdates(String id) { - return ''' - subscription { - beaconJoined (id: "$id") { - _id - name - } - } - '''; - } - - String createLandmark(String id, String lat, String lon, String title) { - return ''' - mutation{ - createLandmark( - landmark:{ - location:{lat:"$lat", lon:"$lon"}, - title:"$title" - }, - beaconID:"$id") - { - title - location{ - lat - lon - } - } - } - '''; - } -} diff --git a/lib/router.dart b/lib/router.dart deleted file mode 100644 index 8f81bd15..00000000 --- a/lib/router.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:beacon/models/beacon/beacon.dart'; -import 'package:beacon/splash_screen.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:beacon/utilities/constants.dart'; -import 'package:beacon/views/auth_screen.dart'; -import 'package:beacon/views/home.dart'; -import 'package:beacon/views/hike_screen.dart'; - -Route generateRoute(RouteSettings settings) { - switch (settings.name) { - case Routes.authScreen: - return MaterialPageRoute( - builder: (context) => const AuthScreen(key: Key('auth'))); - case Routes.mainScreen: - return MaterialPageRoute( - builder: (context) => const MainScreen(key: Key('MainScreen'))); - case Routes.hikeScreen: - HikeScreen arguments = settings.arguments; - return MaterialPageRoute( - builder: (context) => HikeScreen( - arguments.beacon, - isLeader: arguments.isLeader, - )); - default: - return MaterialPageRoute( - builder: (context) => const SplashScreen(key: Key('SplashScreen'))); - } -} diff --git a/lib/services/database_mutation_functions.dart b/lib/services/database_mutation_functions.dart deleted file mode 100644 index 0def2267..00000000 --- a/lib/services/database_mutation_functions.dart +++ /dev/null @@ -1,313 +0,0 @@ -import 'dart:async'; -import 'dart:ffi'; -import 'package:beacon/models/beacon/beacon.dart'; -import 'package:beacon/models/landmarks/landmark.dart'; -import 'package:beacon/models/location/location.dart'; -import 'package:beacon/queries/auth.dart'; -import 'package:beacon/queries/beacon.dart'; -import 'package:beacon/services/navigation_service.dart'; -import 'package:beacon/utilities/constants.dart'; -import 'package:flutter/material.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; -import 'package:graphql_flutter/graphql_flutter.dart'; -import 'package:beacon/models/user/user_info.dart'; -import '../locator.dart'; - -class DataBaseMutationFunctions { - GraphQLClient clientNonAuth; - GraphQLClient clientAuth; - GraphQLClient webSocketClient; - AuthQueries _authQuery; - BeaconQueries _beaconQuery; - init() { - clientNonAuth = graphqlConfig.clientToQuery(); - clientAuth = graphqlConfig.authClient(); - webSocketClient = graphqlConfig.webSocketClient(); - _authQuery = AuthQueries(); - _beaconQuery = BeaconQueries(); - } - - GraphQLError userNotFound = const GraphQLError(message: 'User not found'); - GraphQLError userNotAuthenticated = const GraphQLError( - message: 'Authentication required to perform this action.'); - GraphQLError emailAccountPresent = - const GraphQLError(message: 'Email address already exists'); - GraphQLError wrongCredentials = - const GraphQLError(message: 'Invalid credentials'); - - bool encounteredExceptionOrError(OperationException exception, - {bool showSnackBar = true}) { - if (exception.linkException != null) { - debugPrint(exception.linkException.toString()); - if (showSnackBar) { - debugPrint("Server not running/wrong url"); - } - return false; - } else { - debugPrint(exception.graphqlErrors.toString()); - for (int i = 0; i < exception.graphqlErrors.length; i++) { - if (exception.graphqlErrors[i].message == - userNotAuthenticated.message) { - return true; - } else if (exception.graphqlErrors[i].message == userNotFound.message) { - if (showSnackBar) { - navigationService - .showSnackBar("No account registered with this email"); - } - return false; - } else if (exception.graphqlErrors[i].message == - wrongCredentials.message) { - if (showSnackBar) { - navigationService.showSnackBar("Enter a valid password"); - } - return false; - } else if (exception.graphqlErrors[i].message == - emailAccountPresent.message) { - if (showSnackBar) { - navigationService - .showSnackBar("Account with this email already registered"); - } - return false; - } - } - print("Something went wrong"); - return false; - } - } - - Future> gqlquery(String query) async { - final QueryOptions options = QueryOptions( - document: gql(query), - variables: {}, - ); - - final QueryResult result = await clientAuth.query(options); - if (result.hasException) { - final bool exception = - encounteredExceptionOrError(result.exception, showSnackBar: false); - if (exception) debugPrint("Exception Occured"); - } else if (result.data != null && result.isConcrete) { - return result.data; - } - - return result.data; - } - - Future signup({String name, String email, String password}) async { - final QueryResult result = email != null - ? await clientNonAuth.mutate(MutationOptions( - document: gql(_authQuery.registerUser(name, email, password)))) - : await clientNonAuth.mutate( - MutationOptions(document: gql(_authQuery.loginAsGuest(name)))); - if (result.hasException) { - final bool exception = encounteredExceptionOrError(result.exception); - debugPrint('${result.exception.graphqlErrors}'); - return false; - } else if (result.data != null && result.isConcrete) { - final User signedInUser = - User.fromJson(result.data['register'] as Map); - final bool logIn = email != null - ? await databaseFunctions.login( - email: email, password: password, user: signedInUser) - : await databaseFunctions.login(user: signedInUser); - return logIn; - } - return false; - } - - Future login({String email, String password, User user}) async { - final QueryResult result = (user != null && email == null) - ? await clientNonAuth.mutate( - MutationOptions(document: gql(_authQuery.loginUsingID(user.id)))) - : await clientNonAuth.mutate(MutationOptions( - document: gql(_authQuery.loginUser(email, password)))); - if (result.hasException) { - navigationService.showSnackBar( - "Something went wrong: ${result.exception.graphqlErrors.first.message}"); - print("${result.exception.graphqlErrors}"); - return false; - } else if (result.data != null && result.isConcrete) { - bool userSaved = false; - if (user != null && email == null) { - user.isGuest = true; - user.authToken = "Bearer ${result.data['login']}"; - userSaved = await userConfig.updateUser(user); - } else { - final User loggedInUser = - User(authToken: "Bearer ${result.data['login']}", isGuest: false); - userSaved = await userConfig.updateUser(loggedInUser); - } - final bool fetchInfo = await databaseFunctions.fetchCurrentUserInfo(); - return userSaved && fetchInfo; - } - return false; - } - - Future fetchCurrentUserInfo() async { - final QueryResult result = await clientAuth - .query(QueryOptions(document: gql(_authQuery.fetchUserInfo()))); - if (result.hasException) { - final bool exception = - encounteredExceptionOrError(result.exception, showSnackBar: false); - if (exception) { - await userConfig.currentUser.delete(); - navigationService.pushReplacementScreen('/auth'); - } - } else if (result.data != null && result.isConcrete) { - final User userInfo = User.fromJson( - result.data['me'] as Map, - ); - userInfo.authToken = userConfig.currentUser.authToken; - userInfo.isGuest = userConfig.currentUser.isGuest; - userConfig.updateUser(userInfo); - return true; - } - return false; - } - - Future fetchBeaconInfo(String id) async { - final QueryResult result = await clientAuth - .query(QueryOptions(document: gql(_beaconQuery.fetchBeaconDetail(id)))); - if (result.hasException) { - final bool exception = - encounteredExceptionOrError(result.exception, showSnackBar: false); - if (exception) { - print('Exception: ${result.exception}'); - } - } else if (result.data != null && result.isConcrete) { - final Beacon beacon = Beacon.fromJson( - result.data['beacon'] as Map, - ); - return beacon; - } - return null; - } - - Future> fetchUserBeacons() async { - List beacons = []; - final QueryResult result = await clientAuth - .query(QueryOptions(document: gql(_authQuery.fetchUserInfo()))); - if (result.hasException) { - final bool exception = - encounteredExceptionOrError(result.exception, showSnackBar: false); - if (exception) { - print('$exception'); - } - } else if (result.data != null && result.isConcrete) { - final User userInfo = User.fromJson( - result.data['me'] as Map, - ); - beacons = userInfo.beacon; - } - return beacons; - } - - Future createBeacon(String title, int expiresAt) async { - LatLng loc; - try { - loc = await AppConstants.getLocation(); - } catch (onErr) { - navigationService - .showSnackBar("$onErr : Allow location access to start beacon"); - return null; - } - final QueryResult result = await clientAuth.mutate(MutationOptions( - document: gql(_beaconQuery.createBeacon(title, expiresAt, - loc.latitude.toString(), loc.longitude.toString())))); - if (result.hasException) { - navigationService.showSnackBar( - "Something went wrong: ${result.exception.graphqlErrors.first.message}"); - print("Something went wrong: ${result.exception}"); - } else if (result.data != null && result.isConcrete) { - final Beacon beacon = Beacon.fromJson( - result.data['createBeacon'] as Map, - ); - return beacon; - } - return null; - } - - Future updateLeaderLoc(String id, LatLng latLng) async { - final QueryResult result = await clientAuth.mutate(MutationOptions( - document: gql(_beaconQuery.updateLeaderLoc( - id, latLng.latitude.toString(), latLng.longitude.toString())))); - if (result.hasException) { - print("Something went wrong: ${result.exception}"); - navigationService.showSnackBar( - "Something went wrong: ${result.exception.graphqlErrors.first.message}"); - } else if (result.data != null && result.isConcrete) { - final Location location = Location.fromJson( - result.data['updateBeaconLocation']['location'] as Map, - ); - print('location update successful'); - return location; - } - return null; - } - - Future joinBeacon(String shortcode) async { - final QueryResult result = await clientAuth.mutate( - MutationOptions(document: gql(_beaconQuery.joinBeacon(shortcode)))); - if (result.hasException) { - navigationService.showSnackBar( - "Something went wrong: ${result.exception.graphqlErrors.first.message}"); - print("Something went wrong: ${result.exception}"); - } else if (result.data != null && result.isConcrete) { - final Beacon beacon = Beacon.fromJson( - result.data['joinBeacon'] as Map, - ); - beacon.route.add(beacon.leader.location); - return beacon; - } - return null; - } - - Future createLandmark(String title, LatLng loc, String id) async { - await clientAuth - .mutate(MutationOptions( - document: gql(_beaconQuery.createLandmark( - id, loc.latitude.toString(), loc.longitude.toString(), title)))) - .then((value) { - if (value.hasException) { - navigationService.showSnackBar( - "Something went wrong: ${value.exception.graphqlErrors.first.message}"); - print("Something went wrong: ${value.exception}"); - } else if (value.data != null && value.isConcrete) { - final Landmark landmark = Landmark.fromJson( - value.data['createLandmark'] as Map, - ); - return landmark; - } - return null; - }); - return null; - } - - Future> fetchNearbyBeacon() async { - await databaseFunctions.init(); - List _nearbyBeacons = []; - LatLng loc; - try { - loc = await AppConstants.getLocation(); - } catch (onErr) { - return null; - } - final QueryResult result = await clientAuth.query(QueryOptions( - document: gql(_beaconQuery.fetchNearbyBeacons( - loc.latitude.toString(), loc.longitude.toString())))); - if (result.hasException) { - final bool exception = - encounteredExceptionOrError(result.exception, showSnackBar: false); - if (exception) { - print('${result.exception}'); - return null; - } - } else if (result.data != null && result.isConcrete) { - _nearbyBeacons = (result.data['nearbyBeacons'] as List) - .map((e) => Beacon.fromJson(e as Map)) - .toList(); - return _nearbyBeacons; - } - return _nearbyBeacons; - } -} diff --git a/lib/services/graphql_config.dart b/lib/services/graphql_config.dart deleted file mode 100644 index c664b9e0..00000000 --- a/lib/services/graphql_config.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:beacon/locator.dart'; -import 'package:flutter/material.dart'; -import 'package:graphql_flutter/graphql_flutter.dart'; -import 'package:web_socket_channel/web_socket_channel.dart'; -import 'package:web_socket_channel/io.dart'; - -class GraphQLConfig { - static String token; - static final HttpLink httpLink = HttpLink( - "https://beacon.aadibajpai.com/graphql", - ); - - static final AuthLink authLink = AuthLink(getToken: () async => token); - - static final WebSocketLink websocketLink = - WebSocketLink('ws://beacon.aadibajpai.com/subscriptions/', - config: SocketClientConfig( - autoReconnect: true, - )); - - Future getToken() async { - final _token = userConfig.currentUser.authToken; - token = _token; - return true; - } - - static final Link link = authLink.concat(httpLink).concat(websocketLink); - GraphQLClient clientToQuery() { - return GraphQLClient( - cache: GraphQLCache(partialDataPolicy: PartialDataCachePolicy.accept), - link: httpLink.concat(websocketLink), - ); - } - - GraphQLClient authClient() { - final AuthLink authLink = - AuthLink(getToken: () async => userConfig.currentUser.authToken); - final Link finalAuthLink = authLink.concat(httpLink); - return GraphQLClient( - cache: GraphQLCache(), - link: finalAuthLink, - ); - } - - GraphQLClient webSocketClient() { - return GraphQLClient( - cache: GraphQLCache(), - link: - Link.split((request) => request.isSubscription, websocketLink, link), - ); - } - - ValueNotifier client = ValueNotifier(GraphQLClient( - link: link, - cache: GraphQLCache(), - )); -} diff --git a/lib/services/navigation_service.dart b/lib/services/navigation_service.dart deleted file mode 100644 index 938b7936..00000000 --- a/lib/services/navigation_service.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -class NavigationService { - GlobalKey navigatorKey = GlobalKey(); - - Future pushScreen(String routeName, {dynamic arguments}) { - return navigatorKey.currentState.pushNamed(routeName, arguments: arguments); - } - - Future pushReplacementScreen(String routeName, {dynamic arguments}) { - return navigatorKey.currentState - .pushReplacementNamed(routeName, arguments: arguments); - } - - void fromInviteLink(List routeNames, List arguments) { - int i = 0; - removeAllAndPush('/${routeNames[i]}', '/', arguments: arguments[i]); - for (i = 1; i < routeNames.length; i++) { - pushScreen('/${routeNames[i]}', arguments: arguments[i]); - } - } - - Future removeAllAndPush(String routeName, String tillRoute, - {dynamic arguments}) { - return navigatorKey.currentState.pushNamedAndRemoveUntil( - routeName, ModalRoute.withName(tillRoute), - arguments: arguments); - } - - void pushDialog(Widget dialog) { - showDialog( - context: navigatorKey.currentContext, - barrierColor: Colors.transparent, - barrierDismissible: false, - builder: (BuildContext context) { - return dialog; - }); - } - - void showSnackBar(String message, - {Duration duration = const Duration(seconds: 2)}) { - ScaffoldMessenger.of(navigatorKey.currentContext) - .showSnackBar(SnackBar(duration: duration, content: Text(message))); - } - - void pop() { - return navigatorKey.currentState.pop(); - } -} diff --git a/lib/services/shared_preference_service.dart b/lib/services/shared_preference_service.dart deleted file mode 100644 index 53431a38..00000000 --- a/lib/services/shared_preference_service.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:shared_preferences/shared_preferences.dart'; - -class SharedPreferenceService { - SharedPreferences _prefs; - - Future getSharedPreferencesInstance() async { - _prefs = await SharedPreferences.getInstance().catchError((e) { - print("shared preferences error : $e"); - return false; - }); - return true; - } - - Future setToken(String token) async { - await _prefs.setString('token', token); - } - - Future clearToken() async { - await _prefs.clear(); - } - - Future get token async => _prefs.getString('token'); -} - -SharedPreferenceService sharedPreferenceService = SharedPreferenceService(); \ No newline at end of file diff --git a/lib/services/size_config.dart b/lib/services/size_config.dart deleted file mode 100644 index dd4bf4bb..00000000 --- a/lib/services/size_config.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:flutter/widgets.dart'; - -class SizeConfig { - static MediaQueryData _mediaQueryData; - static double screenWidth; - static double screenHeight; - static double blockSizeHorizontal; - static double blockSizeVertical; - static double paddingTop; - - static double _safeAreaHorizontal; - static double _safeAreaVertical; - static double safeBlockHorizontal; - static double safeBlockVertical; - - void init(BuildContext context) { - _mediaQueryData = MediaQuery.of(context); - screenWidth = _mediaQueryData.size.width; - screenHeight = _mediaQueryData.size.height; - blockSizeHorizontal = screenWidth / 100; - blockSizeVertical = screenHeight / 100; - - _safeAreaHorizontal = - _mediaQueryData.padding.left + _mediaQueryData.padding.right; - _safeAreaVertical = - _mediaQueryData.padding.top + _mediaQueryData.padding.bottom; - safeBlockHorizontal = (screenWidth - _safeAreaHorizontal) / 100; - safeBlockVertical = (screenHeight - _safeAreaVertical) / 100; - debugPrint("safeBlockHorizontal: $safeBlockHorizontal"); - debugPrint("safeBlockVertical: $safeBlockVertical"); - } -} diff --git a/lib/services/user_config.dart b/lib/services/user_config.dart deleted file mode 100644 index 32f1a767..00000000 --- a/lib/services/user_config.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'dart:async'; -import 'package:flutter/material.dart'; -import 'package:hive/hive.dart'; -import 'package:beacon/models/user/user_info.dart'; -import '../locator.dart'; - -class UserConfig { - User _currentUser = User(id: 'null', authToken: 'null'); - User get currentUser => _currentUser; - - Future userLoggedIn() async { - final boxUser = Hive.box('currentUser'); - _currentUser = boxUser.get('user'); - if (_currentUser == null) { - _currentUser = User(id: 'null', authToken: 'null'); - return false; - } - bool userUpdated = true; - graphqlConfig.getToken().then((value) async { - print('${userConfig._currentUser.authToken}'); - databaseFunctions.init(); - final bool fetchUpdates = await databaseFunctions.fetchCurrentUserInfo(); - if (fetchUpdates) { - saveUserInHive(); - userUpdated = true; - } else { - navigationService.showSnackBar("Couldn't update User details"); - userUpdated = false; - } - }); - print('user updated: $userUpdated'); - return userUpdated; - } - - Future updateUser(User updatedUserDetails) async { - try { - _currentUser = updatedUserDetails; - print("User is guest or not: ${updatedUserDetails.isGuest}"); - saveUserInHive(); - return true; - } on Exception catch (e) { - debugPrint(e.toString()); - return false; - } - } - - saveUserInHive() { - final box = Hive.box('currentUser'); - if (box.get('user') == null) { - box.put('user', _currentUser); - } else { - box.put('user', _currentUser); - } - } -} diff --git a/lib/services/validators.dart b/lib/services/validators.dart deleted file mode 100644 index d6749d7e..00000000 --- a/lib/services/validators.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:http/http.dart' as http; - -class Validator { - static String validateEmail(String email) { - // If email is empty return. - if (email.isEmpty) { - return "Email must not be left blank"; - } - const String pattern = - r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?)*$"; - final RegExp regex = RegExp(pattern); - if (!regex.hasMatch(email)) { - return 'Please enter a valid Email Address'; - } - return null; - } - - static String validatePassword(String password) { - // If password is empty return. - if (password.isEmpty) { - return "Password must not be left blank"; - } - // const String pattern = r'^(?=.*?[0-9])(?=.*?[!@#\$&*%^~.]).{8,}$'; - // final RegExp regExp = RegExp(pattern); - - //Regex for no spaces allowed - const String noSpaces = r'^\S+$'; - final RegExp noSpaceRegex = RegExp(noSpaces); - - if (password.length < 8) { - return "Must be of atleast 8 characters"; - } - // if (!regExp.hasMatch(password)) { - // return "At least 1 number and symbol required"; - // } - if (!noSpaceRegex.hasMatch(password)) { - return "Password must not contain spaces"; - } - return null; - } - - static String validateBeaconTitle(String title) { - if (title.isEmpty) { - return "Title must not be left blank"; - } - return null; - } - - static String validatePasskey(String passkey) { - if (passkey.isEmpty) { - return "Passkey must not be left blank"; - } - const String pattern = r'[A-Z]+'; - final RegExp regExp = RegExp(pattern); - if (!regExp.hasMatch(passkey) || passkey.length != 6) { - return "Invalid passkey"; - } - return null; - } -} diff --git a/lib/splash_screen.dart b/lib/splash_screen.dart deleted file mode 100644 index bd859812..00000000 --- a/lib/splash_screen.dart +++ /dev/null @@ -1,117 +0,0 @@ -import 'dart:async'; - -import 'package:beacon/models/beacon/beacon.dart'; -import 'package:beacon/views/hike_screen.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:beacon/locator.dart'; -import 'package:uni_links/uni_links.dart'; - -class SplashScreen extends StatefulWidget { - const SplashScreen({@required Key key}) : super(key: key); - - @override - _SplashScreenState createState() => _SplashScreenState(); -} - -class _SplashScreenState extends State { - Uri _initialUri; - Uri _latestUri; - StreamSubscription _sub; - bool isCheckingUrl = false; - - Future _handleInitialUri() async { - _sub = uriLinkStream.listen((Uri uri) { - if (!mounted) return; - setState(() { - _latestUri = uri; - }); - }, onError: (Object err) { - if (!mounted) return; - setState(() { - _latestUri = null; - }); - }); - try { - final uri = await getInitialUri(); - if (!mounted) return; - setState(() => _initialUri = uri); - } on PlatformException { - if (!mounted) return; - setState(() => _initialUri = null); - } on FormatException catch (err) { - debugPrint(err.toString()); - if (!mounted) return; - setState(() => _initialUri = null); - } - if (_latestUri == null && _initialUri == null) { - final bool userLoggedIn = await userConfig.userLoggedIn(); - Future.delayed(const Duration(milliseconds: 750)).then((value) async { - if (userLoggedIn) { - navigationService.pushReplacementScreen('/main'); - } else { - navigationService.pushReplacementScreen('/auth'); - } - }); - } else { - if (_initialUri != null) { - var shortcode = _initialUri.queryParameters['shortcode']; - final bool userLoggedIn = await userConfig.userLoggedIn(); - - Future.delayed(const Duration(milliseconds: 1200)).then((value) async { - if (userLoggedIn) { - databaseFunctions.init(); - final Beacon beacon = await databaseFunctions.joinBeacon(shortcode); - Future.delayed(const Duration(milliseconds: 750)) - .then((value) async { - if (beacon != null) { - navigationService.pushScreen('/hikeScreen', - arguments: HikeScreen(beacon, isLeader: false)); - } else { - navigationService.showSnackBar('SomeThing went wrong'); - navigationService.pushReplacementScreen('/main'); - } - }); - } else { - // login in anonymously and join hike - databaseFunctions.init(); - await databaseFunctions.signup(name: "Anonymous"); - final Beacon beacon = await databaseFunctions.joinBeacon(shortcode); - Future.delayed(const Duration(milliseconds: 750)) - .then((value) async { - if (beacon != null) { - navigationService.pushScreen('/hikeScreen', - arguments: HikeScreen(beacon, isLeader: false)); - } else { - navigationService.showSnackBar('SomeThing went wrong'); - navigationService.pushReplacementScreen('/main'); - } - }); - } - }); - } - } - } - - @override - void initState() { - _handleInitialUri(); - super.initState(); - } - - @override - void dispose() { - _sub.cancel(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - key: const Key('SplashScreenScaffold'), - body: Center( - child: new Image(image: new AssetImage('images/hikers_group.png')), - ), - ); - } -} diff --git a/lib/theme.dart b/lib/theme.dart new file mode 100644 index 00000000..a57f4de5 --- /dev/null +++ b/lib/theme.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; + +class CustomTheme { + static const String _fontFamily = + 'FuturaBold'; // Match the name in pubspec.yaml + + // Define your custom colors + Color kYellow = Color(0xFFFDBB2C); + Color kBlue = Color(0xFF222375); + Color lightkBlue = Color(0xFF535393); + Color kLightBlue = Color(0xFFE8F1F8); + Color kBlack = Color(0xFF343434); + Color shimmerSkeletonColor = Color(0xff4e4f91); + Color hintColor = Colors.black54; + + // Define your custom theme data + ThemeData myTheme = ThemeData( + fontFamily: _fontFamily, + // Define primary colors + primaryColor: const Color(0xFF222375), + hintColor: const Color.fromARGB(255, 105, 103, 103), + + // Define text themes + textTheme: const TextTheme( + displayLarge: TextStyle( + fontSize: 19, + fontWeight: FontWeight.bold, + fontFamily: _fontFamily, + color: Colors.white, + ), + displayMedium: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w800, + fontFamily: _fontFamily, + color: Colors.black, + ), + bodyLarge: TextStyle( + fontSize: 16, + fontFamily: _fontFamily, + color: Colors.white, + ), + ), + + // Define other theme properties such as fonts, buttons, etc. + ); +} diff --git a/lib/view_model/auth_screen_model.dart b/lib/view_model/auth_screen_model.dart deleted file mode 100644 index 4ce1aff4..00000000 --- a/lib/view_model/auth_screen_model.dart +++ /dev/null @@ -1,119 +0,0 @@ -import 'package:beacon/enums/view_state.dart'; -import 'package:beacon/utilities/constants.dart'; -import 'package:flutter/material.dart'; -import 'package:beacon/locator.dart'; -import 'package:beacon/view_model/base_view_model.dart'; - -class AuthViewModel extends BaseModel { - final formKeySignup = GlobalKey(); - final formKeyLogin = GlobalKey(); - - AutovalidateMode validate = AutovalidateMode.disabled; - final GlobalKey scaffoldKey = new GlobalKey(); - - final FocusNode emailLogin = FocusNode(); - final FocusNode passwordLogin = FocusNode(); - - final FocusNode password = FocusNode(); - final FocusNode email = FocusNode(); - final FocusNode name = FocusNode(); - - TextEditingController loginEmailController = new TextEditingController(); - TextEditingController loginPasswordController = new TextEditingController(); - - bool obscureTextLogin = true; - bool obscureTextSignup = true; - - TextEditingController signupEmailController = new TextEditingController(); - TextEditingController signupNameController = new TextEditingController(); - TextEditingController signupPasswordController = new TextEditingController(); - - PageController pageController = PageController(); - - Color left = Colors.white; - Color right = Colors.black; - - Color leftBg = kLightBlue; - Color rightBg = kBlue; - - next_signup() async { - FocusScope.of(navigationService.navigatorKey.currentContext).unfocus(); - validate = AutovalidateMode.always; - if (formKeySignup.currentState.validate()) { - setState(ViewState.busy); - validate = AutovalidateMode.disabled; - databaseFunctions.init(); - final bool signUpSuccess = await databaseFunctions.signup( - name: signupNameController.text ?? "Anonymous", - email: signupEmailController.text, - password: signupPasswordController.text); - if (signUpSuccess) { - userConfig.currentUser.print(); - navigationService.removeAllAndPush('/main', '/'); - } else { - navigationService.showSnackBar('SomeThing went wrong'); - } - setState(ViewState.idle); - } else { - navigationService.showSnackBar('Enter valid entries'); - } - } - - loginAsGuest() async { - setState(ViewState.busy); - databaseFunctions.init(); - final bool signUpSuccess = - await databaseFunctions.signup(name: "Anonymous"); - if (signUpSuccess) { - userConfig.currentUser.print(); - navigationService.removeAllAndPush('/main', '/'); - } else { - navigationService.showSnackBar('SomeThing went wrong'); - } - setState(ViewState.idle); - } - - next_login() async { - FocusScope.of(navigationService.navigatorKey.currentContext).unfocus(); - validate = AutovalidateMode.always; - if (formKeyLogin.currentState.validate()) { - setState(ViewState.busy); - validate = AutovalidateMode.disabled; - databaseFunctions.init(); - final bool loginSuccess = await databaseFunctions.login( - email: loginEmailController.text, - password: loginPasswordController.text); - if (loginSuccess) { - userConfig.currentUser.print(); - navigationService.removeAllAndPush('/main', '/'); - } else { - navigationService.showSnackBar('SomeThing went wrong'); - } - setState(ViewState.idle); - } else { - navigationService.showSnackBar('Enter valid entries'); - } - } - - void onSignInButtonPress() { - pageController.animateToPage(0, - duration: Duration(milliseconds: 500), curve: Curves.decelerate); - } - - void onSignUpButtonPress() { - pageController?.animateToPage(1, - duration: Duration(milliseconds: 500), curve: Curves.decelerate); - } - - displayPasswordLogin() { - setState(ViewState.busy); - obscureTextLogin = !obscureTextLogin; - setState(ViewState.idle); - } - - displayPasswordSignup() { - setState(ViewState.busy); - obscureTextSignup = !obscureTextSignup; - setState(ViewState.idle); - } -} diff --git a/lib/view_model/base_view_model.dart b/lib/view_model/base_view_model.dart deleted file mode 100644 index d0b66cb8..00000000 --- a/lib/view_model/base_view_model.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:beacon/enums/view_state.dart'; - -class BaseModel extends ChangeNotifier { - ViewState _state = ViewState.idle; - - ViewState get state => _state; - bool get isBusy => _state == ViewState.busy; - - void setState(ViewState viewState) { - _state = viewState; - notifyListeners(); - } -} diff --git a/lib/view_model/hike_screen_model.dart b/lib/view_model/hike_screen_model.dart deleted file mode 100644 index b402ad1b..00000000 --- a/lib/view_model/hike_screen_model.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'dart:async'; - -import 'package:beacon/models/location/location.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:fluttertoast/fluttertoast.dart'; -import 'package:geocoder/geocoder.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; - -import 'package:beacon/components/dialog_boxes.dart'; -import 'package:beacon/enums/view_state.dart'; -import 'package:share/share.dart'; -import 'package:beacon/models/beacon/beacon.dart'; -import 'package:beacon/models/user/user_info.dart'; -import 'package:beacon/view_model/base_view_model.dart'; - -class HikeScreenViewModel extends BaseModel { - double screenHeight, screenWidth, lat, long; - Beacon beacon; - bool isGeneratingLink = false, isReferred, isBeaconExpired = false; - List hikers = []; - List route = []; - Duration newDuration = Duration(seconds: 0); - Completer mapController = Completer(); - String address; - - Future createDynamicLink(bool short) async { - // setState(() { - // _isGeneratingLink = true; - // }); - } - - void relayBeacon(User newHolder) { - Fluttertoast.showToast(msg: 'Beacon handed over to $newHolder'); - } - - Future onWillPop(context) async { - return (await showDialog( - context: context, - // builder: (context) => DialogBoxes.showExitDialog(context), - )) ?? - false; - } - - void initialise(Beacon beaconParsed) async { - setState(ViewState.busy); - beacon = beaconParsed; - hikers.add(beacon.leader); - hikers.addAll(beacon.followers); - lat = double.parse(beacon.route.last.lat); - long = double.parse(beacon.route.last.lon); - route.add(Location(lat: beacon.route.last.lat, lon: beacon.route.last.lon)); - Coordinates coordinates = Coordinates(lat, long); - var addresses = - await Geocoder.local.findAddressesFromCoordinates(coordinates); - address = addresses.first.addressLine; - setState(ViewState.idle); - } - - void beaconExpired() { - // TODO - Fluttertoast.showToast(msg: 'Beacon Expired'); - } - - // startCountdown() { - // Future.delayed( - // DateTime.fromMillisecondsSinceEpoch(beacon.expiresAt) - // .difference(DateTime.now()), () { - // beaconExpired(); - // }); - // } - - generateUrl(String shortcode) async { - setState(ViewState.busy); - Uri url = Uri.parse('https://beacon.aadibajpai.com/?shortcode=$shortcode'); - Share.share('To join beacon follow this link: $url'); - setState(ViewState.idle); - } -} diff --git a/lib/view_model/home_view_model.dart b/lib/view_model/home_view_model.dart deleted file mode 100644 index 8a5e7348..00000000 --- a/lib/view_model/home_view_model.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:beacon/enums/view_state.dart'; -import 'package:beacon/locator.dart'; -import 'package:beacon/models/beacon/beacon.dart'; -import 'package:beacon/view_model/base_view_model.dart'; -import 'package:beacon/views/hike_screen.dart'; -import 'package:flutter/material.dart'; - -class HomeViewModel extends BaseModel { - final formKeyCreate = GlobalKey(); - final formKeyJoin = GlobalKey(); - Duration resultingDuration = Duration(minutes: 30); - AutovalidateMode validate = AutovalidateMode.onUserInteraction; - bool isCreatingHike = false; - String title; - TextEditingController _titleController = new TextEditingController(); - TextEditingController durationController = new TextEditingController(); - String enteredPasskey = ''; - - createHikeRoom() async { - FocusScope.of(navigationService.navigatorKey.currentContext).unfocus(); - validate = AutovalidateMode.always; - if (formKeyCreate.currentState.validate()) { - navigationService.pop(); - setState(ViewState.busy); - validate = AutovalidateMode.disabled; - databaseFunctions.init(); - final Beacon beacon = await databaseFunctions.createBeacon(title, - DateTime.now().add(resultingDuration).millisecondsSinceEpoch.toInt()); - // setState(ViewState.idle); - if (beacon != null) { - navigationService.pushScreen('/hikeScreen', - arguments: HikeScreen( - beacon, - isLeader: true, - )); - } else { - // navigationService.showSnackBar('Something went wrong'); - setState(ViewState.idle); - } - } - } - - joinHikeRoom() async { - FocusScope.of(navigationService.navigatorKey.currentContext).unfocus(); - validate = AutovalidateMode.always; - if (formKeyJoin.currentState.validate()) { - setState(ViewState.busy); - validate = AutovalidateMode.disabled; - databaseFunctions.init(); - final Beacon beacon = await databaseFunctions.joinBeacon(enteredPasskey); - // setState(ViewState.idle); - if (beacon != null) { - navigationService.pushScreen('/hikeScreen', - arguments: HikeScreen(beacon, isLeader: false)); - } else { - navigationService.showSnackBar('SomeThing went wrong'); - } - } else { - navigationService.showSnackBar('Enter valid passkey'); - } - } - - logout() async { - setState(ViewState.busy); - await userConfig.currentUser.delete(); - // setState(ViewState.idle); - navigationService.removeAllAndPush('/auth', '/'); - } -} diff --git a/lib/views/auth_screen.dart b/lib/views/auth_screen.dart deleted file mode 100644 index 455b7d28..00000000 --- a/lib/views/auth_screen.dart +++ /dev/null @@ -1,379 +0,0 @@ -import 'package:beacon/components/hike_button.dart'; -import 'package:beacon/components/shape_painter.dart'; -import 'package:beacon/services/validators.dart'; -import 'package:beacon/utilities/indication_painter.dart'; -import 'package:beacon/view_model/auth_screen_model.dart'; -import 'package:beacon/views/base_view.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -class AuthScreen extends StatefulWidget { - const AuthScreen({Key key}) : super(key: key); - - @override - _AuthScreenState createState() => _AuthScreenState(); -} - -class _AuthScreenState extends State - with SingleTickerProviderStateMixin { - @override - Widget build(BuildContext context) { - return BaseView(builder: (context, model, child) { - return (model.isBusy) - ? Scaffold(body: Center(child: CircularProgressIndicator())) - : new Scaffold( - key: model.scaffoldKey, - // resizeToAvoidBottomInset: false, - body: Container( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height >= 775.0 - ? MediaQuery.of(context).size.height - : 775.0, - child: Stack( - children: [ - CustomPaint( - size: Size(MediaQuery.of(context).size.width, - MediaQuery.of(context).size.height), - painter: ShapePainter(), - ), - Container( - alignment: Alignment.center, - padding: EdgeInsets.only( - top: MediaQuery.of(context).size.height / 3.5), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: EdgeInsets.only(top: 20.0), - child: _buildMenuBar(context, model), - ), - Expanded( - flex: 2, - child: PageView( - controller: model.pageController, - onPageChanged: (i) { - if (i == 0) { - setState(() { - model.right = Colors.black; - model.left = Colors.white; - }); - } else if (i == 1) { - setState(() { - model.right = Colors.white; - model.left = Colors.black; - }); - } - }, - children: [ - new ConstrainedBox( - constraints: const BoxConstraints.expand(), - child: _buildSignIn(context, model), - ), - new ConstrainedBox( - constraints: const BoxConstraints.expand(), - child: _buildSignUp(context, model), - ), - ], - ), - ), - ], - ), - ), - ], - ), - ), - ); - }); - } - - Widget _buildMenuBar(BuildContext context, AuthViewModel model) { - return Container( - padding: EdgeInsets.symmetric(horizontal: 50), - width: MediaQuery.of(context).size.width, - height: 50.0, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(25.0)), - ), - child: CustomPaint( - painter: TabIndicationPainter(pageController: model.pageController), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Expanded( - child: FlatButton( - splashColor: Colors.white, - highlightColor: Colors.white, - onPressed: model.onSignInButtonPress, - child: Text( - "Existing", - style: TextStyle( - color: model.left, - fontSize: 16.0, - ), - ), - ), - ), - //Container(height: 33.0, width: 1.0, color: Colors.white), - Expanded( - child: FlatButton( - splashColor: Colors.transparent, - highlightColor: Colors.transparent, - onPressed: model.onSignUpButtonPress, - child: Text( - "New", - style: TextStyle( - color: model.right, - fontSize: 16.0, - ), - ), - ), - ), - ], - ), - ), - ); - } - - Widget _buildSignIn(BuildContext context, AuthViewModel model) { - return SingleChildScrollView( - child: Container( - padding: EdgeInsets.only(top: 23.0, left: 35, right: 35), - width: MediaQuery.of(context).size.width, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Card( - elevation: 2.0, - color: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), - ), - child: Form( - key: model.formKeyLogin, - autovalidateMode: model.validate, - child: Container( - // width: MediaQuery.of(context).size.width - 35, - // height: MediaQuery.of(context).size.height / 4.3, - child: Column( - children: [ - Padding( - padding: - EdgeInsets.symmetric(horizontal: 10, vertical: 20), - child: TextFormField( - focusNode: model.emailLogin, - controller: model.loginEmailController, - validator: (value) => Validator.validateEmail(value), - keyboardType: TextInputType.emailAddress, - style: TextStyle(fontSize: 16.0, color: Colors.black), - decoration: InputDecoration( - border: InputBorder.none, - icon: Icon( - Icons.mail_outline, - color: Colors.black, - size: 22.0, - ), - hintText: "Email Address", - hintStyle: TextStyle(fontSize: 17.0), - ), - ), - ), - Container( - width: 250.0, - height: 1.0, - color: Colors.grey[400], - ), - Padding( - padding: - EdgeInsets.symmetric(horizontal: 10, vertical: 20), - child: TextFormField( - focusNode: model.passwordLogin, - controller: model.loginPasswordController, - obscureText: model.obscureTextLogin, - validator: (value) => - Validator.validatePassword(value), - style: TextStyle(fontSize: 16.0, color: Colors.black), - decoration: InputDecoration( - border: InputBorder.none, - icon: Icon( - Icons.lock, - size: 22.0, - color: Colors.black, - ), - hintText: "Password", - hintStyle: TextStyle(fontSize: 17.0), - suffixIcon: IconButton( - onPressed: () => model.displayPasswordLogin(), - icon: Icon( - model.obscureTextLogin - ? Icons.remove_red_eye_sharp - : Icons.remove_red_eye_outlined, - size: 15.0, - color: Colors.black, - ), - ), - ), - ), - ), - ], - ), - ), - ), - ), - SizedBox( - height: 20, - ), - HikeButton( - onTap: model.next_login, - text: 'LOGIN', - buttonWidth: 90, - buttonHeight: 15, - ), - Padding( - padding: - EdgeInsets.only(left: 15.0, right: 15.0, top: 20, bottom: 20), - child: Text( - "Or", - style: TextStyle( - color: Colors.black, - fontSize: 16.0, - ), - ), - ), - HikeButton( - onTap: () => model.loginAsGuest(), - text: 'LOGIN AS GUEST', - buttonHeight: 15, - buttonWidth: 35, - ), - ], - ), - ), - ); - } - - Widget _buildSignUp(BuildContext context, AuthViewModel model) { - return SingleChildScrollView( - child: Container( - padding: EdgeInsets.only(top: 23.0), - child: Column( - children: [ - Card( - elevation: 2.0, - color: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), - ), - child: Form( - key: model.formKeySignup, - autovalidateMode: model.validate, - child: Container( - width: MediaQuery.of(context).size.width - 70, - // height: 280.0, - child: Column(children: [ - Padding( - padding: - EdgeInsets.symmetric(horizontal: 13, vertical: 20), - child: TextFormField( - focusNode: model.name, - textInputAction: TextInputAction.next, - controller: model.signupNameController, - keyboardType: TextInputType.text, - textCapitalization: TextCapitalization.words, - style: TextStyle(fontSize: 16.0, color: Colors.black), - decoration: InputDecoration( - border: InputBorder.none, - icon: Icon( - Icons.account_box, - color: Colors.black, - ), - hintText: "Name", - hintStyle: TextStyle(fontSize: 16.0), - ), - ), - ), - Container( - width: 250.0, - height: 1.0, - color: Colors.grey[400], - ), - Padding( - padding: - EdgeInsets.symmetric(horizontal: 10, vertical: 20), - child: TextFormField( - validator: (value) => Validator.validateEmail(value), - focusNode: model.email, - textInputAction: TextInputAction.next, - controller: model.signupEmailController, - keyboardType: TextInputType.emailAddress, - style: TextStyle(fontSize: 16.0, color: Colors.black), - decoration: InputDecoration( - border: InputBorder.none, - icon: Icon( - Icons.mail, - color: Colors.black, - ), - hintText: "Email Address", - hintStyle: TextStyle(fontSize: 16.0), - ), - ), - ), - Container( - width: 250.0, - height: 1.0, - color: Colors.grey[400], - ), - Padding( - padding: - EdgeInsets.symmetric(horizontal: 10, vertical: 20), - child: TextFormField( - focusNode: model.password, - textInputAction: TextInputAction.done, - validator: (value) => - Validator.validatePassword(value), - controller: model.signupPasswordController, - obscureText: model.obscureTextSignup, - style: TextStyle(fontSize: 16.0, color: Colors.black), - decoration: InputDecoration( - border: InputBorder.none, - icon: Icon( - Icons.lock, - color: Colors.black, - ), - suffixIcon: IconButton( - onPressed: () => model.displayPasswordSignup(), - icon: Icon( - model.obscureTextSignup - ? Icons.remove_red_eye_sharp - : Icons.remove_red_eye_outlined, - size: 15.0, - color: Colors.black, - ), - ), - hintText: "Password", - hintStyle: TextStyle(fontSize: 16.0), - ), - ), - ), - ])), - ), - ), - SizedBox( - height: 15, - ), - Container( - // margin: EdgeInsets.only(top: 300.0), - decoration: new BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(5.0)), - ), - child: HikeButton( - onTap: () => model.next_signup(), - text: 'SIGNIN', - buttonHeight: 18, - buttonWidth: 55, - )), - ], - ), - ), - ); - } -} diff --git a/lib/views/base_view.dart b/lib/views/base_view.dart deleted file mode 100644 index e7444e2c..00000000 --- a/lib/views/base_view.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:beacon/locator.dart'; - -class BaseView extends StatefulWidget { - const BaseView({ - @required this.builder, - this.onModelReady, - }); - final Function(T) onModelReady; - final Widget Function(BuildContext, T, Widget) builder; - - @override - _BaseViewState createState() => _BaseViewState(); -} - -class _BaseViewState extends State> { - T model = locator(); - - @override - void initState() { - if (widget.onModelReady != null) { - widget.onModelReady( - model, - ); - } - super.initState(); - } - - @override - Widget build( - BuildContext context, - ) { - return ChangeNotifierProvider( - create: (context) => model, - child: Consumer( - builder: widget.builder, - ), - ); - } -} diff --git a/lib/views/hike_screen.dart b/lib/views/hike_screen.dart deleted file mode 100644 index 019746a2..00000000 --- a/lib/views/hike_screen.dart +++ /dev/null @@ -1,553 +0,0 @@ -import 'dart:async'; -import 'package:beacon/components/hike_button.dart'; -import 'package:flutter/rendering.dart'; -import 'package:beacon/components/create_join_dialog.dart'; -import 'package:beacon/models/landmarks/landmark.dart'; -import 'package:beacon/queries/beacon.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:fluttertoast/fluttertoast.dart'; -import 'package:geocoder/geocoder.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; -import 'package:graphql_flutter/graphql_flutter.dart'; -import 'package:intl/intl.dart'; -import 'package:location/location.dart'; -import 'package:modal_progress_hud/modal_progress_hud.dart'; -import 'package:flutter_polyline_points/flutter_polyline_points.dart'; -import 'package:beacon/components/dialog_boxes.dart'; -import 'package:beacon/components/hike_screen_widget.dart'; -import 'package:beacon/components/shape_painter.dart'; -import 'package:beacon/locator.dart'; -import 'package:beacon/models/beacon/beacon.dart'; -import 'package:beacon/models/user/user_info.dart'; -import 'package:beacon/services/graphql_config.dart'; -import 'package:beacon/utilities/constants.dart'; -import 'package:rxdart/rxdart.dart'; -import 'package:sliding_up_panel/sliding_up_panel.dart'; - -class HikeScreen extends StatefulWidget { - final Beacon beacon; - final bool isLeader; - HikeScreen(this.beacon, {this.isLeader}); - @override - _HikeScreenState createState() => _HikeScreenState(); -} - -class _HikeScreenState extends State { - double screenHeight, screenWidth; - Beacon beacon; - bool isGeneratingLink = false, isReferred, isBeaconExpired = false; - List hikers = []; - List route = []; - Duration newDuration = Duration(seconds: 0); - Completer mapController = Completer(); - String address, prevAddress; - bool isBusy = false; - Set markers = {}; - StreamSubscription _leaderLocation; - Stream _streamLocation, _streamFollower, _mixedStream; - List polylineCoordinates = []; - PolylinePoints polylinePoints = PolylinePoints(); - Map polylines = {}; - final GlobalKey _landmarkFormKey = GlobalKey(); - ScrollController _scrollController = ScrollController(); - Location loc = new Location(); - PanelController _panelController = PanelController(); - - setPolyline() async { - PolylineResult result = await polylinePoints.getRouteBetweenCoordinates( - 'AIzaSyCXlRxfbr9Y368nLy8o59r0_XZmHdK5-2w', // Google Maps API Key - PointLatLng(route.first.latitude, route.first.longitude), - PointLatLng(route.last.latitude, route.last.longitude), - ); - if (result.points.isNotEmpty) { - result.points.forEach((PointLatLng point) { - polylineCoordinates.add(LatLng(point.latitude, point.longitude)); - }); - } - PolylineId id = PolylineId('poly'); - Polyline polyline = Polyline( - polylineId: id, - color: Colors.red, - points: polylineCoordinates, - width: 3, - ); - polylines[id] = polyline; - } - - _addMarker() { - markers.add(Marker( - markerId: MarkerId((markers.length + 1).toString()), - position: route.last, - )); - } - - initSubscriptions() { - if (widget.isLeader) { - _leaderLocation = - loc.onLocationChanged.listen((LocationData currentLocation) async { - Coordinates coordinates = - Coordinates(currentLocation.latitude, currentLocation.longitude); - var addresses = - await Geocoder.local.findAddressesFromCoordinates(coordinates); - - String _address = addresses.first.addressLine; - if (address != _address) { - databaseFunctions.init(); - await databaseFunctions.updateLeaderLoc(widget.beacon.id, - LatLng(currentLocation.latitude, currentLocation.longitude)); - setState(() { - address = _address; - _addMarker(); - setPolyline(); - }); - } - }); - } else { - _streamLocation = GraphQLConfig().client.value.subscribe( - SubscriptionOptions( - document: - gql(BeaconQueries().fetchLocationUpdates(widget.beacon.id)))); - } - - _streamFollower = GraphQLConfig().client.value.subscribe( - SubscriptionOptions( - document: - gql(BeaconQueries().fetchFollowerUpdates(widget.beacon.id)))); - if (!widget.isLeader) { - _mixedStream = MergeStream([_streamFollower, _streamLocation]); - } else { - _mixedStream = _streamFollower; - } - setState(() { - isBusy = false; - }); - } - - fetchData() async { - Coordinates coordinates = Coordinates( - double.parse(widget.beacon.location.lat), - double.parse(widget.beacon.location.lon)); - var addresses = - await Geocoder.local.findAddressesFromCoordinates(coordinates); - await databaseFunctions.fetchBeaconInfo(widget.beacon.id).then((value) { - beacon = value; - setState(() { - hikers.add(value.leader); - hikers.addAll(value.followers); - var lat = double.parse(value.location.lat); - var lon = double.parse(value.location.lon); - route.add(LatLng(lat, lon)); - address = addresses.first.addressLine; - markers.add(Marker( - markerId: MarkerId("0"), - position: route.first, - )); - markers.add(Marker( - markerId: MarkerId("1"), - position: route.last, - )); - for (var i in value.landmarks) { - markers.add(Marker( - markerId: MarkerId((markers.length + 1).toString()), - position: LatLng( - double.parse(i.location.lat), double.parse(i.location.lon)), - infoWindow: InfoWindow( - title: '${i.title}', - ), - icon: - BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueBlue), - )); - } - }); - }); - } - - @override - void initState() { - super.initState(); - isBusy = true; - fetchData(); - initSubscriptions(); - } - - @override - void dispose() { - super.dispose(); - if (widget.isLeader) { - _leaderLocation.cancel(); - } - } - - @override - Widget build(BuildContext context) { - screenHeight = MediaQuery.of(context).size.height; - screenWidth = MediaQuery.of(context).size.width; - return isBusy - ? CircularProgressIndicator() - : WillPopScope( - onWillPop: () => onWillPop(context), - child: Scaffold( - body: ModalProgressHUD( - inAsyncCall: isGeneratingLink || isBusy, - child: StreamBuilder( - stream: _mixedStream, - builder: (context, snapshot) { - if (snapshot.hasData) { - print(snapshot.data); - if (snapshot.data.data != null && - snapshot?.data?.data['beaconJoined'] != null) { - User newJoinee = - User.fromJson(snapshot.data.data['beaconJoined']); - setState(() { - hikers.add(newJoinee); - }); - } else if (snapshot.data.data != null && - snapshot?.data?.data['beaconLocation'] != null) { - setState(() { - markers.removeWhere( - (element) => element.markerId == MarkerId("1")); - markers.add(Marker( - markerId: MarkerId("1"), - position: LatLng( - double.parse(snapshot - .data.data['beaconLocation']['lat']), - double.parse(snapshot - .data.data['beaconLocation']['lon'])))); - }); - } - } - return SlidingUpPanel( - maxHeight: MediaQuery.of(context).size.height * 0.6, - minHeight: 154, - controller: _panelController, - collapsed: Container( - decoration: BoxDecoration( - color: kBlue, - borderRadius: BorderRadius.only( - topRight: Radius.circular(10), - topLeft: Radius.circular(10))), - child: Column( - children: [ - SizedBox( - height: 12.0, - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - width: 60, - height: 5, - decoration: BoxDecoration( - color: Colors.grey[300], - borderRadius: BorderRadius.all( - Radius.circular(12.0))), - ), - ], - ), - SizedBox( - height: 10, - ), - Container( - width: double.infinity, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 15), - child: RichText( - text: TextSpan( - style: TextStyle( - fontWeight: FontWeight.bold), - children: [ - TextSpan( - text: isBeaconExpired - ? 'Beacon has been expired\n' - : 'Beacon expiring at ${widget.beacon.expiresAt == null ? '' : DateFormat("hh:mm a, d/M/y").format(DateTime.fromMillisecondsSinceEpoch(widget.beacon.expiresAt)).toString()}\n', - style: TextStyle(fontSize: 16)), - TextSpan( - text: - 'Beacon holder at: $address\n', - style: TextStyle(fontSize: 14)), - TextSpan( - text: - 'Total Followers: ${hikers.length - 1} (Swipe to view the list of followers)\n', - style: TextStyle(fontSize: 12)), - TextSpan( - text: - 'Share this passkey to add user: ${widget.beacon.shortcode}\n', - style: TextStyle(fontSize: 12)), - ]), - ), - ), - height: 120, - ), - ], - ), - ), - panel: _panel(_scrollController), - body: Stack( - alignment: Alignment.topCenter, - children: [ - GoogleMap( - mapType: MapType.terrain, - markers: markers.toSet(), - polylines: Set.of(polylines.values), - initialCameraPosition: CameraPosition( - target: LatLng( - double.parse(widget.beacon.location.lat), - double.parse(widget.beacon.location.lon)), - zoom: 17.0), - onMapCreated: (GoogleMapController controller) { - mapController.complete(controller); - }, - onTap: (loc) async { - String title; - showDialog( - context: context, - builder: (context) => Dialog( - child: Container( - height: 250, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 32, vertical: 16), - child: Form( - key: _landmarkFormKey, - child: Column( - children: [ - Container( - height: 100, - child: Padding( - padding: - const EdgeInsets.all(4.0), - child: TextFormField( - onChanged: (key) { - title = key; - }, - validator: (value) { - if (value == null || - value.isEmpty) { - return "Please enter title for landmark"; - } else { - return null; - } - }, - decoration: InputDecoration( - alignLabelWithHint: true, - floatingLabelBehavior: - FloatingLabelBehavior - .always, - hintText: - 'Add title for the landmark', - hintStyle: TextStyle( - fontSize: 15, - color: kBlack), - labelText: 'Title', - labelStyle: TextStyle( - fontSize: 20, - color: kYellow), - ), - ), - ), - color: kLightBlue, - ), - SizedBox( - height: 30, - ), - Flexible( - child: HikeButton( - buttonWidth: 25, - text: 'Create Landmark', - textColor: Colors.white, - buttonColor: kYellow, - onTap: () async { - if (_landmarkFormKey - .currentState - .validate()) { - navigationService.pop(); - await databaseFunctions - .init(); - await databaseFunctions - .createLandmark( - title, - loc, - beacon.id) - .then((value) { - setState(() { - markers.add(Marker( - markerId: MarkerId( - (markers.length + - 1) - .toString()), - position: loc, - infoWindow: - InfoWindow( - title: '$title', - ), - icon: BitmapDescriptor - .defaultMarkerWithHue( - BitmapDescriptor - .hueBlue), - )); - }); - }); - } - }), - ), - ], - ), - ), - ), - ), - ), - ); - }, - ), - CustomPaint( - size: Size(screenWidth, screenHeight), - foregroundPainter: ShapePainter(), - ), - Align( - alignment: Alignment(0.9, -0.8), - child: HikeScreenWidget.shareButton( - context, widget.beacon.shortcode)), - Align( - alignment: Alignment(-0.8, -0.8), - child: GestureDetector( - onTap: () { - onWillPop(context); - }, - child: Icon( - Icons.arrow_back, - size: 30, - color: Colors.white, - ), - ), - ), - ], - ), - ); - }), - ), - ), - ); - } - - Column _panel(ScrollController sc) { - return Column(children: [ - SizedBox( - height: 15.0, - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - width: 60, - height: 5, - decoration: BoxDecoration( - color: Colors.grey[300], - borderRadius: BorderRadius.all(Radius.circular(12.0))), - ), - ], - ), - SizedBox( - height: 12, - ), - Container( - height: MediaQuery.of(context).size.height * 0.6 - 32, - child: ListView( - controller: sc, - physics: AlwaysScrollableScrollPhysics(), - children: [ - widget.isLeader - ? Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: RichText( - text: TextSpan( - style: TextStyle( - fontWeight: FontWeight.bold, color: kBlack), - children: [ - TextSpan( - text: - 'Long Press on any hiker to hand over the beacon\n', - style: TextStyle(fontSize: 16)), - TextSpan( - text: - 'Double tap on beacon to change the duration\n', - style: TextStyle(fontSize: 14)), - ]), - ), - ) - : Container(), - SizedBox( - height: 6, - ), - Material( - child: ListView.builder( - shrinkWrap: true, - clipBehavior: Clip.antiAlias, - scrollDirection: Axis.vertical, - physics: const NeverScrollableScrollPhysics(), - itemCount: hikers.length, - itemBuilder: (BuildContext context, int index) { - return ListTile( - onTap: () { - hikers[index].id == userConfig.currentUser.id - ? Fluttertoast.showToast(msg: 'Yeah, that\'s you') - : beacon.leader.id == userConfig.currentUser.id - ? relayBeacon(hikers[index]) - : Fluttertoast.showToast( - msg: 'You dont have beacon to relay'); - }, - leading: CircleAvatar( - backgroundColor: - isBeaconExpired ? Colors.grey : kYellow, - radius: 18, - child: ClipRRect( - borderRadius: BorderRadius.circular(50), - child: Icon( - Icons.person_outline, - color: Colors.white, - )), - ), - title: Text( - hikers[index].name, - style: TextStyle(color: Colors.black, fontSize: 18), - ), - trailing: hikers[index].id == beacon.leader.id - ? GestureDetector( - onDoubleTap: () { - !widget.isLeader - ? Fluttertoast.showToast( - msg: - 'Only beacon holder has access to change the duration') - : DialogBoxes.changeDurationDialog(context); - }, - child: Icon( - Icons.room, - color: isBeaconExpired ? Colors.grey : kYellow, - size: 40, - ), - ) - : Container(width: 10), - ); - }, - ), - ), - ]), - ), - ]); - } - - Future onWillPop(context) async { - return (await showDialog( - context: context, - builder: (context) => DialogBoxes.showExitDialog( - context, widget.isLeader, hikers.length), - )) ?? - false; - } - - void relayBeacon(User newHolder) { - Fluttertoast.showToast(msg: 'Beacon handed over to $newHolder'); - } -} diff --git a/lib/views/home.dart b/lib/views/home.dart deleted file mode 100644 index 74039c86..00000000 --- a/lib/views/home.dart +++ /dev/null @@ -1,285 +0,0 @@ -import 'package:beacon/components/beacon_card.dart'; -import 'package:beacon/components/create_join_dialog.dart'; -import 'package:beacon/components/dialog_boxes.dart'; -import 'package:beacon/components/hike_button.dart'; -import 'package:beacon/components/shape_painter.dart'; -import 'package:beacon/locator.dart'; -import 'package:beacon/models/beacon/beacon.dart'; -import 'package:beacon/utilities/constants.dart'; -import 'package:beacon/view_model/home_view_model.dart'; -import 'package:beacon/views/base_view.dart'; -import 'package:flutter/material.dart'; -import 'package:modal_progress_hud/modal_progress_hud.dart'; - -class MainScreen extends StatefulWidget { - const MainScreen({Key key}) : super(key: key); - - @override - _MainScreenState createState() => _MainScreenState(); -} - -class _MainScreenState extends State with TickerProviderStateMixin { - @override - Widget build(BuildContext context) { - return BaseView(builder: (context, model, child) { - TabController tabController = new TabController(length: 2, vsync: this); - return model.isBusy - ? Scaffold(body: Center(child: CircularProgressIndicator())) - : Scaffold( - resizeToAvoidBottomInset: false, - body: SafeArea( - child: ModalProgressHUD( - inAsyncCall: model.isCreatingHike, - child: Stack( - children: [ - CustomPaint( - size: Size(MediaQuery.of(context).size.width, - MediaQuery.of(context).size.height - 200), - painter: ShapePainter(), - ), - Align( - alignment: Alignment(0.9, -0.8), - child: FloatingActionButton( - onPressed: () => showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text( - (userConfig.currentUser.isGuest) - ? 'Create Account' - : 'Logout', - style: TextStyle( - fontSize: 25, color: kYellow), - ), - content: Text( - (userConfig.currentUser.isGuest) - ? 'Would you like to create an account?' - : 'Are you sure you wanna logout?', - style: TextStyle( - fontSize: 16, color: kBlack), - ), - actions: [ - HikeButton( - buttonHeight: 20, - buttonWidth: 40, - onTap: () => - Navigator.of(context).pop(false), - text: 'No', - ), - HikeButton( - buttonHeight: 20, - buttonWidth: 40, - onTap: () { - navigationService.pop(); - model.logout(); - }, - text: 'Yes', - ), - ], - )), - backgroundColor: kYellow, - child: (userConfig.currentUser.isGuest) - ? Icon(Icons.person) - : Icon(Icons.logout), - ), - ), - Padding( - padding: EdgeInsets.fromLTRB(8, 185, 8, 5), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - HikeButton( - text: 'Create Hike', - textColor: Colors.white, - borderColor: Colors.white, - buttonColor: kYellow, - buttonWidth: 12, - onTap: () { - if (userConfig.currentUser.isGuest) { - navigationService.showSnackBar( - 'You need to login with credentials to start a hike'); - } else { - CreateJoinBeaconDialog.createHikeDialog( - context, model); - } - }, - ), - HikeButton( - text: 'Join a Hike', - textColor: kYellow, - borderColor: kYellow, - buttonColor: Colors.white, - buttonWidth: 12, - onTap: () async { - CreateJoinBeaconDialog.joinBeaconDialog( - context, model); - }, - ) - ], - ), - ), - Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - height: MediaQuery.of(context).size.height / 1.75, - margin: EdgeInsets.only(top: 20), - decoration: BoxDecoration( - color: kLightBlue, - borderRadius: BorderRadius.only( - topLeft: const Radius.circular(50.0), - topRight: const Radius.circular(50.0))), - child: Column( - children: [ - TabBar( - indicatorSize: TabBarIndicatorSize.tab, - indicatorColor: kBlue, - labelColor: kBlack, - tabs: [ - Tab(text: 'Your Beacons'), - Tab(text: 'Nearby Beacons'), - ], - controller: tabController, - ), - Expanded( - child: TabBarView( - controller: tabController, - children: [ - Padding( - padding: const EdgeInsets.all(12.0), - child: FutureBuilder( - future: databaseFunctions - .fetchUserBeacons(), - builder: (context, snapshot) { - if (snapshot.connectionState == - ConnectionState.done) { - if (snapshot.hasError) { - return Center( - child: Text( - snapshot.error.toString(), - textAlign: TextAlign.center, - textScaleFactor: 1.3, - ), - ); - } - final List posts = - snapshot.data; - return Container( - alignment: Alignment.center, - child: posts.length == 0 - ? SingleChildScrollView( - physics: - AlwaysScrollableScrollPhysics(), - child: Center( - child: Text( - 'You haven\'t joined or created any beacon yet :(', - style: TextStyle( - color: - kBlack, - fontSize: - 18))), - ) - : ListView.builder( - physics: - AlwaysScrollableScrollPhysics(), - scrollDirection: - Axis.vertical, - itemCount: - posts?.length, - padding: - EdgeInsets.all(8), - itemBuilder: - (context, index) { - return BeaconCustomWidgets - .getBeaconCard( - context, - posts[ - index]); - }, - )); - } else { - return Center( - child: BeaconCustomWidgets - .getPlaceholder(), - ); - } - }, - ), - ), - Container( - alignment: Alignment.center, - child: FutureBuilder( - future: databaseFunctions - .fetchNearbyBeacon(), - builder: (context, snapshot) { - if (snapshot.connectionState == - ConnectionState.done) { - if (snapshot.hasError) { - return Center( - child: Text( - snapshot.error.toString(), - textAlign: TextAlign.center, - textScaleFactor: 1.3, - ), - ); - } - - final posts = snapshot.data; - if (posts == null || - posts.length == 0) { - return SingleChildScrollView( - physics: - AlwaysScrollableScrollPhysics(), - child: Center( - child: Text( - 'No nearby beacons found :(', - style: TextStyle( - color: kBlack, - fontSize: 18))), - ); - } - return ListView.builder( - physics: - AlwaysScrollableScrollPhysics(), - scrollDirection: Axis.vertical, - itemCount: posts.length, - padding: EdgeInsets.all(8), - itemBuilder: (context, index) { - return BeaconCustomWidgets - .getBeaconCard(context, - posts[index]); - }, - ); - } else { - return SingleChildScrollView( - physics: - AlwaysScrollableScrollPhysics(), - child: Center( - child: Text( - 'No nearby beacons found :(', - style: TextStyle( - color: kBlack, - fontSize: 18))), - ); - } - }, - ), - ), - ], - ), - ), - ], - ), - ), - ], - ), - ], - ), - ), - ), - ); - }); - } -} diff --git a/lib/views/workspace.code-workspace b/lib/views/workspace.code-workspace deleted file mode 100644 index ce7c3760..00000000 --- a/lib/views/workspace.code-workspace +++ /dev/null @@ -1,11 +0,0 @@ -{ - "folders": [ - { - "path": "../.." - }, - { - "path": "../../../../EnHike/images" - } - ], - "settings": {} -} \ No newline at end of file diff --git a/linux/.gitignore b/linux/.gitignore new file mode 100644 index 00000000..d3896c98 --- /dev/null +++ b/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt new file mode 100644 index 00000000..90fb9e64 --- /dev/null +++ b/linux/CMakeLists.txt @@ -0,0 +1,145 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "beacon") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.example.beacon") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +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() + +# Define build configuration 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. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +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() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +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}") + +# Define the application target. To change its name, change BINARY_NAME above, +# not the value here, or `flutter run` will no longer work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +# Run the Flutter tool portions of the build. This must not be removed. +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) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# 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/linux/flutter/CMakeLists.txt b/linux/flutter/CMakeLists.txt new file mode 100644 index 00000000..d5bd0164 --- /dev/null +++ b/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +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/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000..f6f23bfe --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include + +void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); +} diff --git a/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000..e0f0a47b --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake new file mode 100644 index 00000000..f16b4c34 --- /dev/null +++ b/linux/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_linux +) + +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/linux/main.cc b/linux/main.cc new file mode 100644 index 00000000..e7c5c543 --- /dev/null +++ b/linux/main.cc @@ -0,0 +1,6 @@ +#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/linux/my_application.cc b/linux/my_application.cc new file mode 100644 index 00000000..c3fa79f6 --- /dev/null +++ b/linux/my_application.cc @@ -0,0 +1,104 @@ +#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, "beacon"); + 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, "beacon"); + } + + 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/linux/my_application.h b/linux/my_application.h new file mode 100644 index 00000000..72271d5e --- /dev/null +++ b/linux/my_application.h @@ -0,0 +1,18 @@ +#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/macos/.gitignore b/macos/.gitignore new file mode 100644 index 00000000..746adbb6 --- /dev/null +++ b/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 00000000..4b81f9b2 --- /dev/null +++ b/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 00000000..5caa9d15 --- /dev/null +++ b/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 00000000..3b47e80a --- /dev/null +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,28 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import connectivity_plus +import device_info_plus +import flutter_local_notifications +import geolocator_apple +import google_sign_in_ios +import location +import path_provider_foundation +import share_plus +import shared_preferences_foundation + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) + DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) + FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) + GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) + FLTGoogleSignInPlugin.register(with: registry.registrar(forPlugin: "FLTGoogleSignInPlugin")) + LocationPlugin.register(with: registry.registrar(forPlugin: "LocationPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) +} diff --git a/macos/Podfile b/macos/Podfile new file mode 100644 index 00000000..c795730d --- /dev/null +++ b/macos/Podfile @@ -0,0 +1,43 @@ +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/macos/Podfile.lock b/macos/Podfile.lock new file mode 100644 index 00000000..ce5c339a --- /dev/null +++ b/macos/Podfile.lock @@ -0,0 +1,73 @@ +PODS: + - connectivity_plus (0.0.1): + - FlutterMacOS + - ReachabilitySwift + - flutter_local_notifications (0.0.1): + - FlutterMacOS + - FlutterMacOS (1.0.0) + - geolocator_apple (1.2.0): + - FlutterMacOS + - location (0.0.1): + - FlutterMacOS + - modal_progress_hud_nsn (0.0.1): + - FlutterMacOS + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - ReachabilitySwift (5.0.0) + - share_plus (0.0.1): + - FlutterMacOS + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS + +DEPENDENCIES: + - connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`) + - flutter_local_notifications (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos`) + - FlutterMacOS (from `Flutter/ephemeral`) + - geolocator_apple (from `Flutter/ephemeral/.symlinks/plugins/geolocator_apple/macos`) + - location (from `Flutter/ephemeral/.symlinks/plugins/location/macos`) + - modal_progress_hud_nsn (from `Flutter/ephemeral/.symlinks/plugins/modal_progress_hud_nsn/macos`) + - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) + - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) + - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) + +SPEC REPOS: + trunk: + - ReachabilitySwift + +EXTERNAL SOURCES: + connectivity_plus: + :path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos + flutter_local_notifications: + :path: Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos + FlutterMacOS: + :path: Flutter/ephemeral + geolocator_apple: + :path: Flutter/ephemeral/.symlinks/plugins/geolocator_apple/macos + location: + :path: Flutter/ephemeral/.symlinks/plugins/location/macos + modal_progress_hud_nsn: + :path: Flutter/ephemeral/.symlinks/plugins/modal_progress_hud_nsn/macos + path_provider_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin + share_plus: + :path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos + shared_preferences_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin + +SPEC CHECKSUMS: + connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747 + flutter_local_notifications: 3805ca215b2fb7f397d78b66db91f6a747af52e4 + FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 + geolocator_apple: 72a78ae3f3e4ec0db62117bd93e34523f5011d58 + location: 7cdb0665bd6577d382b0a343acdadbcb7f964775 + modal_progress_hud_nsn: 8099d46c2cf9de7af8fe0a3f8f5d2aa32cf956c3 + path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c + ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 + share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7 + shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 + +PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 + +COCOAPODS: 1.15.2 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..36528b75 --- /dev/null +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,791 @@ +// !$*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 */ + 2DF8873C90925E5B812AFA02 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 082F17B8E2FFA1516D17AA17 /* Pods_RunnerTests.framework */; }; + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; + 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 */; }; + DCE1466E5A58C64E6C856312 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1034300CB80096E49F9DD01 /* Pods_Runner.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 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 */ + 082F17B8E2FFA1516D17AA17 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; 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 /* beacon.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = beacon.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 = ""; }; + 5D8BA4AFF049D8C9E67BA0FB /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 7EA481464D47228636CD122B /* 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 = ""; }; + 899A97D187B50B492C399063 /* 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 = ""; }; + 8A6CBB06D6EBE604CA3ACFCF /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + A1034300CB80096E49F9DD01 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E4B5C11C76E4A237E46AD106 /* 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 = ""; }; + FCE014195BC474DAB37381A1 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2DF8873C90925E5B812AFA02 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DCE1466E5A58C64E6C856312 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 269C1FA85054AD00FC722174 /* Pods */ = { + isa = PBXGroup; + children = ( + E4B5C11C76E4A237E46AD106 /* Pods-Runner.debug.xcconfig */, + 7EA481464D47228636CD122B /* Pods-Runner.release.xcconfig */, + 899A97D187B50B492C399063 /* Pods-Runner.profile.xcconfig */, + 8A6CBB06D6EBE604CA3ACFCF /* Pods-RunnerTests.debug.xcconfig */, + FCE014195BC474DAB37381A1 /* Pods-RunnerTests.release.xcconfig */, + 5D8BA4AFF049D8C9E67BA0FB /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 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 */, + 331C80D6294CF71000263BE5 /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + 269C1FA85054AD00FC722174 /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* beacon.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, + ); + 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 = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + A1034300CB80096E49F9DD01 /* Pods_Runner.framework */, + 082F17B8E2FFA1516D17AA17 /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 0D119790046456CC2D5DCDBD /* [CP] Check Pods Manifest.lock */, + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + D6473656BDE293E3E92F3D0C /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + 33295FFD373B8D40AE5FF10C /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* beacon.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 = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 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 */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 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 */ + 0D119790046456CC2D5DCDBD /* [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-RunnerTests-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; + }; + 33295FFD373B8D40AE5FF10C /* [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; + }; + 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"; + }; + D6473656BDE293E3E92F3D0C /* [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 */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 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 */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; + 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 */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8A6CBB06D6EBE604CA3ACFCF /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.beacon.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/beacon.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/beacon"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = FCE014195BC474DAB37381A1 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.beacon.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/beacon.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/beacon"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5D8BA4AFF049D8C9E67BA0FB /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.beacon.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/beacon.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/beacon"; + }; + name = Profile; + }; + 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.14; + 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.14; + 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.14; + 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 */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 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/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..6099c631 --- /dev/null +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..21a3cc14 --- /dev/null +++ b/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift new file mode 100644 index 00000000..d53ef643 --- /dev/null +++ b/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..a2ec33f1 --- /dev/null +++ b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "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/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 00000000..82b6f9d9 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 00000000..13b35eba Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 00000000..0a3f5fa4 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 00000000..bdb57226 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 00000000..f083318e Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 00000000..326c0e72 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 00000000..2f1632cf Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/macos/Runner/Base.lproj/MainMenu.xib b/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 00000000..80e867a4 --- /dev/null +++ b/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 00000000..d205eeba --- /dev/null +++ b/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// 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 = beacon + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.beacon + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2024 com.example. All rights reserved. diff --git a/macos/Runner/Configs/Debug.xcconfig b/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 00000000..36b0fd94 --- /dev/null +++ b/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Release.xcconfig b/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 00000000..dff4f495 --- /dev/null +++ b/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Warnings.xcconfig b/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 00000000..42bcbf47 --- /dev/null +++ b/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +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/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements new file mode 100644 index 00000000..dddb8a30 --- /dev/null +++ b/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist new file mode 100644 index 00000000..4789daa6 --- /dev/null +++ b/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + 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/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 00000000..3cc05eb2 --- /dev/null +++ b/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements new file mode 100644 index 00000000..852fa1a4 --- /dev/null +++ b/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/macos/RunnerTests/RunnerTests.swift b/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 00000000..5418c9f5 --- /dev/null +++ b/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import FlutterMacOS +import Cocoa +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/pubspec.lock b/pubspec.lock index d9554d96..0b4c1256 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,296 +5,447 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - url: "https://pub.dartlang.org" + sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + url: "https://pub.dev" source: hosted - version: "22.0.0" + version: "67.0.0" analyzer: dependency: transitive description: name: analyzer - url: "https://pub.dartlang.org" + sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + url: "https://pub.dev" source: hosted - version: "1.7.1" + version: "6.4.1" archive: dependency: transitive description: name: archive - url: "https://pub.dartlang.org" + sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d + url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.6.1" args: dependency: transitive description: name: args - url: "https://pub.dartlang.org" + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.5.0" async: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + auto_route: + dependency: "direct main" + description: + name: auto_route + sha256: a9001a90539ca3effc168f7e1029a5885c7326b9032c09ac895e303c1d137704 + url: "https://pub.dev" source: hosted - version: "2.6.1" + version: "8.3.0" + bloc: + dependency: "direct main" + description: + name: bloc + sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e" + url: "https://pub.dev" + source: hosted + version: "8.1.4" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" build: dependency: transitive description: name: build - url: "https://pub.dartlang.org" + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "2.4.1" build_config: dependency: transitive description: name: build_config - url: "https://pub.dartlang.org" + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.1" build_daemon: dependency: transitive description: name: build_daemon - url: "https://pub.dartlang.org" + sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" + url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "4.0.2" build_resolvers: dependency: transitive description: name: build_resolvers - url: "https://pub.dartlang.org" + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" + url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "2.4.2" build_runner: dependency: "direct dev" description: name: build_runner - url: "https://pub.dartlang.org" + sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7" + url: "https://pub.dev" source: hosted - version: "2.0.4" + version: "2.4.11" build_runner_core: dependency: transitive description: name: build_runner_core - url: "https://pub.dartlang.org" + sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe + url: "https://pub.dev" source: hosted - version: "7.0.0" + version: "7.3.1" built_collection: dependency: transitive description: name: built_collection - url: "https://pub.dartlang.org" + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" source: hosted - version: "5.1.0" + version: "5.1.1" built_value: dependency: transitive description: name: built_value - url: "https://pub.dartlang.org" + sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb + url: "https://pub.dev" source: hosted - version: "8.0.6" + version: "8.9.2" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" checked_yaml: dependency: transitive description: name: checked_yaml - url: "https://pub.dartlang.org" + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.3" cli_util: dependency: transitive description: name: cli_util - url: "https://pub.dartlang.org" + sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 + url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.4.1" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" code_builder: dependency: transitive description: name: code_builder - url: "https://pub.dartlang.org" + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 + url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.10.0" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.18.0" connectivity_plus: dependency: "direct main" description: name: connectivity_plus - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.3" - connectivity_plus_linux: - dependency: transitive - description: - name: connectivity_plus_linux - url: "https://pub.dartlang.org" + sha256: b74247fad72c171381dbe700ca17da24deac637ab6d43c343b42867acb95c991 + url: "https://pub.dev" source: hosted - version: "1.0.3" - connectivity_plus_macos: - dependency: transitive - description: - name: connectivity_plus_macos - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.2" + version: "3.0.6" connectivity_plus_platform_interface: dependency: transitive description: name: connectivity_plus_platform_interface - url: "https://pub.dartlang.org" + sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a + url: "https://pub.dev" source: hosted - version: "1.0.2" - connectivity_plus_web: + version: "1.2.4" + convert: dependency: transitive description: - name: connectivity_plus_web - url: "https://pub.dartlang.org" + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" source: hosted - version: "1.0.2" - connectivity_plus_windows: + version: "3.1.1" + coverage: dependency: transitive description: - name: connectivity_plus_windows - url: "https://pub.dartlang.org" + name: coverage + sha256: "576aaab8b1abdd452e0f656c3e73da9ead9d7880e15bdc494189d9c1a1baf0db" + url: "https://pub.dev" source: hosted - version: "1.0.2" - convert: + version: "1.9.0" + cross_file: dependency: transitive description: - name: convert - url: "https://pub.dartlang.org" + name: cross_file + sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" + url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "0.3.4+2" crypto: dependency: transitive description: name: crypto - url: "https://pub.dartlang.org" + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.3" + csslib: + dependency: transitive + description: + name: csslib + sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" + url: "https://pub.dev" + source: hosted + version: "1.0.0" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - url: "https://pub.dartlang.org" + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.0.8" dart_style: dependency: transitive description: name: dart_style - url: "https://pub.dartlang.org" + sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" + url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.3.6" + data_connection_checker_nulls: + dependency: "direct main" + description: + name: data_connection_checker_nulls + sha256: "86a67945b1c78f17d094ea175a4b0be4a20bcd89146ce5523974b614693091e2" + url: "https://pub.dev" + source: hosted + version: "0.0.2" date_time_picker: dependency: "direct main" description: name: date_time_picker - url: "https://pub.dartlang.org" + sha256: "6923c568bcb67a66ab7e083708d0adbcae8214b41bb84d49febc17e89e06fc4a" + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.1.0" dbus: dependency: transitive description: name: dbus - url: "https://pub.dartlang.org" + sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" + url: "https://pub.dev" + source: hosted + version: "0.7.10" + device_info_plus: + dependency: transitive + description: + name: device_info_plus + sha256: a7fd703482b391a87d60b6061d04dfdeab07826b96f9abd8f5ed98068acc0074 + url: "https://pub.dev" + source: hosted + version: "10.1.2" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: "282d3cf731045a2feb66abfe61bbc40870ae50a3ed10a4d3d217556c35c8c2ba" + url: "https://pub.dev" + source: hosted + version: "7.0.1" + duration_picker: + dependency: "direct main" + description: + name: duration_picker + sha256: e505a749c93f3218aa4194d339e5d5480d927df23a81f075b5282511f6ac11ab + url: "https://pub.dev" source: hosted - version: "0.5.2" + version: "1.2.0" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.1" ffi: dependency: transitive description: name: ffi - url: "https://pub.dartlang.org" + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" + url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "2.1.3" file: dependency: transitive description: name: file - url: "https://pub.dartlang.org" + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" source: hosted - version: "6.1.2" + version: "7.0.0" fixnum: dependency: transitive description: name: fixnum - url: "https://pub.dartlang.org" + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.0" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" - flutter_duration_picker: + flutter_animarker: dependency: "direct main" description: - path: "." - ref: HEAD - resolved-ref: "285ff19ad8b440fb6b8727ea849b7ce679d67c75" - url: "https://github.com/bartektartanus/flutter_duration_picker" - source: git - version: "1.0.4" + name: flutter_animarker + sha256: d95448cc1776bc8e17aea3b24c01f3b7565c6eab394f708bc9fa9f7763e9dd77 + url: "https://pub.dev" + source: hosted + version: "3.2.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a + url: "https://pub.dev" + source: hosted + version: "8.1.6" + flutter_config: + dependency: "direct main" + description: + name: flutter_config + sha256: a07e6156bb6e776e29c6357be433155acda87d1dab1a3f787a72091a1b71ffbf + url: "https://pub.dev" + source: hosted + version: "2.0.2" + flutter_countdown_timer: + dependency: "direct main" + description: + name: flutter_countdown_timer + sha256: dfcbd7d6f76a5589f78f3f3ba2f9ea2e199368eccc1adce4153ce985b9587bc5 + url: "https://pub.dev" + source: hosted + version: "4.1.0" + flutter_geocoder_alternative: + dependency: "direct main" + description: + name: flutter_geocoder_alternative + sha256: "77ec25113ad1d603a8136e14f33217a2bc707e2346cdf8fbc5d501bf8b13df36" + url: "https://pub.dev" + source: hosted + version: "1.0.3" + flutter_hooks: + dependency: transitive + description: + name: flutter_hooks + sha256: "6a126f703b89499818d73305e4ce1e3de33b4ae1c5512e3b8eab4b986f46774c" + url: "https://pub.dev" + source: hosted + version: "0.18.6" flutter_launcher_icons: dependency: "direct dev" description: name: flutter_launcher_icons - url: "https://pub.dartlang.org" + sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea" + url: "https://pub.dev" source: hosted - version: "0.9.1" + version: "0.13.1" + flutter_local_notifications: + dependency: "direct main" + description: + name: flutter_local_notifications + sha256: c500d5d9e7e553f06b61877ca6b9c8b92c570a4c8db371038702e8ce57f8a50f + url: "https://pub.dev" + source: hosted + version: "17.2.2" + flutter_local_notifications_linux: + dependency: transitive + description: + name: flutter_local_notifications_linux + sha256: c49bd06165cad9beeb79090b18cd1eb0296f4bf4b23b84426e37dd7c027fc3af + url: "https://pub.dev" + source: hosted + version: "4.0.1" + flutter_local_notifications_platform_interface: + dependency: transitive + description: + name: flutter_local_notifications_platform_interface + sha256: "85f8d07fe708c1bdcf45037f2c0109753b26ae077e9d9e899d55971711a4ea66" + url: "https://pub.dev" + source: hosted + version: "7.2.0" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle - url: "https://pub.dartlang.org" + sha256: "9d98bd47ef9d34e803d438f17fd32b116d31009f534a6fa5ce3a1167f189a6de" + url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.21" flutter_polyline_points: dependency: "direct main" description: name: flutter_polyline_points - url: "https://pub.dartlang.org" + sha256: "02699e69142f51a248d784b6e3eec524194467fca5f7c4da19699ce2368b6980" + url: "https://pub.dev" source: hosted version: "1.0.0" + flutter_slidable: + dependency: "direct main" + description: + name: flutter_slidable + sha256: "2c5611c0b44e20d180e4342318e1bbc28b0a44ad2c442f5df16962606fd3e8e3" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + flutter_spinkit: + dependency: "direct main" + description: + name: flutter_spinkit + sha256: d2696eed13732831414595b98863260e33e8882fc069ee80ec35d4ac9ddb0472 + url: "https://pub.dev" + source: hosted + version: "5.2.1" flutter_test: dependency: "direct dev" description: flutter @@ -309,471 +460,794 @@ packages: dependency: "direct main" description: name: fluttertoast - url: "https://pub.dartlang.org" + sha256: "7eae679e596a44fdf761853a706f74979f8dd3cd92cf4e23cae161fda091b847" + url: "https://pub.dev" + source: hosted + version: "8.2.6" + freezed: + dependency: "direct dev" + description: + name: freezed + sha256: a434911f643466d78462625df76fd9eb13e57348ff43fe1f77bbe909522c67a1 + url: "https://pub.dev" source: hosted - version: "8.0.3" + version: "2.5.2" + freezed_annotation: + dependency: "direct main" + description: + name: freezed_annotation + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 + url: "https://pub.dev" + source: hosted + version: "2.4.4" frontend_server_client: dependency: transitive description: name: frontend_server_client - url: "https://pub.dartlang.org" + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" source: hosted - version: "2.1.0" - geocoder: + version: "4.0.0" + gap: dependency: "direct main" description: - name: geocoder - url: "https://pub.dartlang.org" + name: gap + sha256: f19387d4e32f849394758b91377f9153a1b41d79513ef7668c088c77dbc6955d + url: "https://pub.dev" source: hosted - version: "0.2.1" + version: "3.0.1" geolocator: dependency: "direct main" description: name: geolocator - url: "https://pub.dartlang.org" + sha256: "149876cc5207a0f5daf4fdd3bfcf0a0f27258b3fe95108fa084f527ad0568f1b" + url: "https://pub.dev" source: hosted - version: "7.0.1" + version: "12.0.0" + geolocator_android: + dependency: transitive + description: + name: geolocator_android + sha256: "93906636752ea4d4e778afa981fdfe7409f545b3147046300df194330044d349" + url: "https://pub.dev" + source: hosted + version: "4.3.1" + geolocator_apple: + dependency: transitive + description: + name: geolocator_apple + sha256: bc2aca02423ad429cb0556121f56e60360a2b7d694c8570301d06ea0c00732fd + url: "https://pub.dev" + source: hosted + version: "2.3.7" geolocator_platform_interface: dependency: transitive description: name: geolocator_platform_interface - url: "https://pub.dartlang.org" + sha256: "386ce3d9cce47838355000070b1d0b13efb5bc430f8ecda7e9238c8409ace012" + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "4.2.4" geolocator_web: dependency: transitive description: name: geolocator_web - url: "https://pub.dartlang.org" + sha256: "7a22f400d831f924a89d931ba126a10e6b8b437f31e6b9311320435f3e1571bd" + url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "4.0.0" + geolocator_windows: + dependency: transitive + description: + name: geolocator_windows + sha256: "53da08937d07c24b0d9952eb57a3b474e29aae2abf9dd717f7e1230995f13f0e" + url: "https://pub.dev" + source: hosted + version: "0.2.3" get_it: dependency: "direct main" description: name: get_it - url: "https://pub.dartlang.org" + sha256: d85128a5dae4ea777324730dc65edd9c9f43155c109d5cc0a69cab74139fbac1 + url: "https://pub.dev" source: hosted - version: "7.1.3" + version: "7.7.0" glob: dependency: transitive description: name: glob - url: "https://pub.dartlang.org" + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.2" + google_identity_services_web: + dependency: transitive + description: + name: google_identity_services_web + sha256: "5be191523702ba8d7a01ca97c17fca096822ccf246b0a9f11923a6ded06199b6" + url: "https://pub.dev" + source: hosted + version: "0.3.1+4" + google_maps: + dependency: transitive + description: + name: google_maps + sha256: "463b38e5a92a05cde41220a11fd5eef3847031fef3e8cf295ac76ec453246907" + url: "https://pub.dev" + source: hosted + version: "8.0.0" google_maps_flutter: dependency: "direct main" description: name: google_maps_flutter - url: "https://pub.dartlang.org" + sha256: "2e302fa3aaf4e2a297f0342d83ebc5e8e9f826e9a716aef473fe7f404ec630a7" + url: "https://pub.dev" + source: hosted + version: "2.9.0" + google_maps_flutter_android: + dependency: transitive + description: + name: google_maps_flutter_android + sha256: a1aeca23cdd499496fb3271fb039a3c660d69e3cb8234df141d1c650844c01e0 + url: "https://pub.dev" + source: hosted + version: "2.14.0" + google_maps_flutter_ios: + dependency: transitive + description: + name: google_maps_flutter_ios + sha256: "3a484846fc56f15e47e3de1f5ea80a7ff2b31721d2faa88f390f3b3cf580c953" + url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.13.0" google_maps_flutter_platform_interface: dependency: transitive description: name: google_maps_flutter_platform_interface - url: "https://pub.dartlang.org" + sha256: "4f6930fd668bf5d40feb2695d5695dbc0c35e5542b557a34ad35be491686d2ba" + url: "https://pub.dev" + source: hosted + version: "2.9.0" + google_maps_flutter_web: + dependency: transitive + description: + name: google_maps_flutter_web + sha256: ff39211bd25d7fad125d19f757eba85bd154460907cd4d135e07e3d0f98a4130 + url: "https://pub.dev" + source: hosted + version: "0.5.10" + google_sign_in: + dependency: "direct main" + description: + name: google_sign_in + sha256: "0b8787cb9c1a68ad398e8010e8c8766bfa33556d2ab97c439fb4137756d7308f" + url: "https://pub.dev" source: hosted - version: "2.0.4" + version: "6.2.1" + google_sign_in_android: + dependency: transitive + description: + name: google_sign_in_android + sha256: "5a47ebec9af97daf0822e800e4f101c3340b5ebc3f6898cf860c1a71b53cf077" + url: "https://pub.dev" + source: hosted + version: "6.1.28" + google_sign_in_ios: + dependency: transitive + description: + name: google_sign_in_ios + sha256: a058c9880be456f21e2e8571c1126eaacd570bdc5b6c6d9d15aea4bdf22ca9fe + url: "https://pub.dev" + source: hosted + version: "5.7.6" + google_sign_in_platform_interface: + dependency: transitive + description: + name: google_sign_in_platform_interface + sha256: "1f6e5787d7a120cc0359ddf315c92309069171306242e181c09472d1b00a2971" + url: "https://pub.dev" + source: hosted + version: "2.4.5" + google_sign_in_web: + dependency: transitive + description: + name: google_sign_in_web + sha256: "042805a21127a85b0dc46bba98a37926f17d2439720e8a459d27045d8ef68055" + url: "https://pub.dev" + source: hosted + version: "0.12.4+2" gql: dependency: transitive description: name: gql - url: "https://pub.dartlang.org" + sha256: "998304fbb88a3956cfea10cd27a56f8e5d4b3bc110f03c952c18a9310774e8bb" + url: "https://pub.dev" source: hosted - version: "0.13.0" + version: "0.14.0" gql_dedupe_link: dependency: transitive description: name: gql_dedupe_link - url: "https://pub.dartlang.org" + sha256: "89681048cf956348e865da872a40081499b8c087fc84dd4d4b9c134bd70d27b3" + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.0.3+1" gql_error_link: dependency: transitive description: name: gql_error_link - url: "https://pub.dartlang.org" + sha256: e7bfdd2b6232f3e15861cd96c2ad6b7c9c94693843b3dea18295136a5fb5b534 + url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.2.3+1" gql_exec: dependency: transitive description: name: gql_exec - url: "https://pub.dartlang.org" + sha256: "0d1fdb2e4154efbfc1dcf3f35ec36d19c8428ff0d560eb4c45b354f8f871dc50" + url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.4.3" gql_http_link: dependency: transitive description: name: gql_http_link - url: "https://pub.dartlang.org" + sha256: "89ef87b32947acf4189f564c095f1148b0ab9bb9996fe518716dbad66708b834" + url: "https://pub.dev" source: hosted - version: "0.4.0" + version: "0.4.5" gql_link: dependency: transitive description: name: gql_link - url: "https://pub.dartlang.org" + sha256: f7973279126bc922d465c4f4da6ed93d187085e597b3480f5e14e74d28fe14bd + url: "https://pub.dev" source: hosted - version: "0.4.0" + version: "0.5.1" gql_transform_link: dependency: transitive description: name: gql_transform_link - url: "https://pub.dartlang.org" + sha256: b1735a9a92d25a92960002a8b40dfaede95ec1e5ed848906125d69efd878661f + url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.2.2+1" graphql: dependency: transitive description: name: graphql - url: "https://pub.dartlang.org" + sha256: b061201579040e9548cec2bae17bbdea0ab30666cb4e7ba48b9675f14d982199 + url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "5.1.3" graphql_flutter: dependency: "direct main" description: name: graphql_flutter - url: "https://pub.dartlang.org" + sha256: "06059ac9e8417c71582f05e28a59b1416d43959d34a6a0d9565341e3a362e117" + url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "5.1.2" graphs: dependency: transitive description: name: graphs - url: "https://pub.dartlang.org" + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.3.2" hive: dependency: "direct main" description: name: hive - url: "https://pub.dartlang.org" + sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" + url: "https://pub.dev" source: hosted - version: "2.0.4" + version: "2.2.3" hive_generator: dependency: "direct dev" description: name: hive_generator - url: "https://pub.dartlang.org" + sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4" + url: "https://pub.dev" source: hosted - version: "1.1.0" - http: + version: "2.0.1" + html: dependency: transitive + description: + name: html + sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" + url: "https://pub.dev" + source: hosted + version: "0.15.4" + http: + dependency: "direct main" description: name: http - url: "https://pub.dartlang.org" + sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" + url: "https://pub.dev" source: hosted - version: "0.13.3" + version: "0.13.6" http_multi_server: dependency: transitive description: name: http_multi_server - url: "https://pub.dartlang.org" + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.2.1" http_parser: dependency: transitive description: name: http_parser - url: "https://pub.dartlang.org" + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.0.2" image: dependency: transitive description: name: image - url: "https://pub.dartlang.org" + sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8" + url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "4.2.0" intl: dependency: "direct main" description: name: intl - url: "https://pub.dartlang.org" + sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + url: "https://pub.dev" source: hosted version: "0.17.0" io: dependency: transitive description: name: io - url: "https://pub.dartlang.org" + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.4" js: dependency: transitive description: name: js - url: "https://pub.dartlang.org" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" source: hosted - version: "0.6.3" + version: "0.6.7" json_annotation: - dependency: transitive + dependency: "direct dev" description: name: json_annotation - url: "https://pub.dartlang.org" + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" source: hosted - version: "4.0.1" + version: "4.9.0" + json_serializable: + dependency: "direct dev" + description: + name: json_serializable + sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b + url: "https://pub.dev" + source: hosted + version: "6.8.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + url: "https://pub.dev" + source: hosted + version: "10.0.4" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" location: dependency: "direct main" description: name: location - url: "https://pub.dartlang.org" + sha256: "6463a242973bf247e3fb1c7722919521b98026978ee3b5177202e103a39c145e" + url: "https://pub.dev" source: hosted - version: "4.3.0" + version: "7.0.0" location_platform_interface: dependency: transitive description: name: location_platform_interface - url: "https://pub.dartlang.org" + sha256: "1e535ccc8b4a9612de4e4319871136b45d2b5d1fb0c2a8bf99687242bf7ca5f7" + url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "5.0.0" location_web: dependency: transitive description: name: location_web - url: "https://pub.dartlang.org" + sha256: "613597b489beb396f658c6f4358dd383c5ed0a1402d95e287642a5f2d8171cb0" + url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "5.0.3" logging: dependency: transitive description: name: logging - url: "https://pub.dartlang.org" + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.2.0" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" source: hosted - version: "0.12.10" + version: "0.12.16+1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + url: "https://pub.dev" + source: hosted + version: "0.8.0" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.12.0" mime: dependency: transitive description: name: mime - url: "https://pub.dartlang.org" + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" + url: "https://pub.dev" source: hosted - version: "1.0.0" - modal_progress_hud: + version: "1.0.5" + mockito: + dependency: "direct main" + description: + name: mockito + sha256: "6841eed20a7befac0ce07df8116c8b8233ed1f4486a7647c7fc5a02ae6163917" + url: "https://pub.dev" + source: hosted + version: "5.4.4" + modal_progress_hud_nsn: dependency: "direct main" description: - name: modal_progress_hud - url: "https://pub.dartlang.org" + name: modal_progress_hud_nsn + sha256: "7d1b2eb50da63c994f8ec2da5738183dbc8235a528e840ecbf67439adb7a6cc2" + url: "https://pub.dev" source: hosted - version: "0.1.3" + version: "0.5.1" nested: dependency: transitive description: name: nested - url: "https://pub.dartlang.org" + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" source: hosted version: "1.0.0" + nm: + dependency: transitive + description: + name: nm + sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" + url: "https://pub.dev" + source: hosted + version: "0.5.0" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" normalize: dependency: transitive description: name: normalize - url: "https://pub.dartlang.org" + sha256: baf8caf2d8b745af5737cca6c24f7fe3cf3158897fdbcde9a909b9c8d3e2e5af + url: "https://pub.dev" source: hosted - version: "0.5.4" + version: "0.7.2" + overlay_support: + dependency: "direct main" + description: + name: overlay_support + sha256: fc39389bfd94e6985e1e13b2a88a125fc4027608485d2d4e2847afe1b2bb339c + url: "https://pub.dev" + source: hosted + version: "2.1.0" package_config: dependency: transitive description: name: package_config - url: "https://pub.dartlang.org" + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.1.0" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.0" path_provider: dependency: "direct main" description: name: path_provider - url: "https://pub.dartlang.org" + sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 + url: "https://pub.dev" source: hosted - version: "2.0.2" - path_provider_linux: + version: "2.1.4" + path_provider_android: dependency: transitive description: - name: path_provider_linux - url: "https://pub.dartlang.org" + name: path_provider_android + sha256: "6f01f8e37ec30b07bc424b4deabac37cacb1bc7e2e515ad74486039918a37eb7" + url: "https://pub.dev" + source: hosted + version: "2.2.10" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 + url: "https://pub.dev" source: hosted - version: "2.0.0" - path_provider_macos: + version: "2.4.0" + path_provider_linux: dependency: transitive description: - name: path_provider_macos - url: "https://pub.dartlang.org" + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.2.1" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - url: "https://pub.dartlang.org" + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.2" path_provider_windows: dependency: transitive description: name: path_provider_windows - url: "https://pub.dartlang.org" + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" source: hosted - version: "2.0.1" - pedantic: - dependency: transitive - description: - name: pedantic - url: "https://pub.dartlang.org" - source: hosted - version: "1.11.0" + version: "2.3.0" petitparser: dependency: transitive description: name: petitparser - url: "https://pub.dartlang.org" + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "6.0.2" + pinput: + dependency: "direct main" + description: + name: pinput + sha256: "7bf9aa7d0eeb3da9f7d49d2087c7bc7d36cd277d2e94cc31c6da52e1ebb048d0" + url: "https://pub.dev" + source: hosted + version: "5.0.0" platform: dependency: transitive description: name: platform - url: "https://pub.dartlang.org" + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" + url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "3.1.5" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - url: "https://pub.dartlang.org" + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.1.8" pool: dependency: transitive description: name: pool - url: "https://pub.dartlang.org" + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" source: hosted - version: "1.5.0" - process: - dependency: transitive - description: - name: process - url: "https://pub.dartlang.org" - source: hosted - version: "4.2.1" + version: "1.5.1" provider: dependency: "direct main" description: name: provider - url: "https://pub.dartlang.org" + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "6.1.2" pub_semver: dependency: transitive description: name: pub_semver - url: "https://pub.dartlang.org" + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.1.4" pubspec_parse: dependency: transitive description: name: pubspec_parse - url: "https://pub.dartlang.org" + sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 + url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.3.0" + responsive_sizer: + dependency: "direct main" + description: + name: responsive_sizer + sha256: "6aefee6065c0883681bc7559147a24d8fa1846d296a216abcda25ff349340712" + url: "https://pub.dev" + source: hosted + version: "3.3.1" rxdart: dependency: "direct main" description: name: rxdart - url: "https://pub.dartlang.org" + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + url: "https://pub.dev" source: hosted - version: "0.26.0" - share: + version: "0.27.7" + sanitize_html: + dependency: transitive + description: + name: sanitize_html + sha256: "12669c4a913688a26555323fb9cec373d8f9fbe091f2d01c40c723b33caa8989" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + share_plus: dependency: "direct main" description: - name: share - url: "https://pub.dartlang.org" + name: share_plus + sha256: ef3489a969683c4f3d0239010cc8b7a2a46543a8d139e111c06c558875083544 + url: "https://pub.dev" + source: hosted + version: "9.0.0" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: "0f9e4418835d1b2c3ae78fdb918251959106cefdbc4dd43526e182f80e82f6d4" + url: "https://pub.dev" source: hosted - version: "2.0.4" + version: "4.0.0" shared_preferences: dependency: "direct main" description: name: shared_preferences - url: "https://pub.dartlang.org" + sha256: c272f9cabca5a81adc9b0894381e9c1def363e980f960fa903c604c471b22f68 + url: "https://pub.dev" source: hosted - version: "2.0.6" - shared_preferences_linux: + version: "2.3.1" + shared_preferences_android: dependency: transitive description: - name: shared_preferences_linux - url: "https://pub.dartlang.org" + name: shared_preferences_android + sha256: a7e8467e9181cef109f601e3f65765685786c1a738a83d7fbbde377589c0d974 + url: "https://pub.dev" + source: hosted + version: "2.3.1" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: c4b35f6cb8f63c147312c054ce7c2254c8066745125264f0c88739c417fc9d9f + url: "https://pub.dev" source: hosted - version: "2.0.0" - shared_preferences_macos: + version: "2.5.2" + shared_preferences_linux: dependency: transitive description: - name: shared_preferences_macos - url: "https://pub.dartlang.org" + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.4.1" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - url: "https://pub.dartlang.org" + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.4.1" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - url: "https://pub.dartlang.org" + sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.4.2" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - url: "https://pub.dartlang.org" + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.4.1" shelf: dependency: transitive description: name: shelf - url: "https://pub.dartlang.org" + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" source: hosted - version: "1.1.4" + version: "1.4.1" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e + url: "https://pub.dev" + source: hosted + version: "1.1.2" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - url: "https://pub.dartlang.org" + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.0.4" + simple_pip_mode: + dependency: "direct main" + description: + name: simple_pip_mode + sha256: "89f8137fa5a8d113f39c61007d4b658048a9359362447b8cfb8bce93631882ad" + url: "https://pub.dev" + source: hosted + version: "0.8.0" skeleton_text: dependency: "direct main" description: name: skeleton_text - url: "https://pub.dartlang.org" + sha256: bacd536bf0664efe1cae53bcbd78c3d4040a120f300f69dc85d83f358471cc6c + url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "3.0.1" sky_engine: dependency: transitive description: flutter @@ -783,163 +1257,314 @@ packages: dependency: "direct main" description: name: sliding_up_panel - url: "https://pub.dartlang.org" + sha256: "578e90956a6212d1e406373250b2436a0f3afece29aee3c24c8360094d6cf968" + url: "https://pub.dev" source: hosted version: "2.0.0+1" source_gen: dependency: transitive description: name: source_gen - url: "https://pub.dartlang.org" + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.5.0" source_helper: dependency: transitive description: name: source_helper - url: "https://pub.dartlang.org" + sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd" + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.3.4" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" + url: "https://pub.dev" + source: hosted + version: "0.10.12" source_span: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" source: hosted - version: "1.8.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.2" stream_transform: dependency: transitive description: name: stream_transform - url: "https://pub.dartlang.org" + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.1" + test: + dependency: "direct dev" + description: + name: test + sha256: "7ee446762c2c50b3bd4ea96fe13ffac69919352bd3b4b17bac3f3465edc58073" + url: "https://pub.dev" + source: hosted + version: "1.25.2" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.7.0" + test_core: + dependency: transitive + description: + name: test_core + sha256: "2bc4b4ecddd75309300d8096f781c0e3280ca1ef85beda558d33fcbedc2eead4" + url: "https://pub.dev" + source: hosted + version: "0.6.0" + timezone: + dependency: transitive + description: + name: timezone + sha256: "2236ec079a174ce07434e89fcd3fcda430025eb7692244139a9cf54fdcf1fc7d" + url: "https://pub.dev" + source: hosted + version: "0.9.4" timing: dependency: transitive description: name: timing - url: "https://pub.dartlang.org" + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.1" typed_data: dependency: transitive description: name: typed_data - url: "https://pub.dartlang.org" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.3.2" uni_links: dependency: "direct main" description: name: uni_links - url: "https://pub.dartlang.org" + sha256: "051098acfc9e26a9fde03b487bef5d3d228ca8f67693480c6f33fd4fbb8e2b6e" + url: "https://pub.dev" source: hosted version: "0.5.1" uni_links_platform_interface: dependency: transitive description: name: uni_links_platform_interface - url: "https://pub.dartlang.org" + sha256: "929cf1a71b59e3b7c2d8a2605a9cf7e0b125b13bc858e55083d88c62722d4507" + url: "https://pub.dev" source: hosted version: "1.0.0" uni_links_web: dependency: transitive description: name: uni_links_web - url: "https://pub.dartlang.org" + sha256: "7539db908e25f67de2438e33cc1020b30ab94e66720b5677ba6763b25f6394df" + url: "https://pub.dev" source: hosted version: "0.1.0" + universal_platform: + dependency: transitive + description: + name: universal_platform + sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: e2b9622b4007f97f504cd64c0128309dfb978ae66adbe944125ed9e1750f06af + url: "https://pub.dev" + source: hosted + version: "3.2.0" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" + url: "https://pub.dev" + source: hosted + version: "2.3.3" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "49c10f879746271804767cb45551ec5592cdab00ee105c06dddde1a98f73b185" + url: "https://pub.dev" + source: hosted + version: "3.1.2" uuid: dependency: transitive description: name: uuid - url: "https://pub.dartlang.org" + sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" + url: "https://pub.dev" source: hosted - version: "3.0.4" + version: "3.0.7" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.dartlang.org" + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.4" + vibration: + dependency: "direct main" + description: + name: vibration + sha256: "06588a845a4ebc73ab7ff7da555c2b3dbcd9676164b5856a38bf0b2287f1045d" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + vibration_platform_interface: + dependency: transitive + description: + name: vibration_platform_interface + sha256: "735a5fef0f284de0ad9449a5ed7d36ba017c6f59b5b20ac64418af4a6bd35ee7" + url: "https://pub.dev" + source: hosted + version: "0.0.1" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + url: "https://pub.dev" + source: hosted + version: "14.2.1" watcher: dependency: transitive description: name: watcher - url: "https://pub.dartlang.org" + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + url: "https://pub.dev" + source: hosted + version: "0.5.1" web_socket_channel: dependency: transitive description: name: web_socket_channel - url: "https://pub.dartlang.org" + sha256: "3a969ddcc204a3e34e863d204b29c0752716f78b6f9cc8235083208d268a4ccd" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.2.0" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" win32: dependency: transitive description: name: win32 - url: "https://pub.dartlang.org" + sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a" + url: "https://pub.dev" + source: hosted + version: "5.5.4" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "723b7f851e5724c55409bb3d5a32b203b3afe8587eaf5dafb93a5fed8ecda0d6" + url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "1.1.4" xdg_directories: dependency: transitive description: name: xdg_directories - url: "https://pub.dartlang.org" + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "1.0.4" xml: dependency: transitive description: name: xml - url: "https://pub.dartlang.org" + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + url: "https://pub.dev" source: hosted - version: "5.1.2" + version: "6.5.0" yaml: dependency: transitive description: name: yaml - url: "https://pub.dartlang.org" + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.2" sdks: - dart: ">=2.13.0 <3.0.0" - flutter: ">=1.22.0" + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index 45fdab00..6f7c441b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,46 +1,81 @@ name: beacon description: A new Flutter project. +publish_to: "none" + version: 1.0.0+1 environment: - sdk: '>=2.7.0 <3.0.0' + sdk: '>=3.3.0 <4.0.0' dependencies: + connectivity_plus: any + cupertino_icons: ^1.0.5 + date_time_picker: ^2.1.0 + duration_picker: ^1.1.1 flutter: sdk: flutter - - cupertino_icons: ^1.0.2 - fluttertoast: ^8.0.3 - connectivity_plus: ^1.0.2 - geolocator: ^7.0.1 - google_maps_flutter: ^2.0.6 - intl: ^0.17.0 - date_time_picker: ^2.0.0 - modal_progress_hud: ^0.1.3 - hive: ^2.0.4 - path_provider: ^2.0.2 - shared_preferences: ^2.0.6 - graphql_flutter: ^5.0.0 - get_it: ^7.1.3 - provider: ^5.0.0 - uni_links: ^0.5.1 - geocoder: ^0.2.1 - share: ^2.0.4 - location: ^4.3.0 - rxdart: ^0.26.0 + flutter_animarker: ^3.2.0 + flutter_config: ^2.0.2 + flutter_countdown_timer: ^4.1.0 + flutter_local_notifications: ^17.2.1+1 flutter_polyline_points: ^1.0.0 + flutter_spinkit: ^5.2.0 + fluttertoast: ^8.2.4 + get_it: ^7.6.4 + google_maps_flutter: ^2.5.3 + hive: ^2.2.3 + intl: ^0.17.0 + location: ^7.0.0 + mockito: + modal_progress_hud_nsn: ^0.5.1 + overlay_support: ^2.1.0 + path_provider: ^2.0.4 + provider: ^6.1.1 + rxdart: ^0.27.7 + share_plus: ^9.0.0 + shared_preferences: ^2.0.7 + responsive_sizer: ^3.3.1 + skeleton_text: ^3.0.1 sliding_up_panel: ^2.0.0+1 - skeleton_text: ^1.0.0 - flutter_duration_picker: - git: https://github.com/bartektartanus/flutter_duration_picker + uni_links: ^0.5.1 + simple_pip_mode: ^0.8.0 + data_connection_checker_nulls: ^0.0.2 + flutter_geocoder_alternative: any + gap: ^3.0.1 + + # routing + auto_route: ^8.1.1 + + # state management + bloc: ^8.1.4 + flutter_bloc: ^8.1.5 + + # class comparison + freezed_annotation: ^2.4.1 + + # for dismissable widget + flutter_slidable: ^3.1.0 + graphql_flutter: ^5.1.2 + geolocator: ^12.0.0 + http: ^0.13.6 + pinput: ^5.0.0 + vibration: ^1.9.0 + google_sign_in: ^6.2.1 + + + dev_dependencies: + build_runner: ^2.1.2 + flutter_launcher_icons: ^0.13.1 flutter_test: sdk: flutter - hive_generator: ^1.1.0 - build_runner: ^2.0.4 - flutter_launcher_icons: "^0.9.1" + freezed: ^2.5.2 + test: ^1.16.5 + hive_generator: + json_annotation: ^4.9.0 + json_serializable: ^6.8.0 flutter_icons: android: "launcher_icon" @@ -56,4 +91,4 @@ flutter: fonts: - family: FuturaBold fonts: - - asset: fonts/futura_bold.otf \ No newline at end of file + - asset: fonts/futura_bold.otf diff --git a/screenshots/1.jpg b/screenshots/1.jpg new file mode 100755 index 00000000..6088fb69 Binary files /dev/null and b/screenshots/1.jpg differ diff --git a/screenshots/10.png b/screenshots/10.png new file mode 100644 index 00000000..7c1d1007 Binary files /dev/null and b/screenshots/10.png differ diff --git a/screenshots/2.jpg b/screenshots/2.jpg new file mode 100755 index 00000000..464888a9 Binary files /dev/null and b/screenshots/2.jpg differ diff --git a/screenshots/3.jpg b/screenshots/3.jpg new file mode 100755 index 00000000..5f46c082 Binary files /dev/null and b/screenshots/3.jpg differ diff --git a/screenshots/4.jpg b/screenshots/4.jpg new file mode 100755 index 00000000..29e80edf Binary files /dev/null and b/screenshots/4.jpg differ diff --git a/screenshots/5.png b/screenshots/5.png new file mode 100644 index 00000000..fe76ff01 Binary files /dev/null and b/screenshots/5.png differ diff --git a/screenshots/6.png b/screenshots/6.png new file mode 100644 index 00000000..4049a860 Binary files /dev/null and b/screenshots/6.png differ diff --git a/screenshots/7.png b/screenshots/7.png new file mode 100644 index 00000000..e8967166 Binary files /dev/null and b/screenshots/7.png differ diff --git a/screenshots/8.png b/screenshots/8.png new file mode 100644 index 00000000..dc382892 Binary files /dev/null and b/screenshots/8.png differ diff --git a/screenshots/9.png b/screenshots/9.png new file mode 100644 index 00000000..b1bce712 Binary files /dev/null and b/screenshots/9.png differ diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png new file mode 100644 index 00000000..eb9b4d76 Binary files /dev/null and b/web/icons/Icon-maskable-192.png differ diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png new file mode 100644 index 00000000..d69c5669 Binary files /dev/null and b/web/icons/Icon-maskable-512.png differ diff --git a/windows/.gitignore b/windows/.gitignore new file mode 100644 index 00000000..d492d0d9 --- /dev/null +++ b/windows/.gitignore @@ -0,0 +1,17 @@ +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/windows/CMakeLists.txt b/windows/CMakeLists.txt new file mode 100644 index 00000000..3076b456 --- /dev/null +++ b/windows/CMakeLists.txt @@ -0,0 +1,108 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(beacon LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "beacon") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +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() +# Define settings for the Profile build mode. +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. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +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() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +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() + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# 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/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt new file mode 100644 index 00000000..903f4899 --- /dev/null +++ b/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +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") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === 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" + ${FLUTTER_TARGET_PLATFORM} $ + 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/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000..1032d3a8 --- /dev/null +++ b/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,23 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include +#include +#include +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + ConnectivityPlusWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); + GeolocatorWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("GeolocatorWindows")); + SharePlusWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); +} diff --git a/windows/flutter/generated_plugin_registrant.h b/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000..dc139d85 --- /dev/null +++ b/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake new file mode 100644 index 00000000..684998e8 --- /dev/null +++ b/windows/flutter/generated_plugins.cmake @@ -0,0 +1,27 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + connectivity_plus + geolocator_windows + share_plus + 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/windows/runner/CMakeLists.txt b/windows/runner/CMakeLists.txt new file mode 100644 index 00000000..394917c0 --- /dev/null +++ b/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +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 the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc new file mode 100644 index 00000000..fecaa677 --- /dev/null +++ b/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// 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 +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#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", "beacon" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "beacon" "\0" + VALUE "LegalCopyright", "Copyright (C) 2024 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "beacon.exe" "\0" + VALUE "ProductName", "beacon" "\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/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp new file mode 100644 index 00000000..955ee303 --- /dev/null +++ b/windows/runner/flutter_window.cpp @@ -0,0 +1,71 @@ +#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()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + + 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/windows/runner/flutter_window.h b/windows/runner/flutter_window.h new file mode 100644 index 00000000..6da0652f --- /dev/null +++ b/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#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/windows/runner/main.cpp b/windows/runner/main.cpp new file mode 100644 index 00000000..30fc358e --- /dev/null +++ b/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#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.Create(L"beacon", 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/windows/runner/resource.h b/windows/runner/resource.h new file mode 100644 index 00000000..66a65d1e --- /dev/null +++ b/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{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/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico new file mode 100644 index 00000000..c04e20ca Binary files /dev/null and b/windows/runner/resources/app_icon.ico differ diff --git a/windows/runner/runner.exe.manifest b/windows/runner/runner.exe.manifest new file mode 100644 index 00000000..a42ea768 --- /dev/null +++ b/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/windows/runner/utils.cpp b/windows/runner/utils.cpp new file mode 100644 index 00000000..b2b08734 --- /dev/null +++ b/windows/runner/utils.cpp @@ -0,0 +1,65 @@ +#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) + -1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); + std::string utf8_string; + if (target_length <= 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/windows/runner/utils.h b/windows/runner/utils.h new file mode 100644 index 00000000..3879d547 --- /dev/null +++ b/windows/runner/utils.h @@ -0,0 +1,19 @@ +#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/windows/runner/win32_window.cpp b/windows/runner/win32_window.cpp new file mode 100644 index 00000000..60608d0f --- /dev/null +++ b/windows/runner/win32_window.cpp @@ -0,0 +1,288 @@ +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + +// 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 registrar 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::Create(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, + 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; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + +// 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; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + 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. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/windows/runner/win32_window.h b/windows/runner/win32_window.h new file mode 100644 index 00000000..e901dde6 --- /dev/null +++ b/windows/runner/win32_window.h @@ -0,0 +1,102 @@ +#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 a win32 window with |title| that is positioned and sized 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 this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // 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 + // responds 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; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + 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_