28 Commits

Author SHA1 Message Date
3a74b8017d Scooter order payment screen fix 2026-09-15 21:28:33 +03:00
153a763b6f Registration pipeline chenged - fill profile step added. Order payment fixed 2026-09-14 19:20:31 +03:00
7460408784 Finish screen - attention message added 2026-09-14 08:42:25 +03:00
5f57af2ad2 bug fixes 2026-09-04 13:41:57 +03:00
cf5b56e365 Tariff subsriptionId fix 2026-08-31 10:37:08 +03:00
b0c9f6dc17 fix qr 2026-08-31 09:57:40 +03:00
ece8e7ba79 bug fixes 2026-08-31 09:53:29 +03:00
02b3e5caa8 Tariff description fix, iOS payment card binding fix 2026-08-31 09:18:33 +03:00
a556dc5736 bug fixes 2026-08-29 00:00:09 +03:00
6513a004d9 fix profile 2026-08-18 15:09:53 +03:00
80a95ece5b fix to prod 2026-08-14 13:47:11 +03:00
f53fea576e add page company detail 2026-08-13 09:34:06 +03:00
182be1049b some fix
# Conflicts:
#	lib/presentation/navigation/app_router.dart
#	pubspec.yaml
2026-08-11 00:23:16 +03:00
c9b8d143d1 fixing the app launch bug 2026-07-27 11:06:04 +03:00
d5f70ee9c4 change package name 2026-07-19 20:27:26 +03:00
99d2875b56 Merge pull request 'map fix' (#8) from fix/ios-fix into main
Reviewed-on: #8
2026-07-16 11:16:14 +00:00
46cd22c287 fix merge conflict 2026-07-16 14:14:22 +03:00
0a6c40181a Merge pull request 'add test block' (#7) from fix/bug-fix into main
Reviewed-on: #7
2026-07-16 07:26:56 +00:00
fcb23e0d5f fix merge conflict 2026-07-16 10:15:10 +03:00
7a2c214ff5 map fix 2026-07-15 22:58:33 +03:00
11c00ad2cc add test block 2026-07-15 22:46:42 +03:00
419b54d5dd Merge pull request 'fix payment card change. fix adding a new payment card' (#6) from fix/bug-fix into main
Reviewed-on: #6
2026-07-13 12:47:55 +00:00
a6e8b9d070 fix payment card change. fix adding a new payment card 2026-07-13 15:46:34 +03:00
4a919a5bcc Merge pull request 'fix add new card in bottomsheet' (#5) from fix/bug-fix into main
Reviewed-on: #5
2026-07-13 08:50:59 +00:00
e91f373258 fix add new card in bottomsheet 2026-07-13 11:49:00 +03:00
902c6d27a4 Merge pull request 'fix/bug-fix' (#4) from fix/bug-fix into main
Reviewed-on: #4
2026-07-12 22:53:19 +00:00
98c5a7a1d1 fix 2026-07-13 01:38:13 +03:00
9eda119527 fix 2026-07-08 13:09:20 +03:00
372 changed files with 26925 additions and 22785 deletions

View File

@@ -15,7 +15,7 @@ plugins {
}
android {
namespace = "com.sparkit.be_happy"
namespace = "com.behappybel.behappy"
compileSdk = flutter.compileSdkVersion
ndkVersion = "27.0.12077973"
@@ -44,7 +44,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.sparkit.behappy"
applicationId = "com.behappybel.behappy"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = 26

View File

@@ -8,6 +8,7 @@
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
</intent>
<intent>
@@ -25,7 +26,7 @@
<meta-data
android:name="com.yandex.runtime.API_KEY"
android:value="a0ef1404-2650-4f28-9891-c965ecc09174" />
android:value="45fe500d-b549-4444-9e4b-3b2acd78ee2c" />
<activity
android:name=".MainActivity"
@@ -49,6 +50,14 @@
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter android:label="card_binding_deep_link">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="com.behappy.app" android:host="card-result" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->

View File

@@ -1,4 +1,4 @@
package com.sparkit.be_happy
package com.behappybel.behappy
import android.os.Bundle
import com.yandex.mapkit.MapKitFactory
@@ -6,7 +6,7 @@ import io.flutter.embedding.android.FlutterActivity
class MainActivity : FlutterActivity() {
override protected fun onCreate(savedInstanceState: Bundle?) {
MapKitFactory.setApiKey("a0ef1404-2650-4f28-9891-c965ecc09174")
MapKitFactory.setApiKey("45fe500d-b549-4444-9e4b-3b2acd78ee2c")
super.onCreate(savedInstanceState)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 B

View File

@@ -1,12 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
<item>
<bitmap android:gravity="fill" android:src="@drawable/background"/>
</item>
<item>
<bitmap android:gravity="center" android:src="@drawable/splash"/>
</item>
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 B

View File

@@ -1,12 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
<item>
<bitmap android:gravity="fill" android:src="@drawable/background"/>
</item>
<item>
<bitmap android:gravity="center" android:src="@drawable/splash"/>
</item>
</layer-list>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@@ -5,6 +5,10 @@
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@@ -5,6 +5,10 @@
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your

View File

@@ -2,3 +2,8 @@ org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m
android.useAndroidX=true
android.enableJetifier=true
yandexMapkit.variant=full
# This builtInKotlin flag was added automatically by Flutter migrator
android.builtInKotlin=false
kotlin.incremental=false
# This newDsl flag was added automatically by Flutter migrator
android.newDsl=false

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

BIN
assets/icons/failed.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
assets/icons/fine.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
assets/icons/success.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

BIN
assets/payment_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
assets/rules_alpha.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
assets/rules_apple_pay.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
assets/rules_belcard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
assets/rules_mastercard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

BIN
assets/rules_visa.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
assets/tech_works.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

3
devtools_options.yaml Normal file
View File

@@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:

View File

@@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>MinimumOSVersion</key>
<string>13.0</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>

View File

@@ -1,6 +1,6 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
#include? "Secrets.xcconfig"
YANDEX_MAPKIT_API_KEY=a0ef1404-2650-4f28-9891-c965ecc09174
YANDEX_MAPKIT_API_KEY=45fe500d-b549-4444-9e4b-3b2acd78ee2c
// Переопределение Pods/ML Kit: arm64 для симулятора на Apple Silicon (M1/M4)
EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386

View File

@@ -1,5 +1,5 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"
#include "Generated.xcconfig"
#include? "Secrets.xcconfig"
YANDEX_MAPKIT_API_KEY=a0ef1404-2650-4f28-9891-c965ecc09174
YANDEX_MAPKIT_API_KEY=45fe500d-b549-4444-9e4b-3b2acd78ee2c
EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386

View File

@@ -1,5 +1,5 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
#include? "Secrets.xcconfig"
YANDEX_MAPKIT_API_KEY=a0ef1404-2650-4f28-9891-c965ecc09174
YANDEX_MAPKIT_API_KEY=45fe500d-b549-4444-9e4b-3b2acd78ee2c
EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386

View File

@@ -1,8 +1,9 @@
# image_picker_ios и др. требуют минимум iOS 13
platform :ios, '13.0'
platform :ios, '15.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
ENV['YANDEX_MAPKIT_VARIANT'] = 'full'
project 'Runner', {
'Debug' => :debug,
@@ -43,6 +44,7 @@ post_install do |installer|
# ML Kit исключает arm64 для симулятора → сборка только x86_64.
# На M1 это ещё могло работать через Rosetta; на M4 симулятор только arm64.
config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'i386'
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
end
end
end

View File

@@ -2,6 +2,8 @@ PODS:
- device_info_plus (0.0.1):
- Flutter
- Flutter (1.0.0)
- flutter_native_splash (2.4.3):
- Flutter
- flutter_secure_storage (6.0.0):
- Flutter
- geolocator_apple (1.2.0):
@@ -12,9 +14,6 @@ PODS:
- mobile_scanner (7.0.0):
- Flutter
- FlutterMacOS
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
@@ -22,17 +21,17 @@ PODS:
- Flutter
- yandex_mapkit (0.0.1):
- Flutter
- YandexMapsMobile (= 4.22.0-lite)
- YandexMapsMobile (4.22.0-lite)
- YandexMapsMobile (= 4.22.0-full)
- YandexMapsMobile (4.22.0-full)
DEPENDENCIES:
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- Flutter (from `Flutter`)
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/darwin`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- mobile_scanner (from `.symlinks/plugins/mobile_scanner/darwin`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- yandex_mapkit (from `.symlinks/plugins/yandex_mapkit/ios`)
@@ -46,6 +45,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/device_info_plus/ios"
Flutter:
:path: Flutter
flutter_native_splash:
:path: ".symlinks/plugins/flutter_native_splash/ios"
flutter_secure_storage:
:path: ".symlinks/plugins/flutter_secure_storage/ios"
geolocator_apple:
@@ -54,8 +55,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/image_picker_ios/ios"
mobile_scanner:
:path: ".symlinks/plugins/mobile_scanner/darwin"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
url_launcher_ios:
@@ -66,16 +65,16 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e
image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93
path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
yandex_mapkit: bac34ca1bdf97e87252a8c6d09a95b1fe39ba103
YandexMapsMobile: c73844c6096bbb240d1491adc511c2ef4a88d88d
yandex_mapkit: b0468cf6fdc2e5ef315e08c081cb48763db1edee
YandexMapsMobile: 27e6e45425bb61f0be2dbc2bf02d4e64926691a9
PODFILE CHECKSUM: 715b1068ab8815ca4115a24e305e836709652b69
PODFILE CHECKSUM: 4db91eb18e4c468b9b75ce0a1fbe33194b57f06a
COCOAPODS: 1.16.2

View File

@@ -10,12 +10,12 @@
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
6D33173F8CCB65895D564988 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BB22D9E6003D7254B27B141 /* Pods_RunnerTests.framework */; };
719658A7EA74BCF79744189C /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 815E41E9A67B82F3B5893FB0 /* Pods_Runner.framework */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
947230899A63DCC5FC354559 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5825CAAC7BEAFA8756F6D22 /* Pods_RunnerTests.framework */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
FB3C1019114E649A3758FC7D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 151F40E0A8A0B88D7F9715D8 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -42,30 +42,30 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
0EB91AA8815BC4C23E9C769C /* 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 = "<group>"; };
12F1F4F8ABC8CE708CB9918F /* 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 = "<group>"; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
151F40E0A8A0B88D7F9715D8 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
4BB22D9E6003D7254B27B141 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4D0302F803C0949E93DDCE44 /* 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 = "<group>"; };
525F1415727A8E2A6F5CA828 /* 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 = "<group>"; };
4AADFAE03DF1A02E1E799949 /* 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 = "<group>"; };
53B78B2283B7ADCE0208F756 /* 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 = "<group>"; };
673086666704B99027FC010B /* 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 = "<group>"; };
6EC6FEDB5882EB1ECF391D95 /* 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 = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB41CF90195004384FC /* Profile.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Profile.xcconfig; path = Flutter/Profile.xcconfig; sourceTree = "<group>"; };
7AFC7F9B8515A109C72DDF1B /* 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 = "<group>"; };
815E41E9A67B82F3B5893FB0 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
9740EEB41CF90195004384FC /* Profile.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Profile.xcconfig; path = Flutter/Profile.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
BAF1C86965EF66AE2F0839BD /* 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 = "<group>"; };
BE9A8C77364E79EA4C6EE36B /* 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 = "<group>"; };
D24CB1920C4748F67C854B22 /* 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 = "<group>"; };
A5825CAAC7BEAFA8756F6D22 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -73,7 +73,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
6D33173F8CCB65895D564988 /* Pods_RunnerTests.framework in Frameworks */,
947230899A63DCC5FC354559 /* Pods_RunnerTests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -81,7 +81,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
FB3C1019114E649A3758FC7D /* Pods_Runner.framework in Frameworks */,
719658A7EA74BCF79744189C /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -96,6 +96,15 @@
path = RunnerTests;
sourceTree = "<group>";
};
4E47A3B8F8440AE11EF826D6 /* Frameworks */ = {
isa = PBXGroup;
children = (
815E41E9A67B82F3B5893FB0 /* Pods_Runner.framework */,
A5825CAAC7BEAFA8756F6D22 /* Pods_RunnerTests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
@@ -115,8 +124,8 @@
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
991E98147265C7C44D711731 /* Pods */,
E94B50B8FA90842D98EF918A /* Frameworks */,
BBAC3160211F77BA6C1CDBEB /* Pods */,
4E47A3B8F8440AE11EF826D6 /* Frameworks */,
);
sourceTree = "<group>";
};
@@ -144,29 +153,19 @@
path = Runner;
sourceTree = "<group>";
};
991E98147265C7C44D711731 /* Pods */ = {
BBAC3160211F77BA6C1CDBEB /* Pods */ = {
isa = PBXGroup;
children = (
4D0302F803C0949E93DDCE44 /* Pods-Runner.debug.xcconfig */,
0EB91AA8815BC4C23E9C769C /* Pods-Runner.release.xcconfig */,
525F1415727A8E2A6F5CA828 /* Pods-Runner.profile.xcconfig */,
BAF1C86965EF66AE2F0839BD /* Pods-RunnerTests.debug.xcconfig */,
D24CB1920C4748F67C854B22 /* Pods-RunnerTests.release.xcconfig */,
BE9A8C77364E79EA4C6EE36B /* Pods-RunnerTests.profile.xcconfig */,
4AADFAE03DF1A02E1E799949 /* Pods-Runner.debug.xcconfig */,
673086666704B99027FC010B /* Pods-Runner.release.xcconfig */,
53B78B2283B7ADCE0208F756 /* Pods-Runner.profile.xcconfig */,
7AFC7F9B8515A109C72DDF1B /* Pods-RunnerTests.debug.xcconfig */,
12F1F4F8ABC8CE708CB9918F /* Pods-RunnerTests.release.xcconfig */,
6EC6FEDB5882EB1ECF391D95 /* Pods-RunnerTests.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
E94B50B8FA90842D98EF918A /* Frameworks */ = {
isa = PBXGroup;
children = (
151F40E0A8A0B88D7F9715D8 /* Pods_Runner.framework */,
4BB22D9E6003D7254B27B141 /* Pods_RunnerTests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -174,7 +173,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
1D742F73AAFC432DF3836D7E /* [CP] Check Pods Manifest.lock */,
8320BCAEA155F95875F06656 /* [CP] Check Pods Manifest.lock */,
331C807D294A63A400263BE5 /* Sources */,
331C807F294A63A400263BE5 /* Resources */,
83F4E53737A5B4793588BFEB /* Frameworks */,
@@ -193,15 +192,15 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
8734FCBECF65F25CB259DF82 /* [CP] Check Pods Manifest.lock */,
C59C8F9337F753AC74343087 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
25F6FF3856E913FFAF963081 /* [CP] Embed Pods Frameworks */,
FF12327738E91E5AA87207B8 /* [CP] Copy Pods Resources */,
FB90C79E7B88832806AD3371 /* [CP] Embed Pods Frameworks */,
81264E30AB9A7C4C10AB44BA /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -273,7 +272,40 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
1D742F73AAFC432DF3836D7E /* [CP] Check Pods Manifest.lock */ = {
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
81264E30AB9A7C4C10AB44BA /* [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;
};
8320BCAEA155F95875F06656 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -295,40 +327,22 @@
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;
};
25F6FF3856E913FFAF963081 /* [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;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
8734FCBECF65F25CB259DF82 /* [CP] Check Pods Manifest.lock */ = {
C59C8F9337F753AC74343087 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -350,36 +364,21 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
FF12327738E91E5AA87207B8 /* [CP] Copy Pods Resources */ = {
FB90C79E7B88832806AD3371 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
"${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-resources.sh\"\n";
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
@@ -479,7 +478,7 @@
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
TARGETED_DEVICE_FAMILY = "1";
VALIDATE_PRODUCT = YES;
};
name = Profile;
@@ -490,15 +489,19 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = J463CU8H5F;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.sparkit.beHappy;
PRODUCT_BUNDLE_IDENTIFIER = com.behappybel.behappy;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
@@ -507,14 +510,14 @@
};
331C8088294A63A400263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = BAF1C86965EF66AE2F0839BD /* Pods-RunnerTests.debug.xcconfig */;
baseConfigurationReference = 7AFC7F9B8515A109C72DDF1B /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.sparkit.beHappy.RunnerTests;
PRODUCT_BUNDLE_IDENTIFIER = com.behappybel.behappy.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -525,14 +528,14 @@
};
331C8089294A63A400263BE5 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D24CB1920C4748F67C854B22 /* Pods-RunnerTests.release.xcconfig */;
baseConfigurationReference = 12F1F4F8ABC8CE708CB9918F /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.sparkit.beHappy.RunnerTests;
PRODUCT_BUNDLE_IDENTIFIER = com.behappybel.behappy.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@@ -541,14 +544,14 @@
};
331C808A294A63A400263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = BE9A8C77364E79EA4C6EE36B /* Pods-RunnerTests.profile.xcconfig */;
baseConfigurationReference = 6EC6FEDB5882EB1ECF391D95 /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.sparkit.beHappy.RunnerTests;
PRODUCT_BUNDLE_IDENTIFIER = com.behappybel.behappy.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@@ -608,7 +611,7 @@
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
TARGETED_DEVICE_FAMILY = "1";
};
name = Debug;
};
@@ -661,7 +664,7 @@
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
TARGETED_DEVICE_FAMILY = "1";
VALIDATE_PRODUCT = YES;
};
name = Release;
@@ -672,15 +675,19 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = J463CU8H5F;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.sparkit.beHappy;
PRODUCT_BUNDLE_IDENTIFIER = com.behappybel.behappy;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@@ -694,15 +701,19 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = J463CU8H5F;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.sparkit.beHappy;
PRODUCT_BUNDLE_IDENTIFIER = com.behappybel.behappy;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 351 KiB

After

Width:  |  Height:  |  Size: 322 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 986 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "background.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 B

View File

@@ -1,23 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 B

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 B

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 B

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -16,13 +16,19 @@
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="LaunchBackground" translatesAutoresizingMaskIntoConstraints="NO" id="tWc-Dq-wcI"/>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"></imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="3T2-ad-Qdv"/>
<constraint firstItem="tWc-Dq-wcI" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="RPx-PI-7Xg"/>
<constraint firstItem="tWc-Dq-wcI" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="SdS-ul-q2q"/>
<constraint firstAttribute="trailing" secondItem="tWc-Dq-wcI" secondAttribute="trailing" id="Swv-Gf-Rwn"/>
<constraint firstAttribute="trailing" secondItem="YRO-k0-Ey4" secondAttribute="trailing" id="TQA-XW-tRk"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="duK-uY-Gun"/>
<constraint firstItem="tWc-Dq-wcI" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="kV7-tw-vXt"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="xPn-NY-SIU"/>
</constraints>
</view>
</viewController>
@@ -32,6 +38,7 @@
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
<image name="LaunchImage" width="130" height="130"/>
<image name="LaunchBackground" width="1" height="1"/>
</resources>
</document>

View File

@@ -1,72 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Be Happy</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>be_happy</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Be Happy</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>be_happy</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneConfigurationName</key>
<string>flutter</string>
<key>UISceneDelegateClassName</key>
<string>FlutterSceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneConfigurationName</key>
<string>flutter</string>
<key>UISceneDelegateClassName</key>
<string>FlutterSceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>YandexMapKitApiKey</key>
<string>$(YANDEX_MAPKIT_API_KEY)</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Данные о местоположении необходимы для поиска ближайших самокатов и отображения их на карте.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Данные о местоположении необходимы для отслеживания поездки на самокате в фоновом режиме.</string>
<key>NSCameraUsageDescription</key>
<string>Приложению нужен доступ к камере для сканирования QR-кодов самокатов</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Приложению нужен доступ к галерее для выбора фото профиля и подтверждения поездки</string>
<key>UIStatusBarHidden</key>
<false/>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>com.behappy.app</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.behappy.app</string>
</array>
</dict>
</array>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>YandexMapKitApiKey</key>
<string>$(YANDEX_MAPKIT_API_KEY)</string>
</dict>
</plist>

View File

@@ -1,5 +1,4 @@
import 'dart:convert';
import '../../domain/entities/client_notification.dart';
class ClientNotificationDto {
@@ -43,12 +42,19 @@ class ClientNotificationDto {
clientId: clientId,
type: _parseType(type),
category: _parseCategory(category),
createdAt: DateTime.parse(createdAt),
canceledAt: canceledAt != null ? DateTime.parse(canceledAt!) : null,
readAt: readAt != null ? DateTime.parse(readAt!) : null,
createdAt: _parseUtcDate(createdAt),
canceledAt: canceledAt != null ? _parseUtcDate(canceledAt!) : null,
readAt: readAt != null ? _parseUtcDate(readAt!) : null,
);
}
DateTime _parseUtcDate(String dateString) {
if (dateString.endsWith('Z') || dateString.contains('+') || dateString.contains('-')) {
return DateTime.parse(dateString).toLocal();
}
return DateTime.parse('${dateString}Z').toLocal();
}
NotificationType _parseType(String type) {
switch (type.toLowerCase()) {
case 'info':

View File

@@ -2,7 +2,7 @@ import '../../domain/entities/payment_card.dart';
class PaymentCardResponseDto {
final int id;
final int clientId;
final String bindingId;
final int expirationMonth;
final int expirationYear;
final String cardHolder;
@@ -12,7 +12,7 @@ class PaymentCardResponseDto {
PaymentCardResponseDto({
required this.id,
required this.clientId,
required this.bindingId,
required this.expirationMonth,
required this.expirationYear,
required this.cardHolder,
@@ -24,7 +24,7 @@ class PaymentCardResponseDto {
factory PaymentCardResponseDto.fromJson(Map<String, dynamic> json) {
return PaymentCardResponseDto(
id: json['id'] as int,
clientId: json['clientId'] as int,
bindingId: json['bindingId'] as String,
expirationMonth: json['expirationMonth'] as int,
expirationYear: json['expirationYear'] as int,
cardHolder: json['cardHolder'] as String,
@@ -37,13 +37,9 @@ class PaymentCardResponseDto {
PaymentCard toEntity(String? fullCardNumber) {
return PaymentCard(
id: id,
clientId: clientId,
expirationMonth: expirationMonth,
expirationYear: expirationYear,
cardHolder: cardHolder,
bindingId: bindingId,
cardLastNumber: cardLastNumber,
isMain: isMain,
type: type,
);
}
}

View File

@@ -21,6 +21,7 @@ import 'package:flutter_client_sse/constants/sse_request_type_enum.dart';
import 'package:flutter_client_sse/flutter_client_sse.dart';
import '../../domain/entities/client_subscription.dart';
import '../../domain/entities/fine.dart';
import '../../domain/entities/point.dart';
import '../../domain/entities/user_profile.dart';
import '../../domain/entities/payment_card.dart';
@@ -35,8 +36,11 @@ import '../models/subscriptions_response.dart';
import '../models/user_check_response_dto.dart';
class ApiService {
static const String baseUrl = "https://sharing-api.sparkit.by/api/v1";
static const String fileBaseUrl = "https://sharing-api.sparkit.by";
static const String baseUrl = "https://api.behappybel.by/api/v1";
static const String fileBaseUrl = "https://api.behappybel.by/";
/*static const String baseUrl = "https://sharing-api.sparkit.by/api/v1";
static const String fileBaseUrl = "https://sharing-api.sparkit.by";*/
final SecurityService _securityService;
final Dio _dio;
@@ -52,7 +56,11 @@ class ApiService {
try {
final response = await _dio.post(
"$baseUrl/auth/client/login",
data: {"phone": phone, "model": model, "systemId": systemId},
data: {
"phone": phone,
"model": model,
"systemId": systemId
},
);
if (response.statusCode == 200 || response.statusCode == 201) {
@@ -143,11 +151,12 @@ class ApiService {
}
dynamic balanceRaw = profileData["balance"];
int? balance;
if (balanceRaw is int) {
balance = balanceRaw;
double? balance;
if (balanceRaw is num) {
balance = balanceRaw.toDouble();
} else if (balanceRaw is String) {
balance = int.tryParse(balanceRaw);
balance = double.tryParse(balanceRaw);
}
return UserProfile(
@@ -166,6 +175,53 @@ class ApiService {
}
}
Future<void> deleteProfile() async {
try {
final response = await _dio.delete(
"$baseUrl/client/me",
options: await _getAuthOptions(),
);
if (response.statusCode == 200) {
final data = response.data;
final profileData = data["profile"];
if (profileData == null) return null;
String? parsedDate;
if (profileData["dob"] != null) {
try {
parsedDate = DateFormat(
'dd.MM.yyyy',
).format(DateTime.parse(profileData["dob"]));
} catch (_) {}
}
final int? avatarId = profileData["avatarId"];
String? avatarUrl;
if (avatarId != null && profileData["avatar"] != null) {
final String? avatarPath = profileData["avatar"]["path"];
if (avatarPath != null && avatarPath.isNotEmpty) {
avatarUrl = Uri.parse(fileBaseUrl).resolve(avatarPath).toString();
}
}
dynamic balanceRaw = profileData["balance"];
double? balance;
if (balanceRaw is num) {
balance = balanceRaw.toDouble();
} else if (balanceRaw is String) {
balance = double.tryParse(balanceRaw);
}
}
return null;
} catch (e) {
return null;
}
}
Future<List<PaymentCard>> getPaymentCards() async {
final response = await _dio.get(
"$baseUrl/client/me",
@@ -177,13 +233,9 @@ class ApiService {
cardsJson.map((cardJson) async {
return PaymentCard(
id: cardJson['id'],
clientId: cardJson['clientId'],
expirationMonth: cardJson['expirationMonth'],
expirationYear: cardJson['expirationYear'],
cardHolder: cardJson['cardHolder'],
bindingId: cardJson['bindingId'],
cardLastNumber: cardJson['cardLastNumber'],
isMain: cardJson['isMain'],
type: cardJson['cardType'],
);
}),
);
@@ -238,11 +290,11 @@ class ApiService {
}
dynamic balanceRaw = data["balance"];
int? balance;
if (balanceRaw is int) {
double? balance;
if (balanceRaw is double) {
balance = balanceRaw;
} else if (balanceRaw is String) {
balance = int.tryParse(balanceRaw);
balance = double.tryParse(balanceRaw);
}
return UserProfile(
@@ -420,7 +472,7 @@ class ApiService {
int page = 1,
int pageSize = 500,
}) async {
const path = "$baseUrl/zone/available";
const path = "$baseUrl/zone/available2";
final queryParams = {
'readAll': 'true',
@@ -446,6 +498,35 @@ class ApiService {
}
}
Future<ZonesResponse?> getAllZones({
int page = 1,
int pageSize = 500,
}) async {
const path = "$baseUrl/zone/available2";
final queryParams = {
'readAll': 'true',
'page': page,
'pageSize': pageSize,
};
try {
final response = await _dio.get(
path,
queryParameters: queryParams,
options: await _getAuthOptions(),
);
if (response.statusCode == 200) {
return ZonesResponse.fromJson(response.data);
}
return null;
} catch (e) {
print("ERROR: getZones failed: $e");
return null;
}
}
Future<TariffsResponse?> getAvailableTariffs({required int scooterId}) async {
final url = "$baseUrl/scooterplan/$scooterId/available";
@@ -517,13 +598,13 @@ class ApiService {
}
}
Future<bool> activateSubscription({required int optionId}) async {
Future<bool> activateSubscription({required int optionId, required int cardId}) async {
const url = "$baseUrl/scootersubscription/client";
try {
final response = await _dio.post(
url,
data: {"optionId": optionId},
data: {"optionId": optionId, "cardId": cardId},
options: await _getAuthOptions(),
);
@@ -539,31 +620,25 @@ class ApiService {
}
}
Future<void> addPaymentCard({
required String cardNumber,
required String cardHolder,
required int expirationMonth,
required int expirationYear,
required String cvv,
}) async {
Future<Map<String, dynamic>> addPaymentCard() async {
const url = "$baseUrl/client/card";
final cleanCardNumber = cardNumber.replaceAll(' ', '');
/*final cleanCardNumber = cardNumber.replaceAll(' ', '');*/
try {
final response = await _dio.post(
url,
data: {
/*data: {
"cardNumber": cleanCardNumber,
"cardHolder": cardHolder,
"expirationMonth": expirationMonth,
"expirationYear": expirationYear,
"cvv": cvv,
},
},*/
options: await _getAuthOptions(),
);
if (response.statusCode == 200 || response.statusCode == 201) {
return;
return response.data;
}
throw AuthException('Непредвиденный статус: ${response.statusCode}', 0);
@@ -714,17 +789,13 @@ class ApiService {
}
}
Future<ScooterOrder?> cancelRide(int orderId) async {
Future<void> cancelRide(int orderId) async {
try {
final response = await _dio.put(
"$baseUrl/scooterorder/$orderId/cancel",
options: await _getAuthOptions(),
);
if (response.statusCode == 200 || response.statusCode == 201) {
return ScooterOrder.fromJson(response.data);
}
return null;
} on DioException catch (e) {
_handleDioError(e);
return null;
@@ -765,7 +836,7 @@ class ApiService {
}
}
Future<ScooterOrder?> finishRide({
Future<void> finishRide({
required int orderId,
required List<int> filesId,
}) async {
@@ -776,10 +847,6 @@ class ApiService {
options: await _getAuthOptions(),
);
if (response.statusCode == 200 || response.statusCode == 201) {
return ScooterOrder.fromJson(response.data);
}
return null;
} on DioException catch (e) {
if (e.response?.statusCode == 400) {
final data = e.response?.data;
@@ -947,6 +1014,64 @@ class ApiService {
}
}
/// GET /scooterorder/fines/client
Future<List<Fine>> getClientFines() async {
try {
final response = await _dio.get(
"$baseUrl/scooterorder/fines/client",
options: await _getAuthOptions(),
);
if (response.statusCode == 200 && response.data is Map) {
final List<dynamic> dataList = response.data['data'] ?? [];
return dataList.map((json) => Fine.fromJson(json)).toList();
}
return [];
} on DioException catch (e) {
_handleDioError(e);
return [];
}
}
/// GET /scooterorder/{id}/fine
Future<Fine?> getFineDetails(int orderId) async {
try {
final response = await _dio.get(
"$baseUrl/scooterorder/fines/client/$orderId",
options: await _getAuthOptions(),
);
if (response.statusCode == 200 && response.data != null) {
return Fine.fromJson(response.data);
}
return null;
} on DioException catch (e) {
_handleDioError(e);
return null;
}
}
/// PUT /scooterorder/fines/client/{id}
/// {id} — это id штрафа (Fine.id), а не id заказа (Fine.orderId)
Future<void> payFine({
required int orderId,
int? cardId,
required bool isBalance,
}) async {
try {
await _dio.put(
"$baseUrl/scooterorder/fines/client/$orderId",
data: {
"cardId": cardId ?? 0,
"isBalance": isBalance,
},
options: await _getAuthOptions(),
);
} on DioException catch (e) {
_handleDioError(e);
}
}
Future<Map<String, dynamic>> getNews() async {
try {
final response = await _dio.get(
@@ -1019,10 +1144,10 @@ class ApiService {
if (event.data != null && event.data!.isNotEmpty) {
try {
final Map<String, dynamic> jsonData = jsonDecode(event.data!);
print("✅ [SSE] Parsed JSON: $jsonData");
print("[SSE] Parsed JSON: $jsonData");
controller.add(jsonData);
} catch (e) {
print("❌ [SSE] JSON Parse Error: $e | Raw: ${event.data}");
print("[SSE] JSON Parse Error: $e | Raw: ${event.data}");
}
}
},
@@ -1040,17 +1165,19 @@ class ApiService {
return controller.stream;
}
Future<List<Map<String, dynamic>>> getNotifications() async {
final url = Uri.parse('$baseUrl/notification/client');
Future<Map<String, dynamic>> getNotifications({
int page = 1,
int pageSize = 10,
}) async {
final url = Uri.parse('$baseUrl/notification/client').replace(
queryParameters: {
'page': page.toString(),
'pageSize': pageSize.toString(),
},
);
final accessToken = await _securityService.getAccessToken();
if (accessToken == null) {
print("APISERVICE Error: Access token is null.");
throw UnauthorizedException();
}
print("GET NOTIFICATIONS REQUEST:");
print("URL: $url");
if (accessToken == null) throw UnauthorizedException();
final response = await http.get(
url,
@@ -1060,34 +1187,17 @@ class ApiService {
},
);
print("GET NOTIFICATIONS RESPONSE:");
print("STATUS: ${response.statusCode}");
print("BODY: ${response.body}");
if (response.statusCode == 200) {
final data = jsonDecode(utf8.decode(response.bodyBytes));
// ✅ Проверяем, является ли ответ массивом или объектом с data[]
if (data is List) {
return data.cast<Map<String, dynamic>>();
} else if (data is Map<String, dynamic>) {
final list = data['data'];
if (list is List) {
return list.cast<Map<String, dynamic>>();
} else {
throw Exception(
'Expected a List under "data" but got ${list.runtimeType}',
);
}
} else {
throw Exception('Expected a List or Map but got ${data.runtimeType}');
if (data is Map<String, dynamic>) {
return data;
}
throw Exception('Expected Map with data and pagination');
} else if (response.statusCode == 401) {
throw UnauthorizedException();
} else if (response.statusCode == 403) {
throw AuthBlockException();
}
throw Exception('Ошибка сервера: ${response.statusCode}');
}
@@ -1176,5 +1286,6 @@ class ApiService {
}
}
// ✅ Добавь исключение (можно в отдельный файл)
class PromoCodeNotFoundException implements Exception {}

View File

@@ -1,7 +1,9 @@
import 'package:be_happy/domain/entities/client_notification.dart';
import '../../data/network/api_service.dart';
import '../../domain/entities/pagination_meta.dart';
import '../../domain/repositories/notification_repository.dart';
import '../../domain/usecase/get_notifications_usecase.dart';
import '../models/client_notification_dto.dart';
class NotificationRepositoryImpl implements NotificationRepository {
@@ -35,19 +37,27 @@ class NotificationRepositoryImpl implements NotificationRepository {
}
@override
Future<List<ClientNotification>> getNotifications() async {
Future<NotificationsResult> getNotifications({
int page = 1,
int pageSize = 10,
}) async {
try {
final List<Map<String, dynamic>> data = await _apiService.getNotifications();
final notifications = data.map((json) {
final dto = ClientNotificationDto.fromJson(json);
return dto.toEntity();
}).toList();
final response = await _apiService.getNotifications(
page: page,
pageSize: pageSize,
);
// dev.log('NotificationRepository: Загружено ${notifications.length} уведомлений');
final notifications = (response['data'] as List)
.map((json) => ClientNotificationDto.fromJson(json).toEntity())
.toList();
return notifications;
final pagination = PaginationMeta.fromJson(response['pagination']);
return NotificationsResult(
notifications: notifications,
pagination: pagination,
);
} catch (e, stackTrace) {
// dev.log('NotificationRepository: Ошибка: $e', stackTrace: stackTrace);
throw Exception('Не удалось загрузить уведомления: $e');
}
}

View File

@@ -32,25 +32,25 @@ class PaymentRepositoryImpl implements PaymentRepository {
}
@override
Future<Result<void>> addPaymentCard({
required String cardNumber,
Future<Result<Map<String, dynamic>>> addPaymentCard(
/*required String cardNumber,
required String cardHolder,
required String expiryMonth,
required String expiryYear,
required String cvv,
}) async {
required String cvv,*/
) async {
try {
await apiService.addPaymentCard(
cardNumber: cardNumber,
final result = await apiService.addPaymentCard(
/*cardNumber: cardNumber,
cardHolder: cardHolder,
expirationMonth: int.parse(expiryMonth),
expirationYear: int.parse(expiryYear),
cvv: cvv,
cvv: cvv,*/
);
// await securityService.saveCardFullNumber(cardId, cardNumber);
return Success(null);
return Success(result);
} on AuthException catch (e) {
return Failure(AuthFailure(e.attemptsLeft));
} on AuthBlockException catch (_) {
@@ -98,9 +98,9 @@ class PaymentRepositoryImpl implements PaymentRepository {
}
@override
Future<Result<bool>> activateSubscription(int optionId) async {
Future<Result<bool>> activateSubscription(int optionId, int cardId) async {
try {
final success = await apiService.activateSubscription(optionId: optionId);
final success = await apiService.activateSubscription(optionId: optionId, cardId: cardId);
return Success(success);
} on AuthException catch (e) {
return Failure(AuthFailure(e.attemptsLeft));

View File

@@ -27,6 +27,11 @@ class UserProfileRepositoryImpl implements UserProfileRepository {
return await _apiService.getProfile() ?? _cachedProfile;
}
@override
Future<void> deleteProfile() async {
return await _apiService.deleteProfile();
}
@override
Future<UserProfile?> updateProfile(UserProfile profile) async {
//await Future.delayed(const Duration(milliseconds: 300));

View File

@@ -1,21 +1,22 @@
import 'dart:io';
import 'package:be_happy/data/exceptions/auth_block_exception.dart';
import 'package:be_happy/data/exceptions/route_history_not_found_exception.dart';
import 'package:be_happy/data/exceptions/scooter_not_found_exception.dart';
import 'package:be_happy/data/exceptions/unauthorized_exception.dart';
import 'package:be_happy/data/exceptions/wrong_zone_exception.dart';
import 'package:be_happy/domain/entities/point.dart';
import 'package:be_happy/domain/repositories/scooter_repository.dart';
import '../../core/failures.dart';
import '../../core/result.dart';
import '../../domain/entities/active_scooter_order.dart';
import '../../domain/entities/client_subscription.dart';
import '../../domain/entities/fine.dart';
import '../../domain/entities/point.dart';
import '../../domain/entities/scooter.dart';
import '../../domain/entities/tariff.dart';
import '../../domain/entities/subscription.dart';
import '../../domain/entities/scooter_order.dart';
import '../../domain/repositories/scooter_repository.dart';
import '../exceptions/auth_block_exception.dart';
import '../exceptions/auth_exception.dart';
import '../exceptions/route_history_not_found_exception.dart';
import '../exceptions/scooter_not_found_exception.dart';
import '../exceptions/unauthorized_exception.dart';
import '../exceptions/wrong_zone_exception.dart';
import '../network/api_service.dart';
class ScooterRepositoryImpl extends ScooterRepository {
@@ -192,15 +193,11 @@ class ScooterRepositoryImpl extends ScooterRepository {
}
@override
Future<Result<ScooterOrder>> cancelRide(int orderId) async {
late final Result<ScooterOrder> result;
Future<Result<void>> cancelRide(int orderId) async {
late final Result<void> result;
try {
final order = await _apiService.cancelRide(orderId);
if (order != null) {
result = Success(order);
} else {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
await _apiService.cancelRide(orderId);
result = Success(null);
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} catch (e) {
@@ -246,18 +243,11 @@ class ScooterRepositoryImpl extends ScooterRepository {
}
@override
Future<Result<ScooterOrder>> finishRide(int orderId, List<int> files) async {
Future<Result<void>> finishRide(int orderId, List<int> files) async {
late final Result<ScooterOrder> result;
try {
final order = await _apiService.finishRide(
orderId: orderId,
filesId: files,
);
if (order != null) {
result = Success(order);
} else {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
await _apiService.finishRide(orderId: orderId, filesId: files);
result = Success(null);
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} on WrongZoneException catch (e) {
@@ -274,7 +264,6 @@ class ScooterRepositoryImpl extends ScooterRepository {
try {
await _apiService.payRide(orderId);
result = Success(null);
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} catch (e) {
@@ -345,7 +334,7 @@ class ScooterRepositoryImpl extends ScooterRepository {
cardId: cardId,
isBalance: isBalance,
);
result = Success(null);
result = Success(null);
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} catch (e) {
@@ -437,4 +426,70 @@ class ScooterRepositoryImpl extends ScooterRepository {
}
return result;
}
@override
Future<Result<Fine>> getFineDetails(int orderId) async {
late final Result<Fine> result;
try {
final fine = await _apiService.getFineDetails(orderId);
if (fine != null) {
result = Success(fine);
} else {
result = Failure(UnknownFailure("Не удалось получить данные о штрафе"));
}
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} on UnauthorizedException catch (e) {
result = Failure(AuthFailure(0, message: e.toString()));
} on AuthBlockException catch (e) {
result = Failure(AuthBlockFailure(e.toString()));
} catch (e) {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
@override
Future<Result<List<Fine>>> getClientFines() async {
late final Result<List<Fine>> result;
try {
final fines = await _apiService.getClientFines();
result = Success(fines);
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} on UnauthorizedException catch (e) {
result = Failure(AuthFailure(0, message: e.toString()));
} on AuthBlockException catch (e) {
result = Failure(AuthBlockFailure(e.toString()));
} catch (e) {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
@override
Future<Result<void>> payFine({
required int orderId,
int? cardId,
required bool isBalance,
}) async {
late final Result<void> result;
try {
await _apiService.payFine(
orderId: orderId,
cardId: cardId,
isBalance: isBalance,
);
result = Success(null);
} on AuthException catch (e) {
result = Failure(AuthFailure(e.attemptsLeft));
} on UnauthorizedException catch (e) {
result = Failure(AuthFailure(0, message: e.toString()));
} on AuthBlockException catch (e) {
result = Failure(AuthBlockFailure(e.toString()));
} catch (e) {
result = Failure(UnknownFailure("Неизвестная ошибка"));
}
return result;
}
}

View File

@@ -10,60 +10,60 @@ class ZoneRepositoryImpl extends ZoneRepository {
ZoneRepositoryImpl(this._apiService);
@override
Future<List<Zone>> getZones(List<double> area,
int page,
int pageSize,) async {
Future<List<Zone>> getZones(List<double> area, int page, int pageSize) async {
final List<Point> list = [];
final List<Point> list2 = [];
/*list.add(Point(55.182372,30.203347));
list.add(Point(55.173746,30.200257));
list.add(Point(55.172153,30.212531));
list.add(Point(55.179946,30.215964));
list2.add(Point(55.178304,30.227380));
list2.add(Point(55.178059,30.229654));
list2.add(Point(55.191339,30.234718));
list2.add(Point(55.194352,30.234890));
list2.add(Point(55.194377,30.231972));
list2.add(Point(55.192123,30.232744));
final zone = Zone(id: 123,
title: "Zone 01",
description: "description",
type: "Finish",
isActive: true,
shapeType: "polygon",
points: list,
speedLimit: "speedLimit");
final zone2 = Zone(id: 124,
title: "Zone 02",
description: "description",
type: "NotDrive",
isActive: true,
shapeType: "polygon",
points: list2,
speedLimit: "speedLimit");
*/
final responce = await _apiService.getZones(
area: area,
page: page,
pageSize: pageSize,
);
final List<Zone>? zones = responce?.zones;
// zones?.add(zone);
// zones?.add(zone2);
print("Scooters: ${zones?.first.title}");
if (responce != null) {
return zones ?? List.empty();
if (responce == null) {
throw Exception(
'Failed to load zones for area: empty response from server',
);
}
return List.empty();
final zones = responce.zones;
print("Zones: ${zones.length}");
return zones;
}
@override
Future<List<Zone>> getAllZones(int page, int pageSize) async {
final List<Point> list = [];
final List<Point> list2 = [];
final responce = await _apiService.getAllZones(
page: page,
pageSize: pageSize,
);
// apiService.getAllZones ловит свои ошибки (сеть/401/5xx) и в этом
// случае возвращает null. Раньше это тихо превращалось в "зон нет" —
// из-за чего сбой запроса выглядел как легитимный пустой ответ и
// (в связке с кешем на уровне MapBloc) навсегда "прятал" зоны на
// время сессии. Бросаем исключение, чтобы вызывающий код (usecase/bloc)
// мог отличить реальный сбой от честного "зон действительно нет"
// и, при необходимости, повторить запрос.
if (responce == null) {
throw Exception('Failed to load zones: empty response from server');
}
final zones = responce.zones;
print(zones.map((z) => '${z.type}:${z.id}'));
for (final z in zones) {
print("Zone ${z.type}:${z.id} geometry.length=${z.geometry.length}");
}
print("Zones: ${zones.length}");
return zones;
}
}

View File

@@ -10,6 +10,6 @@ class AppSettingsService {
}
bool getMapSettingsFlag(String key) {
return sharedPreferences.getBool(key) ?? false;
return sharedPreferences.getBool(key) ?? true;
}
}

View File

@@ -17,6 +17,8 @@ import 'package:be_happy/domain/repositories/scooter_repository.dart';
import 'package:be_happy/domain/repositories/zone_repository.dart';
import 'package:be_happy/domain/service/security_service.dart';
import 'package:be_happy/domain/usecase/add_payment_card_usecase.dart'; // ← новый
import 'package:be_happy/domain/usecase/delete_profile_usecase.dart';
import 'package:be_happy/domain/usecase/get_all_zones_usecase.dart';
import 'package:be_happy/domain/usecase/get_certificates_usecase.dart';
import 'package:be_happy/domain/usecase/get_scooter_order_route_history_usecase.dart';
import 'package:be_happy/domain/usecase/is_pin_set_usecase.dart';
@@ -54,6 +56,7 @@ import 'package:be_happy/domain/usecase/finish_ride_usecase.dart';
import 'package:be_happy/domain/usecase/pause_ride_usecase.dart';
import 'package:be_happy/domain/usecase/resume_ride_usecase.dart';
import 'package:be_happy/domain/usecase/verify_pin_usecase.dart';
import 'package:be_happy/presentation/event/profile_event.dart';
import 'package:be_happy/presentation/viewmodel/active_ride_bloc.dart';
import 'package:be_happy/presentation/viewmodel/add_card_bloc.dart'; // ← новый
import 'package:be_happy/presentation/viewmodel/current_rides_bloc.dart';
@@ -94,14 +97,18 @@ import '../domain/service/device_info_service.dart';
import '../domain/usecase/activate_subscription_usecase.dart';
import '../domain/usecase/apply_promo_code_usecase.dart';
import '../domain/usecase/get_client_subscriptions_usecase.dart';
import '../domain/usecase/get_fine_details_usecase.dart';
import '../domain/usecase/get_fines_usecase.dart';
import '../domain/usecase/get_news_by_id_usecase.dart';
import '../domain/usecase/get_notifications_usecase.dart';
import '../domain/usecase/get_scooter_by_title_usecase.dart';
import '../domain/usecase/get_scooter_order_history_usecase.dart';
import '../domain/usecase/pay_fine_usecase.dart';
import '../domain/usecase/remove_payment_card_usecase.dart';
import '../domain/usecase/set_main_payment_card_usecase.dart';
import '../presentation/viewmodel/auth_bloc.dart';
import '../presentation/viewmodel/edit_profile_bloc.dart';
import '../presentation/viewmodel/fine_list_bloc.dart';
import '../presentation/viewmodel/map_bloc.dart';
import '../presentation/viewmodel/news_bloc.dart';
import '../presentation/viewmodel/notifications_bloc.dart';
@@ -116,7 +123,13 @@ final getIt = GetIt.instance;
Future<void> setupDependencies() async {
final sharedPreferences = await SharedPreferences.getInstance();
final dio = Dio();
dio.interceptors.add(LogInterceptor(/*requestHeader: false, responseHeader:false, */responseBody: true, requestBody: true));
dio.interceptors.add(
LogInterceptor(
/*requestHeader: false, responseHeader:false, */
responseBody: true,
requestBody: true,
),
);
dio.interceptors.add(AuthInterceptor());
// HTTP
getIt.registerSingleton<http.Client>(http.Client());
@@ -140,7 +153,7 @@ Future<void> setupDependencies() async {
getIt.registerSingleton<AppSettingsService>(AppSettingsService(getIt()));
getIt.registerLazySingleton<NewsApiService>(
() => NewsApiService(getIt<ApiService>()),
() => NewsApiService(getIt<ApiService>()),
);
// Repository
@@ -171,7 +184,7 @@ Future<void> setupDependencies() async {
);
getIt.registerLazySingleton<NewsRepository>(
() => NewsRepositoryImpl(getIt<NewsApiService>()),
() => NewsRepositoryImpl(getIt<NewsApiService>()),
);
getIt.registerSingleton<NotificationRepository>(
@@ -195,6 +208,8 @@ Future<void> setupDependencies() async {
getIt.registerSingleton<GetProfileUseCase>(GetProfileUseCase(getIt()));
getIt.registerSingleton<DeleteProfileUsecase>(DeleteProfileUsecase(getIt(), getIt(), getIt()));
getIt.registerSingleton<UpdateProfileUseCase>(UpdateProfileUseCase(getIt()));
getIt.registerSingleton<UploadProfilePhotoUsecase>(
@@ -225,6 +240,9 @@ Future<void> setupDependencies() async {
getIt.registerSingleton<GetAvailableZonesUsecase>(
GetAvailableZonesUsecase(getIt()),
);
getIt.registerSingleton<GetAllZonesUsecase>(
GetAllZonesUsecase(getIt()),
);
getIt.registerSingleton<GetMapSettingsUsecase>(
GetMapSettingsUsecase(getIt()),
);
@@ -296,25 +314,28 @@ Future<void> setupDependencies() async {
GetNotificationsUsecase(getIt<NotificationRepository>()),
);
getIt.registerFactory<NotificationsBloc>(
() => NotificationsBloc(getIt<GetNotificationsUsecase>()),
() => NotificationsBloc(getIt<GetNotificationsUsecase>()),
);
// Blocs
getIt.registerLazySingleton<SplashBloc>(() => SplashBloc(getIt()));
getIt.registerFactory<PhoneAuthBloc>(() => PhoneAuthBloc(getIt()));
getIt.registerFactory<VerifyCodeBloc>(() => VerifyCodeBloc(getIt()));
getIt.registerFactory<VerifyCodeBloc>(
() => VerifyCodeBloc(getIt(), getIt(), getIt()),
);
getIt.registerFactory<ProfileBloc>(() => ProfileBloc(getIt(), getIt(), getIt()));
getIt.registerLazySingleton<ProfileBloc>(
() => ProfileBloc(getIt(), getIt(), getIt(), getIt(), getIt(), getIt()),
);
getIt.registerFactory<EditProfileBloc>(
() => EditProfileBloc(getIt(), getIt()),
() => EditProfileBloc(getIt(), getIt()),
);
getIt.registerFactory<MapBloc>(
() => MapBloc(
() => MapBloc(
getIt(),
getIt(),
getIt(),
@@ -330,33 +351,31 @@ Future<void> setupDependencies() async {
getIt.registerFactory<ScooterDetailBloc>(() => ScooterDetailBloc(getIt()));
getIt.registerFactory<MapSettingsModalBloc>(
() => MapSettingsModalBloc(getIt(), getIt()),
() => MapSettingsModalBloc(getIt(), getIt()),
);
getIt.registerFactory<AddCardBloc>(() => AddCardBloc(getIt()));
getIt.registerFactory<PaymentMethodSheetBloc>(
() => PaymentMethodSheetBloc(getIt()),
() => PaymentMethodSheetBloc(getIt(), getIt(), getIt(), getIt()),
);
getIt.registerFactory<CurrentRidesBloc>(() => CurrentRidesBloc(getIt()));
getIt.registerFactory<ActiveRideBloc>(
() => ActiveRideBloc(getIt(), getIt(), getIt(), getIt(), getIt()),
() => ActiveRideBloc(getIt(), getIt(), getIt(), getIt(), getIt()),
);
getIt.registerFactory<ReservedRideBloc>(
() => ReservedRideBloc(getIt(), getIt()),
() => ReservedRideBloc(getIt(), getIt()),
);
getIt.registerFactory<NewsBloc>(() => NewsBloc(getIt<NewsRepository>()));
getIt.registerFactory<SendPhotoBloc>(() => SendPhotoBloc(getIt(), getIt()));
getIt.registerFactory<SubscriptionListBloc>(
() => SubscriptionListBloc(
() => SubscriptionListBloc(
getAvailableSubscriptionsUsecase: getIt(),
getClientSubscriptionsUsecase: getIt(),
),
@@ -381,26 +400,37 @@ Future<void> setupDependencies() async {
// Bloc
getIt.registerFactory<OrderHistoryBloc>(
() => OrderHistoryBloc(getIt<GetScooterOrderHistoryUsecase>()),
() => OrderHistoryBloc(getIt<GetScooterOrderHistoryUsecase>()),
);
getIt.registerFactory<FineListBloc>(() => FineListBloc(getIt()));
getIt.registerFactory<ScooterCodeBloc>(
() => ScooterCodeBloc(getScooterByTitleUsecase: getIt<GetScooterByTitleUsecase>()),
() => ScooterCodeBloc(
getScooterByTitleUsecase: getIt<GetScooterByTitleUsecase>(),
getAddressUsecase: getIt<GetAddressByPointUsecase>(),
getPedestrianRoutesUsecase: getIt<GetPedestrianRoutesUsecase>()
),
);
// Repository
getIt.registerSingleton<PromoCodeRepository>(
PromoCodeRepositoryImpl(getIt<ApiService>()),
);
// UseCase
// UseCase
getIt.registerSingleton<ApplyPromoCodeUsecase>(
ApplyPromoCodeUsecase(getIt<PromoCodeRepository>()),
);
// Bloc (factory, т.к. экран создаёт новый экземпляр)
getIt.registerFactory<GetFinesUsecase>(() => GetFinesUsecase(getIt()));
getIt.registerFactory<GetFineDetailsUsecase>(() => GetFineDetailsUsecase(getIt()));
getIt.registerFactory<PayFineUsecase>(() => PayFineUsecase(getIt()));
// Bloc (factory, т.к. экран создаёт новый экземпляр)
getIt.registerFactory<PromoCodeBloc>(
() => PromoCodeBloc(getIt<ApplyPromoCodeUsecase>()),
() => PromoCodeBloc(getIt<ApplyPromoCodeUsecase>()),
);
}

View File

@@ -32,7 +32,7 @@ class ClientNotification {
required this.createdAt,
this.canceledAt,
this.readAt,
});
}) ;
ClientNotification copyWith({
int? id,

View File

@@ -0,0 +1,62 @@
import 'package:equatable/equatable.dart';
class Fine extends Equatable {
final int id;
final int orderId; // ← добавлено
final String scooterNumber; // можно оставить для совместимости, но не использовать в UI
final DateTime createdAtDateTime; // ← новое поле
final String description;
final double amount;
final String amountPrint;
final List<String> photoUrls;
final bool isPaid;
const Fine({
required this.id,
required this.orderId,
required this.scooterNumber,
required this.createdAtDateTime,
required this.description,
required this.amount,
required this.amountPrint,
required this.photoUrls,
required this.isPaid,
});
factory Fine.fromJson(Map<String, dynamic> json, {String? scooterNumber}) {
final List<dynamic> filesJson = json['files'] ?? [];
final List<String> photos = filesJson
.map((file) => file['path'] as String? ?? file['filename'] as String? ?? '')
.where((path) => path.isNotEmpty)
.toList();
// Парсим createdAt как DateTime
final createdAtDateTime = DateTime.tryParse(json['createdAt'].toString()) ??
DateTime.now(); // fallback
return Fine(
id: json['id'] as int? ?? 0,
orderId: json['orderId'] as int? ?? 0,
scooterNumber: scooterNumber ?? '',
createdAtDateTime: createdAtDateTime,
description: json['description'] as String? ?? '',
amount: (json['value'] as num?)?.toDouble() ?? 0.0,
amountPrint: json['valuePrint'] as String? ?? '0,00 BYN',
photoUrls: photos,
isPaid: json['isPaid'] as bool? ?? false,
);
}
@override
List<Object?> get props => [
id,
orderId,
scooterNumber,
createdAtDateTime,
description,
amount,
amountPrint,
photoUrls,
isPaid,
];
}

View File

@@ -6,10 +6,10 @@ class MapSettings {
final bool restricted_driving_zones;
MapSettings({
required this.all_placemarks,
required this.all_zones,
required this.parking_zones,
required this.restricted_parking_zones,
required this.restricted_driving_zones,
this.all_placemarks = true,
this.all_zones = true,
this.parking_zones = true,
this.restricted_parking_zones = true,
this.restricted_driving_zones = true,
});
}

View File

@@ -0,0 +1,28 @@
class PaginationMeta {
final int total;
final int lastPage;
final int currentPage;
final int totalPerPage;
final int? prevPage;
final int? nextPage;
PaginationMeta({
required this.total,
required this.lastPage,
required this.currentPage,
required this.totalPerPage,
this.prevPage,
this.nextPage,
});
factory PaginationMeta.fromJson(Map<String, dynamic> json) {
return PaginationMeta(
total: json['total'] as int,
lastPage: json['lastPage'] as int,
currentPage: json['currentPage'] as int,
totalPerPage: json['totalPerPage'] as int,
prevPage: json['prevPage'] as int?,
nextPage: json['nextPage'] as int?,
);
}
}

View File

@@ -1,43 +1,27 @@
class PaymentCard {
final int id;
final int clientId;
final int expirationMonth;
final int expirationYear;
final String cardHolder;
final String? bindingId;
final String cardLastNumber;
final bool isMain;
final String type;
PaymentCard({
required this.id,
required this.clientId,
required this.expirationMonth,
required this.expirationYear,
required this.cardHolder,
required this.bindingId,
required this.cardLastNumber,
required this.isMain,
required this.type,
});
PaymentCard copyWith({
int? id,
int? clientId,
int? expirationMonth,
int? expirationYear,
String? cardHolder,
String? bindingId,
String? cardLastNumber,
bool? isMain,
String? type,
}) {
return PaymentCard(
id: id ?? this.id,
clientId: clientId ?? this.clientId,
expirationMonth: expirationMonth ?? this.expirationMonth,
expirationYear: expirationYear ?? this.expirationYear,
cardHolder: cardHolder ?? this.cardHolder,
bindingId: bindingId ?? this.bindingId,
cardLastNumber: cardLastNumber ?? this.cardLastNumber,
isMain: isMain ?? this.isMain,
type: type ?? this.type,
);
}
}

View File

@@ -27,8 +27,12 @@ class ScooterOrder {
final DateTime? expiresAt;
final DateTime? cancelAt;
final String? cancelDescription;
final double mileage; //эту
final double avgSpeed; //эту и снизу еще 4 есть
final double mileage;
final double avgSpeed;
final String payTotalPricePrint;
final String unpayTotalPricePrint;
final String totalOrderPricePrint;
final bool hasFine;
ScooterOrder({
required this.id,
@@ -57,11 +61,17 @@ class ScooterOrder {
this.expiresAt,
this.cancelAt,
this.cancelDescription,
required this.mileage, //эту
required this.avgSpeed, //эту и снизу еще 2 есть
required this.mileage,
required this.avgSpeed,
required this.payTotalPricePrint, // оплачено
required this.unpayTotalPricePrint, //неоплачено
required this.totalOrderPricePrint, //общая
required this.hasFine,
});
factory ScooterOrder.fromJson(Map<String, dynamic> json) {
final paymentInfo = json['paymentInfo'] as Map<String, dynamic>?;
return ScooterOrder(
id: json['id'] ?? 0,
scooterId: json['scooterId'] ?? 0,
@@ -101,8 +111,12 @@ class ScooterOrder {
? DateTime.parse(json['cancelAt'])
: null,
cancelDescription: json['cancelDescription'],
mileage: (json['mileage'] as num?)?.toDouble() ?? 0.0, //эту
avgSpeed: (json['avgSpeed'] as num?)?.toDouble() ?? 0.0, //эту
mileage: (json['mileage'] as num?)?.toDouble() ?? 0.0,
avgSpeed: (json['avgSpeed'] as num?)?.toDouble() ?? 0.0,
payTotalPricePrint: paymentInfo?['payTotalPricePrint'] ?? '',
unpayTotalPricePrint: paymentInfo?['unpayTotalPricePrint'] ?? '',
totalOrderPricePrint: paymentInfo?['totalOrderPricePrint'] ?? '',
hasFine: paymentInfo?['hasFine'] ?? false,
);
}

View File

@@ -10,6 +10,7 @@ class Tariff {
final double startPrice; // Старт цена
final double cashback; // Процент кэшбэка
final double insurance; // Страховка
final int subscriptionId;
Tariff({
required this.id,
@@ -23,6 +24,7 @@ class Tariff {
required this.startPrice,
required this.cashback,
required this.insurance,
required this.subscriptionId,
});
factory Tariff.fromJson(Map<String, dynamic> json) {
@@ -41,6 +43,7 @@ class Tariff {
startPrice: (planPrice['start'] as num?)?.toDouble() ?? 0.0,
cashback: (planPrice['cashback'] as num?)?.toDouble() ?? 0.0,
insurance: (planPrice['insurance'] as num?)?.toDouble() ?? 0.0,
subscriptionId: json.containsKey('subscriptionId') ? json['subscriptionId'] : 0,
);
}

View File

@@ -3,7 +3,7 @@ class UserProfile {
String birthDate;
String phone;
String email;
int? balance;
double? balance;
int? avatarId;
String? avatarUrl;
@@ -21,7 +21,7 @@ class UserProfile {
String? name,
String? birthDate,
String? email,
int? balance,
double? balance,
int? avatarId,
String? avatarUrl,
}) {

View File

@@ -1,3 +1,4 @@
import 'dart:convert';
import 'package:be_happy/domain/entities/point.dart';
@@ -9,7 +10,7 @@ class Zone {
final String type;
final bool isActive;
final String shapeType;
final List<Point> points;
final List<List<List<Point>>> geometry;
final String speedLimit;
Zone({
@@ -19,56 +20,89 @@ class Zone {
required this.type,
required this.isActive,
required this.shapeType,
required this.points,
required this.geometry,
required this.speedLimit,
});
List<Point> get points {
if (geometry.isNotEmpty && geometry[0].isNotEmpty) {
return geometry[0][0];
}
return [];
}
factory Zone.fromJson(Map<String, dynamic> json) {
final zoneCoordinates = json['coordinates'] as Map<String, dynamic>? ?? {};
final String coordsString = zoneCoordinates['coordinates'] ?? '[]';
final String shapeType = zoneCoordinates['type'] ?? 'Polygon';
List<Point> points = [];
List<List<List<Point>>> parsedGeometry = [];
try {
final dynamic decoded = jsonDecode(coordsString);
if (decoded is List && decoded.isNotEmpty) {
List<dynamic> targetList = [];
if (shapeType == 'Polygon') {
// У полигона структура [[[lat, lon], ...]] -> уходим на 1 уровень вглубь
targetList = decoded[0] as List<dynamic>;
final polygonRings = _parseRings(decoded);
if (polygonRings.isNotEmpty) parsedGeometry.add(polygonRings);
} else if (shapeType == 'MultiPolygon') {
for (var poly in decoded) {
if (poly is List) {
final polygonRings = _parseRings(poly);
if (polygonRings.isNotEmpty) parsedGeometry.add(polygonRings);
}
}
} else {
// У LineString структура [[lat, lon], ...] -> используем как есть
targetList = decoded;
final singleRing = _parseCoordinateList(decoded);
if (singleRing.isNotEmpty) parsedGeometry.add([singleRing]);
}
points = targetList.map((item) {
final List<dynamic> coords = item as List<dynamic>;
return Point(
(coords[1] as num).toDouble(),
(coords[0] as num).toDouble(),
);
}).toList();
}
} catch (e) {
print("PARSE ERROR for Zone ID ${json['id']}: $e");
} catch (e, stackTrace) {
// Replace with your project logger (e.g., debugPrint or Logger)
assert(() {
print("PARSE ERROR for Zone ID ${json['id']}: $e\n$stackTrace");
return true;
}());
}
return Zone(
id: json['id'] ?? 0,
title: json['title'] ?? 'Unknown',
description: json['description'] ?? '',
type: json['type'] ?? '',
isActive: json['isActive'] ?? false,
speedLimit: json['speedLimit'] ?? '',
id: json['id'] as int? ?? 0,
title: json['title'] as String? ?? 'Unknown',
description: json['description'] as String? ?? '',
type: json['type'] as String? ?? '',
isActive: json['isActive'] as bool? ?? false,
speedLimit: json['speedLimit'] as String? ?? '',
shapeType: shapeType,
points: points,
geometry: parsedGeometry,
);
}
static List<List<Point>> _parseRings(List rawRings) {
List<List<Point>> polygonRings = [];
for (var ring in rawRings) {
if (ring is List) {
final ringPoints = _parseCoordinateList(ring);
if (ringPoints.isNotEmpty) polygonRings.add(ringPoints);
}
}
return polygonRings;
}
static List<Point> _parseCoordinateList(List rawCoords) {
List<Point> ringPoints = [];
for (var coord in rawCoords) {
if (coord is List && coord.length >= 2) {
ringPoints.add(Point(
(coord[1] as num).toDouble(),
(coord[0] as num).toDouble(),
));
}
}
return ringPoints;
}
@override
String toString() {
return 'Zone{id: $id, title: $title, type: $type, points: $points}';
return 'Zone{id: $id, title: $title, type: $type, polygonsCount: ${geometry.length}}';
}
}

View File

@@ -1,5 +1,7 @@
import 'package:be_happy/domain/entities/client_notification.dart';
import '../usecase/get_notifications_usecase.dart';
abstract class NotificationRepository {
/// Устанавливает постоянное SSE-соединение и возвращает поток уведомлений
Stream<ClientNotification> getNotificationsStream();
@@ -11,5 +13,7 @@ abstract class NotificationRepository {
void closeStream();
/// получить список уведомлений
Future<List<ClientNotification>> getNotifications();
}
Future<NotificationsResult> getNotifications({
int page = 1,
int pageSize = 10,
});}

View File

@@ -4,17 +4,17 @@ import '../entities/payment_card.dart';
abstract class PaymentRepository {
Future<Result<List<PaymentCard>>> getPaymentCards();
Future<Result<void>> addPaymentCard({
required String cardNumber,
Future<Result<Map<String, dynamic>>> addPaymentCard(
/*required String cardNumber,
required String cardHolder,
required String expiryMonth,
required String expiryYear,
required String cvv,
});
required String cvv,*/
);
Future<Result<void>> setMainPaymentCard(int cardId);
Future<Result<void>> removePaymentCard(int cardId);
Future<Result<bool>> activateSubscription(int optionId);
Future<Result<bool>> activateSubscription(int optionId, int cardId);
}

View File

@@ -5,6 +5,7 @@ import '../entities/user_profile.dart';
abstract class UserProfileRepository {
Future<UserProfile> getProfile();
Future<void> deleteProfile();
Future<UserProfile?> updateProfile(UserProfile profile);
Future<int?> uploadProfilePhoto(File imageFile);
Future<UserCheckFlags?> checkUser();

View File

@@ -5,6 +5,7 @@ import 'package:be_happy/domain/entities/active_scooter_order.dart';
import '../../core/result.dart';
import '../entities/client_subscription.dart';
import '../entities/fine.dart';
import '../entities/point.dart';
import '../entities/scooter.dart';
import '../entities/subscription.dart';
@@ -27,10 +28,10 @@ abstract class ScooterRepository {
required bool isInsurance,
});
Future<Result<ScooterOrder>> startRide(int orderId);
Future<Result<ScooterOrder>> cancelRide(int orderId);
Future<Result<void>> cancelRide(int orderId);
Future<Result<ScooterOrder>> pauseRide(int orderId);
Future<Result<ScooterOrder>> resumeRide(int orderId);
Future<Result<ScooterOrder>> finishRide(int orderId, List<int> files);
Future<Result<void>> finishRide(int orderId, List<int> files);
Future<Result<void>> payRide(int orderId);
Future<Result<List<ScooterOrder>>> getClientOrders();
Future<Result<List<int>>> uploadScooterPhotos(List<File> images);
@@ -53,4 +54,14 @@ abstract class ScooterRepository {
Future<Result<List<Point>>> getScooterOrderRouteHistory(int id);
Future<Result<Fine>> getFineDetails(int orderId);
Future<Result<void>> payFine({
required int orderId,
int? cardId,
required bool isBalance,
});
Future<Result<List<Fine>>> getClientFines();
}

View File

@@ -2,5 +2,6 @@ import '../entities/zone.dart';
abstract class ZoneRepository {
Future<List<Zone>> getZones(List<double> area, int page, int pageSize);
Future<List<Zone>> getAllZones(int page, int pageSize);
}

View File

@@ -6,7 +6,7 @@ class ActivateSubscriptionUsecase {
ActivateSubscriptionUsecase(this.repository);
Future<Result<bool>> call(int optionId) {
return repository.activateSubscription(optionId);
Future<Result<bool>> call(int optionId, int cardId) {
return repository.activateSubscription(optionId, cardId);
}
}

View File

@@ -6,19 +6,19 @@ class AddPaymentCardUsecase {
AddPaymentCardUsecase(this.repository);
Future<Result<void>> call({
required String cardNumber,
Future<Result<Map<String, dynamic>>> call(
/*required String cardNumber,
required String cardHolder,
required String expiryMonth,
required String expiryYear,
required String cvv,
}) {
required String cvv,*/
) {
return repository.addPaymentCard(
cardNumber: cardNumber,
/*cardNumber: cardNumber,
cardHolder: cardHolder,
expiryMonth: expiryMonth,
expiryYear: expiryYear,
cvv: cvv,
cvv: cvv,*/
);
}
}

View File

@@ -7,7 +7,7 @@ class CancelRideUsecase {
CancelRideUsecase(this.repository);
Future<Result<ScooterOrder>> call(int orderId) {
Future<Result<void>> call(int orderId) {
return repository.cancelRide(orderId);
}
}

Some files were not shown because too many files have changed in this diff Show More