forked from eatvio/eatvio_app
Compare commits
80 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33151b7dae | ||
|
|
d53fdd1ccb | ||
|
|
e75539338b | ||
|
|
7267fc454b | ||
|
|
9e0c89c04e | ||
|
|
a9a479b2d3 | ||
|
|
12af348000 | ||
|
|
039c09962a | ||
|
|
681c11fed9 | ||
|
|
5b616a9dc1 | ||
|
|
3e5cc4f8d7 | ||
|
|
3cc7809210 | ||
|
|
7a177f7517 | ||
|
|
c718875b49 | ||
|
|
c618a6a80d | ||
|
|
3e7202d65e | ||
|
|
f78ccd01b6 | ||
|
|
a4d0fd0108 | ||
|
|
6f1368f3c6 | ||
|
|
83a39835fa | ||
|
|
49b597d5ae | ||
|
|
f5d27ed056 | ||
|
|
8a26fa0990 | ||
|
|
15a9e2b1bd | ||
|
|
7192db31e4 | ||
|
|
190e1781ee | ||
|
|
a134c2043b | ||
|
|
09a1ec0947 | ||
|
|
1db9c57140 | ||
|
|
aacbc0ce5a | ||
|
|
1bdc1c53f6 | ||
|
|
20e71a77d8 | ||
|
|
d037b6aaa7 | ||
|
|
43495f407e | ||
|
|
f0c94c4f82 | ||
| 60b0b20844 | |||
|
|
969f8d37af | ||
| d1c9c1f292 | |||
|
|
ec05b0c8fb | ||
| 5eaab6b213 | |||
|
|
5db29ae681 | ||
| b55a0d0590 | |||
|
|
44a938ea29 | ||
| be78fca7d0 | |||
|
|
955daf4933 | ||
| 8bdbb9059f | |||
|
|
27a0bcd6ce | ||
| d5e2af2de5 | |||
|
|
5a8dcafa8e | ||
| be5796fbfd | |||
|
|
02b621d85a | ||
| 683a7e1066 | |||
|
|
773359e900 | ||
|
|
74fa65a1ba | ||
|
|
46f11a8e78 | ||
|
|
3e2aff3ad4 | ||
|
|
09c53a6492 | ||
|
|
452c603b95 | ||
|
|
6bcd540d54 | ||
|
|
2c38be9fa4 | ||
|
|
74df7d82db | ||
|
|
c8d5fc756e | ||
|
|
5ac5deec48 | ||
|
|
08cf1ad370 | ||
|
|
2c59fd0923 | ||
|
|
958d09906b | ||
|
|
631efe6f9e | ||
|
|
0749a53e24 | ||
| 0982c97d66 | |||
|
|
e4168862ad | ||
| 187b056413 | |||
|
|
e546cacd0c | ||
| e1266a18f4 | |||
|
|
27f97d0e11 | ||
|
|
ea2ec05ed6 | ||
|
|
7b1574392b | ||
|
|
4426eef2dc | ||
|
|
e04c5c97f9 | ||
|
|
69412ee7ea | ||
|
|
478ac139dd |
@@ -20,6 +20,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Flutter build
|
- name: Flutter build
|
||||||
run: |
|
run: |
|
||||||
|
echo "${API_APPSTORE_KEY}" | base64 --decode > ios/AuthKey.p8
|
||||||
flutter clean
|
flutter clean
|
||||||
flutter pub get
|
flutter pub get
|
||||||
flutter pub upgrade
|
flutter pub upgrade
|
||||||
@@ -31,6 +32,8 @@ jobs:
|
|||||||
LANG: en_US.UTF-8
|
LANG: en_US.UTF-8
|
||||||
LC_ALL: en_US.UTF-8
|
LC_ALL: en_US.UTF-8
|
||||||
MATCH_PASSWORD: ${{ vars.IOS_RUNNER_CI_PASSWORD }}
|
MATCH_PASSWORD: ${{ vars.IOS_RUNNER_CI_PASSWORD }}
|
||||||
|
FASTLANE_PASSWORD: ${{ vars.FASTLANE_PASSWORD }}
|
||||||
MATCH_GIT_URL: http://gitea.office.local:3000/mso7nueh/fastlane
|
MATCH_GIT_URL: http://gitea.office.local:3000/mso7nueh/fastlane
|
||||||
FASTLANE_USER: eatvio2023@icloud.com
|
FASTLANE_USER: eatvio2023@icloud.com
|
||||||
FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: ${{ vars.FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD }}
|
FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: ${{ vars.FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD }}
|
||||||
|
API_APPSTORE_KEY: ${{ vars.API_APPSTORE_KEY }}
|
||||||
@@ -3,7 +3,7 @@ name: Eatvio App Android Test
|
|||||||
on: [ pull_request ]
|
on: [ pull_request ]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
flutter_version: "3.32.5"
|
flutter_version: "3.32.8"
|
||||||
java_version: "21.x"
|
java_version: "21.x"
|
||||||
flutter_channel: "stable"
|
flutter_channel: "stable"
|
||||||
my_local_cache_dir: "/opt/hostedtoolcache/"
|
my_local_cache_dir: "/opt/hostedtoolcache/"
|
||||||
@@ -119,7 +119,7 @@ jobs:
|
|||||||
run: flutter test
|
run: flutter test
|
||||||
|
|
||||||
- name: Build apk
|
- name: Build apk
|
||||||
run: flutter build apk --release
|
run: flutter build apk --release --dart-define=HIDE_PLANS=false
|
||||||
|
|
||||||
- name: Rename and move apk
|
- name: Rename and move apk
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -12,5 +12,9 @@
|
|||||||
/android/app/.cxx
|
/android/app/.cxx
|
||||||
*.g.dart
|
*.g.dart
|
||||||
*.freezed.dart
|
*.freezed.dart
|
||||||
|
*.gr.dart
|
||||||
|
*.mocks.dart
|
||||||
android/build/
|
android/build/
|
||||||
ios/XCBuildData/
|
ios/XCBuildData/
|
||||||
|
.env
|
||||||
|
env.example
|
||||||
|
|||||||
84
ENV_SETUP.md
Normal file
84
ENV_SETUP.md
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
# Настройка переменных окружения
|
||||||
|
|
||||||
|
Этот проект использует переменные окружения для хранения секретных данных и конфигурации.
|
||||||
|
|
||||||
|
## Установка
|
||||||
|
|
||||||
|
1. Скопируйте файл `env.example` в `.env`:
|
||||||
|
```bash
|
||||||
|
cp env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Отредактируйте файл `.env` и замените значения на ваши собственные секреты.
|
||||||
|
|
||||||
|
## Переменные окружения
|
||||||
|
|
||||||
|
### API URLs
|
||||||
|
- `BASE_URL` - основной URL API (по умолчанию: https://eatvio.ru)
|
||||||
|
- `DEV_BASE_URL` - URL API для разработки (по умолчанию: https://dev2.eatvio.com)
|
||||||
|
|
||||||
|
### Sentry Configuration
|
||||||
|
- `SENTRY_DSN` - DSN для Sentry мониторинга
|
||||||
|
|
||||||
|
### Firebase Configuration
|
||||||
|
- `FIREBASE_PROJECT_ID` - ID проекта Firebase
|
||||||
|
- `FIREBASE_DATABASE_URL` - URL базы данных Firebase
|
||||||
|
- `FIREBASE_STORAGE_BUCKET` - bucket для хранения файлов
|
||||||
|
- `FIREBASE_MESSAGING_SENDER_ID` - ID отправителя сообщений
|
||||||
|
|
||||||
|
### Firebase Android Configuration
|
||||||
|
- `FIREBASE_ANDROID_API_KEY` - API ключ для Android
|
||||||
|
- `FIREBASE_ANDROID_APP_ID` - ID приложения для Android
|
||||||
|
|
||||||
|
### Firebase iOS Configuration
|
||||||
|
- `FIREBASE_IOS_API_KEY` - API ключ для iOS
|
||||||
|
- `FIREBASE_IOS_APP_ID` - ID приложения для iOS
|
||||||
|
- `FIREBASE_IOS_BUNDLE_ID` - Bundle ID для iOS
|
||||||
|
|
||||||
|
### App Store URLs
|
||||||
|
- `APP_STORE_URL` - URL App Store
|
||||||
|
- `RUSTORE_URL` - URL RuStore
|
||||||
|
|
||||||
|
### Support URLs
|
||||||
|
- `TELEGRAM_SUPPORT_URL` - URL поддержки в Telegram
|
||||||
|
- `SUPPORT_EMAIL` - email для поддержки
|
||||||
|
|
||||||
|
### Test Credentials (только для разработки)
|
||||||
|
- `TEST_EMAIL` - тестовый email
|
||||||
|
- `TEST_PASSWORD` - тестовый пароль
|
||||||
|
|
||||||
|
### Email Validation API
|
||||||
|
- `EMAIL_VALIDATION_API_URL` - URL API для валидации email
|
||||||
|
|
||||||
|
### Connection Check URLs
|
||||||
|
- `GOOGLE_CHECK_URL` - URL для проверки соединения (Google)
|
||||||
|
- `YANDEX_CHECK_URL` - URL для проверки соединения (Yandex)
|
||||||
|
|
||||||
|
## Безопасность
|
||||||
|
|
||||||
|
⚠️ **ВАЖНО**: Файл `.env` добавлен в `.gitignore` и не должен попадать в репозиторий. Все секретные данные должны храниться только в этом файле.
|
||||||
|
|
||||||
|
## Использование в коде
|
||||||
|
|
||||||
|
Для доступа к переменным окружения используйте класс `EnvConfig`:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:eatvio_app/core/env_config.dart';
|
||||||
|
|
||||||
|
String baseUrl = EnvConfig.baseUrl;
|
||||||
|
String sentryDsn = EnvConfig.sentryDsn;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Миграция
|
||||||
|
|
||||||
|
Все секреты были перенесены из кода в переменные окружения:
|
||||||
|
|
||||||
|
1. **Sentry DSN** - из `main.dart`
|
||||||
|
2. **Firebase конфигурация** - из `firebase_options.dart`
|
||||||
|
3. **API URLs** - из `const.dart`
|
||||||
|
4. **URL для проверки соединения** - из `internet_connection_checker_screen.dart`
|
||||||
|
5. **URL для обновления приложения** - из `update_screen.dart`
|
||||||
|
6. **URL поддержки Telegram** - из `support_screen.dart`
|
||||||
|
7. **API для валидации email** - из `registration_repository.dart`
|
||||||
|
8. **Тестовые учетные данные** - из `test_cases.dart`
|
||||||
|
9. **Email поддержки** - из `support_screen.dart`
|
||||||
@@ -19,17 +19,17 @@ android {
|
|||||||
ndkVersion = "27.0.12077973"
|
ndkVersion = "27.0.12077973"
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_21
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
targetCompatibility = JavaVersion.VERSION_21
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = 21
|
jvmTarget = JavaVersion.VERSION_11.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.eatvioapp.app"
|
applicationId = "com.eatvioapp.app"
|
||||||
minSdk = flutter.minSdkVersion
|
minSdk = 23
|
||||||
targetSdk = flutter.targetSdkVersion
|
targetSdk = flutter.targetSdkVersion
|
||||||
versionCode = flutter.versionCode
|
versionCode = flutter.versionCode
|
||||||
versionName = flutter.versionName
|
versionName = flutter.versionName
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
|
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="Eatvio"
|
android:label="Итвио"
|
||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="true"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher">
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,3 +1,3 @@
|
|||||||
org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
|
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ plugins {
|
|||||||
// START: FlutterFire Configuration
|
// START: FlutterFire Configuration
|
||||||
id "com.google.gms.google-services" version "4.4.2" apply false
|
id "com.google.gms.google-services" version "4.4.2" apply false
|
||||||
// END: FlutterFire Configuration
|
// END: FlutterFire Configuration
|
||||||
id "org.jetbrains.kotlin.android" version "2.0.21" apply false
|
id "org.jetbrains.kotlin.android" version "2.1.0" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
include ":app"
|
include ":app"
|
||||||
|
|||||||
@@ -213,10 +213,14 @@
|
|||||||
inputFileListPaths = (
|
inputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
);
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
name = "[CP] Embed Pods Frameworks";
|
name = "[CP] Embed Pods Frameworks";
|
||||||
outputFileListPaths = (
|
outputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
);
|
);
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
@@ -283,10 +287,14 @@
|
|||||||
inputFileListPaths = (
|
inputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
);
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
name = "[CP] Copy Pods Resources";
|
name = "[CP] Copy Pods Resources";
|
||||||
outputFileListPaths = (
|
outputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
);
|
);
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
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-resources.sh\"\n";
|
||||||
@@ -388,7 +396,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
|
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
CURRENT_PROJECT_VERSION = 39;
|
CURRENT_PROJECT_VERSION = 64;
|
||||||
DEVELOPMENT_TEAM = VL68C36HYS;
|
DEVELOPMENT_TEAM = VL68C36HYS;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
@@ -406,7 +414,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.50;
|
MARKETING_VERSION = 1.2.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.eatvioapp.app;
|
PRODUCT_BUNDLE_IDENTIFIER = com.eatvioapp.app;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.eatvioapp.app 1747050044";
|
PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.eatvioapp.app 1747050044";
|
||||||
@@ -538,7 +546,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
|
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
CURRENT_PROJECT_VERSION = 39;
|
CURRENT_PROJECT_VERSION = 64;
|
||||||
DEVELOPMENT_TEAM = VL68C36HYS;
|
DEVELOPMENT_TEAM = VL68C36HYS;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
@@ -556,7 +564,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.50;
|
MARKETING_VERSION = 1.2.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.eatvioapp.app;
|
PRODUCT_BUNDLE_IDENTIFIER = com.eatvioapp.app;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.eatvioapp.app 1747050044";
|
PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.eatvioapp.app 1747050044";
|
||||||
@@ -576,7 +584,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
|
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
CURRENT_PROJECT_VERSION = 39;
|
CURRENT_PROJECT_VERSION = 64;
|
||||||
DEVELOPMENT_TEAM = VL68C36HYS;
|
DEVELOPMENT_TEAM = VL68C36HYS;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
@@ -594,7 +602,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.50;
|
MARKETING_VERSION = 1.2.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.eatvioapp.app;
|
PRODUCT_BUNDLE_IDENTIFIER = com.eatvioapp.app;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.eatvioapp.app 1747050044";
|
PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.eatvioapp.app 1747050044";
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>eatvio_app</string>
|
<string>Итвио</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>39</string>
|
<string>64</string>
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
|||||||
@@ -18,9 +18,16 @@ default_platform(:ios)
|
|||||||
platform :ios do
|
platform :ios do
|
||||||
desc "Push a new beta build to TestFlight"
|
desc "Push a new beta build to TestFlight"
|
||||||
lane :beta do
|
lane :beta do
|
||||||
match(type: "appstore")
|
match(type: "appstore", readonly: true)
|
||||||
increment_build_number(xcodeproj: "Runner.xcodeproj")
|
increment_build_number(xcodeproj: "Runner.xcodeproj")
|
||||||
build_app(workspace: "Runner.xcworkspace", scheme: "Runner", export_method: "app-store", codesigning_identity: "Apple Distribution")
|
build_app(
|
||||||
|
workspace: "Runner.xcworkspace",
|
||||||
|
scheme: "Runner",
|
||||||
|
export_method: "app-store",
|
||||||
|
codesigning_identity: "Apple Distribution",
|
||||||
|
build_args: "--dart-define=HIDE_PLANS=true"
|
||||||
|
)
|
||||||
|
app_store_connect_api_key(key_id: "325JS9JCZH", issuer_id: "dfa27d1d-2bb8-4d4d-847e-0b32f9b880b3", key_filepath: "./AuthKey.p8")
|
||||||
upload_to_testflight
|
upload_to_testflight
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,22 +5,24 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000206">
|
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000227">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="1: increment_build_number" time="0.195115">
|
<testcase classname="fastlane.lanes" name="1: match" time="8.292217">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="2: build_app" time="116.686863">
|
<testcase classname="fastlane.lanes" name="2: increment_build_number" time="0.201631">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="3: upload_to_testflight" time="338.642203">
|
<testcase classname="fastlane.lanes" name="3: build_app" time="9.870647">
|
||||||
|
|
||||||
|
<failure message="/opt/homebrew/Cellar/fastlane/2.228.0/libexec/gems/fastlane-2.228.0/fastlane/lib/fastlane/actions/actions_helper.rb:67:in 'Fastlane::Actions.execute_action' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/gems/fastlane-2.228.0/fastlane/lib/fastlane/runner.rb:255:in 'block in Fastlane::Runner#execute_action' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/gems/fastlane-2.228.0/fastlane/lib/fastlane/runner.rb:229:in 'Dir.chdir' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/gems/fastlane-2.228.0/fastlane/lib/fastlane/runner.rb:229:in 'Fastlane::Runner#execute_action' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/gems/fastlane-2.228.0/fastlane/lib/fastlane/runner.rb:157:in 'Fastlane::Runner#trigger_action_by_name' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/gems/fastlane-2.228.0/fastlane/lib/fastlane/fast_file.rb:159:in 'Fastlane::FastFile#method_missing' Fastfile:23:in 'block (2 levels) in Fastlane::FastFile#parsing_binding' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/gems/fastlane-2.228.0/fastlane/lib/fastlane/lane.rb:41:in 'Fastlane::Lane#call' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/gems/fastlane-2.228.0/fastlane/lib/fastlane/runner.rb:49:in 'block in Fastlane::Runner#execute' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/gems/fastlane-2.228.0/fastlane/lib/fastlane/runner.rb:45:in 'Dir.chdir' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/gems/fastlane-2.228.0/fastlane/lib/fastlane/runner.rb:45:in 'Fastlane::Runner#execute' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/gems/fastlane-2.228.0/fastlane/lib/fastlane/lane_manager.rb:46:in 'Fastlane::LaneManager.cruise_lane' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/gems/fastlane-2.228.0/fastlane/lib/fastlane/command_line_handler.rb:34:in 'Fastlane::CommandLineHandler.handle' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/gems/fastlane-2.228.0/fastlane/lib/fastlane/commands_generator.rb:110:in 'block (2 levels) in Fastlane::CommandsGenerator#run' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/gems/commander-4.6.0/lib/commander/command.rb:187:in 'Commander::Command#call' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/gems/commander-4.6.0/lib/commander/command.rb:157:in 'Commander::Command#run' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/gems/commander-4.6.0/lib/commander/runner.rb:444:in 'Commander::Runner#run_active_command' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/gems/fastlane-2.228.0/fastlane_core/lib/fastlane_core/ui/fastlane_runner.rb:124:in 'Commander::Runner#run!' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/gems/commander-4.6.0/lib/commander/delegates.rb:18:in 'Commander::Delegates#run!' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/gems/fastlane-2.228.0/fastlane/lib/fastlane/commands_generator.rb:363:in 'Fastlane::CommandsGenerator#run' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/gems/fastlane-2.228.0/fastlane/lib/fastlane/commands_generator.rb:43:in 'Fastlane::CommandsGenerator.start' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/gems/fastlane-2.228.0/fastlane/lib/fastlane/cli_tools_distributor.rb:123:in 'Fastlane::CLIToolsDistributor.take_off' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/gems/fastlane-2.228.0/bin/fastlane:23:in '<top (required)>' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/bin/fastlane:25:in 'Kernel#load' /opt/homebrew/Cellar/fastlane/2.228.0/libexec/bin/fastlane:25:in '<main>' Error building the application - see the log above" />
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import 'dart:io';
|
|
||||||
import 'package:device_info_plus/device_info_plus.dart';
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:eatvio_app/core/const.dart';
|
import 'package:eatvio_app/core/const.dart';
|
||||||
import 'package:eatvio_app/core/firebase_api.dart';
|
import 'package:eatvio_app/core/firebase_api.dart';
|
||||||
@@ -121,7 +119,9 @@ class AppRouter {
|
|||||||
GoRoute(
|
GoRoute(
|
||||||
path: 'forget-password-screen',
|
path: 'forget-password-screen',
|
||||||
name: 'forgetPasswordScreen',
|
name: 'forgetPasswordScreen',
|
||||||
builder: (context, state) => ForgetPasswordScreen(isAuth: state.extra as bool,),
|
builder: (context, state) => ForgetPasswordScreen(
|
||||||
|
isAuth: state.extra as bool,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: 'scan-qr-code-screen',
|
path: 'scan-qr-code-screen',
|
||||||
@@ -205,21 +205,9 @@ class AppRouter {
|
|||||||
builder: (context, state) => const WelcomeScreen(),
|
builder: (context, state) => const WelcomeScreen(),
|
||||||
redirect: (context, state) async {
|
redirect: (context, state) async {
|
||||||
try {
|
try {
|
||||||
if (await TokenStorage.haveTokenInStorage()) {
|
|
||||||
final firebaseApi = sl<FirebaseApi>();
|
|
||||||
final fbToken = await firebaseApi.getToken();
|
|
||||||
final deviceInfo = DeviceInfoPlugin();
|
|
||||||
await sl<Dio>().post(
|
|
||||||
'/api/profile/notification/token/register',
|
|
||||||
data: {
|
|
||||||
'platform': Platform.isIOS ? 'ios' : 'android',
|
|
||||||
'hw_id': Platform.isIOS ? (await deviceInfo.iosInfo).identifierForVendor : (await deviceInfo.androidInfo).id,
|
|
||||||
'token': fbToken,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
final sharedPreferences = await SharedPreferences.getInstance();
|
final sharedPreferences = await SharedPreferences.getInstance();
|
||||||
final welcomeScreenWasShowed = sharedPreferences.getBool('welcomeScreenWasShowed') ?? false;
|
final welcomeScreenWasShowed = sharedPreferences.getBool('welcomeScreenWasShowed') ?? false;
|
||||||
|
await sl<FirebaseApi>().registerPushToken();
|
||||||
if (welcomeScreenWasShowed == false) {
|
if (welcomeScreenWasShowed == false) {
|
||||||
return null;
|
return null;
|
||||||
} else if (await TokenStorage.haveTokenInStorage()) {
|
} else if (await TokenStorage.haveTokenInStorage()) {
|
||||||
@@ -228,7 +216,7 @@ class AppRouter {
|
|||||||
return '/auth-screen';
|
return '/auth-screen';
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
rethrow;
|
return '/feed-screen';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -373,10 +361,10 @@ class AppRouter {
|
|||||||
name: 'chatScreen',
|
name: 'chatScreen',
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final data = state.extra as Map<String, dynamic>;
|
final data = state.extra as Map<String, dynamic>;
|
||||||
final chatId = data['chatId'] as String;
|
final uuid = data['uuid'] as String;
|
||||||
final sendInitialMessage = data['sendInitialMessage'] as bool?;
|
final sendInitialMessage = data['sendInitialMessage'] as bool?;
|
||||||
return ChatScreen(
|
return ChatScreen(
|
||||||
chatId: chatId,
|
uuid: uuid,
|
||||||
sendInitialMessage: sendInitialMessage,
|
sendInitialMessage: sendInitialMessage,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ void showCommentsActionsBottomSheet(
|
|||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
builder: (context) => Container(
|
builder: (context) => Container(
|
||||||
margin: EdgeInsets.only(left: 8, right: 8, bottom: MediaQuery.of(context).padding.bottom),
|
margin: EdgeInsets.only(left: 8, right: 8, bottom: MediaQuery.of(context).padding.bottom + 20),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
color: appTheme.whiteA700,
|
color: appTheme.whiteA700,
|
||||||
@@ -189,11 +189,12 @@ class _ActionWidget extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Expanded(
|
||||||
text,
|
child: Text(
|
||||||
style: TextStyles.textLMedium.copyWith(color: appTheme.black),
|
text,
|
||||||
|
style: TextStyles.textLMedium.copyWith(color: appTheme.black),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
|
||||||
SvgPicture.asset(iconPath)
|
SvgPicture.asset(iconPath)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
String baseUrl = 'https://eatvio.ru';
|
import 'package:eatvio_app/core/env_config.dart';
|
||||||
// String baseUrl = 'https://dev2.eatvio.com';
|
|
||||||
|
String baseUrl = EnvConfig.baseUrl;
|
||||||
|
bool hidePlans = const bool.fromEnvironment('HIDE_PLANS', defaultValue: false);
|
||||||
|
|
||||||
const List<int> durationValues = [7, 14, 21];
|
const List<int> durationValues = [7, 14, 21];
|
||||||
|
|
||||||
|
|||||||
43
lib/core/env_config.dart
Normal file
43
lib/core/env_config.dart
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||||
|
|
||||||
|
class EnvConfig {
|
||||||
|
static String get baseUrl => dotenv.env['BASE_URL'] ?? 'https://eatvio.ru';
|
||||||
|
static String get devBaseUrl => dotenv.env['DEV_BASE_URL'] ?? 'https://dev2.eatvio.com';
|
||||||
|
|
||||||
|
// Sentry Configuration
|
||||||
|
static String get sentryDsn => dotenv.env['SENTRY_DSN'] ?? 'http://ba7f2653067776f3034e6e89850e85a1@sentry.md-desk.ru:9000/2';
|
||||||
|
|
||||||
|
// Firebase Configuration
|
||||||
|
static String get firebaseProjectId => dotenv.env['FIREBASE_PROJECT_ID'] ?? 'eatvio-3467b';
|
||||||
|
static String get firebaseDatabaseUrl => dotenv.env['FIREBASE_DATABASE_URL'] ?? 'https://eatvio-3467b-default-rtdb.europe-west1.firebasedatabase.app';
|
||||||
|
static String get firebaseStorageBucket => dotenv.env['FIREBASE_STORAGE_BUCKET'] ?? 'eatvio-3467b.firebasestorage.app';
|
||||||
|
static String get firebaseMessagingSenderId => dotenv.env['FIREBASE_MESSAGING_SENDER_ID'] ?? '613980912922';
|
||||||
|
|
||||||
|
// Firebase Android Configuration
|
||||||
|
static String get firebaseAndroidApiKey => dotenv.env['FIREBASE_ANDROID_API_KEY'] ?? 'AIzaSyBUe1UqAAMfgMcd4hWdRoLF1Os-yn-HE9k';
|
||||||
|
static String get firebaseAndroidAppId => dotenv.env['FIREBASE_ANDROID_APP_ID'] ?? '1:613980912922:android:4de95e5f2d50ba69ecc0a3';
|
||||||
|
|
||||||
|
// Firebase iOS Configuration
|
||||||
|
static String get firebaseIosApiKey => dotenv.env['FIREBASE_IOS_API_KEY'] ?? 'AIzaSyBnE8tQjL9_1HuXrTnqNy6qbXpoenO36yQ';
|
||||||
|
static String get firebaseIosAppId => dotenv.env['FIREBASE_IOS_APP_ID'] ?? '1:613980912922:ios:c557d140a1212e63ecc0a3';
|
||||||
|
static String get firebaseIosBundleId => dotenv.env['FIREBASE_IOS_BUNDLE_ID'] ?? 'com.eatvioapp.app';
|
||||||
|
|
||||||
|
// App Store URLs
|
||||||
|
static String get appStoreUrl => dotenv.env['APP_STORE_URL'] ?? 'https://apps.apple.com/app/idYOUR_APP_ID';
|
||||||
|
static String get rustoreUrl => dotenv.env['RUSTORE_URL'] ?? 'https://www.rustore.ru/catalog/app/com.eatvioapp.app';
|
||||||
|
|
||||||
|
// Telegram Support
|
||||||
|
static String get telegramSupportUrl => dotenv.env['TELEGRAM_SUPPORT_URL'] ?? 'https://t.me/eatvio';
|
||||||
|
static String get supportEmail => dotenv.env['SUPPORT_EMAIL'] ?? 'help@eatv.io';
|
||||||
|
|
||||||
|
// Test Credentials (for development only)
|
||||||
|
static String get testEmail => dotenv.env['TEST_EMAIL'] ?? 'reshetnick.mike+user1@gmail.com';
|
||||||
|
static String get testPassword => dotenv.env['TEST_PASSWORD'] ?? '1234567890';
|
||||||
|
|
||||||
|
// Email Validation API
|
||||||
|
static String get emailValidationApiUrl => dotenv.env['EMAIL_VALIDATION_API_URL'] ?? 'https://api.validate.email/validate';
|
||||||
|
|
||||||
|
// Connection Check URLs
|
||||||
|
static String get googleCheckUrl => dotenv.env['GOOGLE_CHECK_URL'] ?? 'https://google.com';
|
||||||
|
static String get yandexCheckUrl => dotenv.env['YANDEX_CHECK_URL'] ?? 'https://yandex.ru';
|
||||||
|
}
|
||||||
@@ -1,3 +1,10 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:eatvio_app/core/locator_service.dart';
|
||||||
|
import 'package:eatvio_app/core/token_storage.dart';
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
|
|
||||||
class FirebaseApi {
|
class FirebaseApi {
|
||||||
@@ -7,4 +14,22 @@ class FirebaseApi {
|
|||||||
await _firebaseMessaging.requestPermission();
|
await _firebaseMessaging.requestPermission();
|
||||||
return await _firebaseMessaging.getToken();
|
return await _firebaseMessaging.getToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> registerPushToken() async {
|
||||||
|
if (await TokenStorage.haveTokenInStorage()) {
|
||||||
|
try {
|
||||||
|
final deviceInfo = DeviceInfoPlugin();
|
||||||
|
await sl<Dio>().post(
|
||||||
|
'/api/profile/notification/token/register',
|
||||||
|
data: {
|
||||||
|
'platform': Platform.isIOS ? 'ios' : 'android',
|
||||||
|
'hw_id': Platform.isIOS ? (await deviceInfo.iosInfo).identifierForVendor : (await deviceInfo.androidInfo).id,
|
||||||
|
'token': await getToken(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} catch (e, st) {
|
||||||
|
log(e.toString(), stackTrace: st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -244,7 +244,6 @@ Future<void> init() async {
|
|||||||
'accept': 'application/json',
|
'accept': 'application/json',
|
||||||
'content-type': 'application/json',
|
'content-type': 'application/json',
|
||||||
},
|
},
|
||||||
validateStatus: (status) => status != null && status < 500,
|
|
||||||
),
|
),
|
||||||
)..interceptors.addAll(
|
)..interceptors.addAll(
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:eatvio_app/core/widgets/show_error_dialog.dart';
|
import 'package:eatvio_app/core/widgets/show_error_dialog.dart';
|
||||||
import 'package:eatvio_app/generated/l10n.dart';
|
import 'package:eatvio_app/generated/l10n.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
@@ -11,6 +13,10 @@ class PermissionHandler {
|
|||||||
required Future<void> Function() onGranted,
|
required Future<void> Function() onGranted,
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
}) async {
|
}) async {
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
await onGranted();
|
||||||
|
return;
|
||||||
|
}
|
||||||
final permissionState = await permission.request();
|
final permissionState = await permission.request();
|
||||||
if (permissionState.isGranted) {
|
if (permissionState.isGranted) {
|
||||||
await onGranted();
|
await onGranted();
|
||||||
|
|||||||
@@ -21,3 +21,25 @@ abstract class TokenStorage {
|
|||||||
|
|
||||||
static Future<void> removeTokenInStorage() async => (await const FlutterSecureStorage().delete(key: 'token'));
|
static Future<void> removeTokenInStorage() async => (await const FlutterSecureStorage().delete(key: 'token'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class SocketIdStorage {
|
||||||
|
static Future<bool> haveSocketIdInStorage() async {
|
||||||
|
try {
|
||||||
|
if (await const FlutterSecureStorage().read(key: 'socketId') == null) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<String?> getSocketIdFromStorage() async => await const FlutterSecureStorage().read(key: 'socketId');
|
||||||
|
|
||||||
|
static Future<void> saveSocketIdInStorage(String socketId) async {
|
||||||
|
await const FlutterSecureStorage().write(key: 'socketId', value: socketId);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> removeSocketIdInStorage() async => (await const FlutterSecureStorage().delete(key: 'socketId'));
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:convert';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:dart_pusher_channels/dart_pusher_channels.dart';
|
import 'package:dart_pusher_channels/dart_pusher_channels.dart';
|
||||||
@@ -59,6 +60,24 @@ class WsManager {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
String? extractSocketId(String? data) {
|
||||||
|
if (data == null) return null;
|
||||||
|
try {
|
||||||
|
final decoded = jsonDecode(data);
|
||||||
|
return decoded['socket_id'] as String?;
|
||||||
|
} catch (_) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client?.eventStream.listen((event) async {
|
||||||
|
if (event.name == 'pusher:connection_established') {
|
||||||
|
final data = event.data;
|
||||||
|
final socketId = extractSocketId(data);
|
||||||
|
await SocketIdStorage.saveSocketIdInStorage(socketId!);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
await client?.connect();
|
await client?.connect();
|
||||||
|
|
||||||
client = client;
|
client = client;
|
||||||
@@ -77,9 +96,8 @@ class WsManager {
|
|||||||
EasyDebounce.debounce(
|
EasyDebounce.debounce(
|
||||||
'searchChatRoom',
|
'searchChatRoom',
|
||||||
const Duration(milliseconds: 300),
|
const Duration(milliseconds: 300),
|
||||||
() => sl<ChatsCubit>().getChatRooms(),
|
() => sl<ChatsCubit>().getChatRooms(),
|
||||||
);
|
);
|
||||||
log(event.rootObject.toString());
|
|
||||||
});
|
});
|
||||||
_subscribe();
|
_subscribe();
|
||||||
} catch (e, st) {
|
} catch (e, st) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:eatvio_app/core/app_router.dart';
|
import 'package:eatvio_app/core/app_router.dart';
|
||||||
import 'package:eatvio_app/core/const.dart';
|
import 'package:eatvio_app/core/const.dart';
|
||||||
|
import 'package:eatvio_app/core/firebase_api.dart';
|
||||||
import 'package:eatvio_app/core/locator_service.dart';
|
import 'package:eatvio_app/core/locator_service.dart';
|
||||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||||
import 'package:eatvio_app/core/utils/image_constant.dart';
|
import 'package:eatvio_app/core/utils/image_constant.dart';
|
||||||
@@ -66,6 +67,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
await sl<ChatsCubit>().connectToWebSocket();
|
await sl<ChatsCubit>().connectToWebSocket();
|
||||||
await sl<NotificationsCubit>().getNotifications();
|
await sl<NotificationsCubit>().getNotifications();
|
||||||
await sl<NotificationsCubit>().connectToWebSocket();
|
await sl<NotificationsCubit>().connectToWebSocket();
|
||||||
|
await sl<FirebaseApi>().registerPushToken();
|
||||||
showSuccessToast('Успешный вход');
|
showSuccessToast('Успешный вход');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
rethrow;
|
rethrow;
|
||||||
@@ -79,6 +81,9 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
var shortestSide = MediaQuery.sizeOf(context).shortestSide;
|
||||||
|
final bool useMobileLayout = shortestSide < 600;
|
||||||
|
if (!useMobileLayout) isFocused = true;
|
||||||
final s = S.of(context);
|
final s = S.of(context);
|
||||||
FlutterNativeSplash.remove();
|
FlutterNativeSplash.remove();
|
||||||
return BlocProvider<AuthCubit>(
|
return BlocProvider<AuthCubit>(
|
||||||
|
|||||||
@@ -22,13 +22,9 @@ abstract class ChatRoomModel with _$ChatRoomModel {
|
|||||||
@freezed
|
@freezed
|
||||||
abstract class Users with _$Users {
|
abstract class Users with _$Users {
|
||||||
const factory Users({
|
const factory Users({
|
||||||
|
|
||||||
@JsonKey(name: 'from_chat_user') required ChatUserModel sender,
|
@JsonKey(name: 'from_chat_user') required ChatUserModel sender,
|
||||||
@JsonKey(name: 'to_chat_user') required ChatUserModel receiver,
|
@JsonKey(name: 'to_chat_user') required ChatUserModel receiver,
|
||||||
|
|
||||||
}) = _Users;
|
}) = _Users;
|
||||||
|
|
||||||
factory Users.fromJson(Map<String, Object?> json) => _$UsersFromJson(json);
|
factory Users.fromJson(Map<String, Object?> json) => _$UsersFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:eatvio_app/core/models/pagination_model.dart';
|
||||||
import 'package:eatvio_app/features/chat/models/chat_message_model.dart';
|
import 'package:eatvio_app/features/chat/models/chat_message_model.dart';
|
||||||
import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart';
|
import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
@@ -8,29 +9,18 @@ part 'chat_room_pagination_model.g.dart';
|
|||||||
@freezed
|
@freezed
|
||||||
abstract class ChatRoomPaginationModel with _$ChatRoomPaginationModel {
|
abstract class ChatRoomPaginationModel with _$ChatRoomPaginationModel {
|
||||||
const factory ChatRoomPaginationModel({
|
const factory ChatRoomPaginationModel({
|
||||||
@JsonKey(name: "chatRooms") required ChatRooms chatRooms,
|
@JsonKey(name: "data") required List<ChatRoom> data,
|
||||||
@JsonKey(name: "number_of_unread_chat_rooms") required int unreadChatsCount,
|
@JsonKey(name: "number_of_unread_chat_rooms") required int unreadChatsCount,
|
||||||
|
required Meta meta,
|
||||||
}) = _ChatRoomPreviewModel;
|
}) = _ChatRoomPreviewModel;
|
||||||
|
|
||||||
factory ChatRoomPaginationModel.fromJson(Map<String, dynamic> json) => _$ChatRoomPaginationModelFromJson(json);
|
factory ChatRoomPaginationModel.fromJson(Map<String, dynamic> json) => _$ChatRoomPaginationModelFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
|
||||||
abstract class ChatRooms with _$ChatRooms {
|
|
||||||
const factory ChatRooms({
|
|
||||||
@JsonKey(name: "data") required List<ChatRoom> data,
|
|
||||||
@JsonKey(name: "last_page") required int lastPage,
|
|
||||||
@JsonKey(name: "current_page") required int currentPage,
|
|
||||||
}) = _ChatRooms;
|
|
||||||
|
|
||||||
factory ChatRooms.fromJson(Map<String, dynamic> json) => _$ChatRoomsFromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
abstract class ChatRoom with _$ChatRoom {
|
abstract class ChatRoom with _$ChatRoom {
|
||||||
const factory ChatRoom({
|
const factory ChatRoom({
|
||||||
@JsonKey(name: "id") required String id,
|
@JsonKey(name: "id") required String id,
|
||||||
@JsonKey(name: "room_name") required String roomName,
|
|
||||||
@JsonKey(name: "last_message", fromJson: parseLastMessage) required ChatMessageModel? lastMessage,
|
@JsonKey(name: "last_message", fromJson: parseLastMessage) required ChatMessageModel? lastMessage,
|
||||||
@JsonKey(name: "users") required Users users,
|
@JsonKey(name: "users") required Users users,
|
||||||
@JsonKey(name: "unread_messages_count") required int unreadMessagesCount,
|
@JsonKey(name: "unread_messages_count") required int unreadMessagesCount,
|
||||||
|
|||||||
@@ -6,11 +6,8 @@ part 'chat_room_settings_model.g.dart';
|
|||||||
@freezed
|
@freezed
|
||||||
abstract class ChatRoomSettingsModel with _$ChatRoomSettingsModel {
|
abstract class ChatRoomSettingsModel with _$ChatRoomSettingsModel {
|
||||||
const factory ChatRoomSettingsModel({
|
const factory ChatRoomSettingsModel({
|
||||||
required int id,
|
@JsonKey(name: 'room_name') required String name,
|
||||||
@JsonKey(name: 'room_name') required String roomName,
|
|
||||||
required String state,
|
required String state,
|
||||||
@JsonKey(name: 'access_level') required String accessLevel,
|
|
||||||
@JsonKey(name: 'updated_at') required String updatedAt,
|
|
||||||
}) = _ChatRoomSettingsModel;
|
}) = _ChatRoomSettingsModel;
|
||||||
|
|
||||||
factory ChatRoomSettingsModel.fromJson(Map<String, Object?> json) => _$ChatRoomSettingsModelFromJson(json);
|
factory ChatRoomSettingsModel.fromJson(Map<String, Object?> json) => _$ChatRoomSettingsModelFromJson(json);
|
||||||
|
|||||||
@@ -47,14 +47,14 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
|
|||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> loadNextPage(String chatId) async {
|
Future<void> loadNextPage(String uuid) async {
|
||||||
if (currentPage < lastPage && !isLoading) {
|
if (currentPage < lastPage && !isLoading) {
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
|
|
||||||
emit((state as ChatRoomStateLoaded).copyWith(isLoading: isLoading));
|
emit((state as ChatRoomStateLoaded).copyWith(isLoading: isLoading));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final chatMessages = await chatRepository.getChatMessages(chatId, currentPage + 1);
|
final chatMessages = await chatRepository.getChatMessages(uuid, currentPage + 1);
|
||||||
|
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
emit(
|
emit(
|
||||||
@@ -89,17 +89,19 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initChat(String chatId, {bool sendInitialMessage = false}) async {
|
Future<void> initChat(
|
||||||
|
String uuid, {
|
||||||
|
bool sendInitialMessage = false,
|
||||||
|
}) async {
|
||||||
try {
|
try {
|
||||||
final chatRoom = await chatRepository.getChatRoom(chatId);
|
final chatRoom = await chatRepository.getChatRoom(uuid);
|
||||||
ChosenPlanModel? chosenPlan;
|
ChosenPlanModel? chosenPlan;
|
||||||
if (chatRoom.chosenPlanId != null) {
|
if (chatRoom.chosenPlanId != null) {
|
||||||
chosenPlan = await chatRepository.getChosenPlan(id: chatRoom.chosenPlanId!);
|
chosenPlan = await chatRepository.getChosenPlan(id: chatRoom.chosenPlanId!);
|
||||||
}
|
}
|
||||||
final chatMessages = await chatRepository.getChatMessages(chatId, currentPage);
|
final chatMessages = await chatRepository.getChatMessages(uuid, currentPage);
|
||||||
lastPage = chatMessages.meta.lastPage;
|
lastPage = chatMessages.meta.lastPage;
|
||||||
|
await _subscribeToChatChannel(uuid: uuid, chatId: chatRoom.id);
|
||||||
await _subscribeToChatChannel(chatId: chatId);
|
|
||||||
emit(
|
emit(
|
||||||
ChatRoomState.loaded(
|
ChatRoomState.loaded(
|
||||||
chatRoom: chatRoom.copyWith(chosenPlan: chosenPlan),
|
chatRoom: chatRoom.copyWith(chosenPlan: chosenPlan),
|
||||||
@@ -158,14 +160,14 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _subscribeToChatChannel({required String chatId}) async {
|
Future<void> _subscribeToChatChannel({required String uuid, required String chatId}) async {
|
||||||
final client = wsManager.client;
|
final client = wsManager.client;
|
||||||
if (client == null) return;
|
if (client == null) return;
|
||||||
await wsManager.initPrivateDirectChatChannel(chatId);
|
await wsManager.initPrivateDirectChatChannel(chatId);
|
||||||
wsManager.subscribeDirectChatChannel();
|
wsManager.subscribeDirectChatChannel();
|
||||||
messagesSubscription = wsManager.directChatStream?.listen((event) {
|
messagesSubscription = wsManager.directChatStream?.listen((event) {
|
||||||
AudioPlayer().play((AssetSource('audio/recive_message.wav')));
|
AudioPlayer().play((AssetSource('audio/recive_message.wav')));
|
||||||
final message = ChatMessageModel.fromJson(jsonDecode(event.data)['message']);
|
final message = ChatMessageModel.fromJson(jsonDecode(event.data));
|
||||||
List<ChatMessageModel> messagesList = [message, ...(state as ChatRoomStateLoaded).chatMessages];
|
List<ChatMessageModel> messagesList = [message, ...(state as ChatRoomStateLoaded).chatMessages];
|
||||||
chatRepository.emitNewMessageToChats(message);
|
chatRepository.emitNewMessageToChats(message);
|
||||||
emit((state as ChatRoomStateLoaded).copyWith(chatMessages: messagesList, isLoading: isLoading));
|
emit((state as ChatRoomStateLoaded).copyWith(chatMessages: messagesList, isLoading: isLoading));
|
||||||
@@ -179,7 +181,15 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
|
|||||||
typingSubscription = wsManager.clientTypingStream?.listen((event) {
|
typingSubscription = wsManager.clientTypingStream?.listen((event) {
|
||||||
emit((state as ChatRoomStateLoaded).copyWith(isTyping: true, isLoading: isLoading));
|
emit((state as ChatRoomStateLoaded).copyWith(isTyping: true, isLoading: isLoading));
|
||||||
EasyDebounce.debounce(
|
EasyDebounce.debounce(
|
||||||
'client-typing', const Duration(seconds: 3), () => emit((state as ChatRoomStateLoaded).copyWith(isTyping: false, isLoading: isLoading)));
|
'client-typing',
|
||||||
|
const Duration(seconds: 3),
|
||||||
|
() => emit(
|
||||||
|
(state as ChatRoomStateLoaded).copyWith(
|
||||||
|
isTyping: false,
|
||||||
|
isLoading: isLoading,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,12 +199,31 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
|
|||||||
required String userSlug,
|
required String userSlug,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
|
final compressedFiles = files != null ? await ImageCompressUtility.compressFilesList(List<File>.from(files)) : null;
|
||||||
|
AudioPlayer().play((AssetSource('audio/send_message.wav')));
|
||||||
|
emit(
|
||||||
|
(state as ChatRoomStateLoaded).copyWith(
|
||||||
|
chatMessages: [
|
||||||
|
ChatMessageModel(
|
||||||
|
id: 1,
|
||||||
|
data: data ?? '',
|
||||||
|
mediaData: compressedFiles,
|
||||||
|
authorId: 1,
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
isRead: true,
|
||||||
|
isCurrentUserAuthor: true,
|
||||||
|
roomId: '',
|
||||||
|
),
|
||||||
|
...(state as ChatRoomStateLoaded).chatMessages,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
await chatRepository.sendMessage(
|
await chatRepository.sendMessage(
|
||||||
data: data,
|
data: data,
|
||||||
files: files != null ? await ImageCompressUtility.compressFilesList(List<File>.from(files)) : null,
|
files: compressedFiles,
|
||||||
slug: userSlug,
|
slug: userSlug,
|
||||||
);
|
);
|
||||||
AudioPlayer().play((AssetSource('audio/send_message.wav')));
|
sl<ChatsCubit>().getChatRooms();
|
||||||
return true;
|
return true;
|
||||||
} catch (e, st) {
|
} catch (e, st) {
|
||||||
log(e.toString(), stackTrace: st);
|
log(e.toString(), stackTrace: st);
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ class ChatsCubit extends Cubit<ChatsState> {
|
|||||||
String? _query;
|
String? _query;
|
||||||
int page = 1;
|
int page = 1;
|
||||||
late int lastPage;
|
late int lastPage;
|
||||||
|
|
||||||
Future<void> connectToWebSocket() async {
|
Future<void> connectToWebSocket() async {
|
||||||
await newIncomingMessagesSubscription?.cancel();
|
await newIncomingMessagesSubscription?.cancel();
|
||||||
newIncomingMessagesSubscription = wsManager.newMessagesStream?.listen((event) {
|
newIncomingMessagesSubscription = wsManager.newMessagesStream?.listen((event) {
|
||||||
@@ -43,10 +44,10 @@ class ChatsCubit extends Cubit<ChatsState> {
|
|||||||
try {
|
try {
|
||||||
final chatRoomsPaginationModel = await chatRepository.getChatRooms(query: query);
|
final chatRoomsPaginationModel = await chatRepository.getChatRooms(query: query);
|
||||||
final unreadChats = chatRoomsPaginationModel.unreadChatsCount;
|
final unreadChats = chatRoomsPaginationModel.unreadChatsCount;
|
||||||
lastPage = chatRoomsPaginationModel.chatRooms.lastPage + 1;
|
lastPage = chatRoomsPaginationModel.meta.lastPage + 1;
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
chatRooms: chatRoomsPaginationModel.chatRooms.data,
|
chatRooms: chatRoomsPaginationModel.data,
|
||||||
chatsWithUnreadMessagesCount: unreadChats,
|
chatsWithUnreadMessagesCount: unreadChats,
|
||||||
state: BaseState.success,
|
state: BaseState.success,
|
||||||
),
|
),
|
||||||
@@ -65,10 +66,10 @@ class ChatsCubit extends Cubit<ChatsState> {
|
|||||||
try {
|
try {
|
||||||
final chatRoomsPaginationModel = await chatRepository.getChatRooms(query: query);
|
final chatRoomsPaginationModel = await chatRepository.getChatRooms(query: query);
|
||||||
final unreadChats = chatRoomsPaginationModel.unreadChatsCount;
|
final unreadChats = chatRoomsPaginationModel.unreadChatsCount;
|
||||||
lastPage = chatRoomsPaginationModel.chatRooms.lastPage + 1;
|
lastPage = chatRoomsPaginationModel.meta.lastPage + 1;
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
chatRooms: chatRoomsPaginationModel.chatRooms.data,
|
chatRooms: chatRoomsPaginationModel.data,
|
||||||
chatsWithUnreadMessagesCount: unreadChats,
|
chatsWithUnreadMessagesCount: unreadChats,
|
||||||
state: BaseState.success,
|
state: BaseState.success,
|
||||||
),
|
),
|
||||||
@@ -90,7 +91,7 @@ class ChatsCubit extends Cubit<ChatsState> {
|
|||||||
final unreadChats = chatRoomsPaginationModel.unreadChatsCount;
|
final unreadChats = chatRoomsPaginationModel.unreadChatsCount;
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
chatRooms: [...state.chatRooms, ...chatRoomsPaginationModel.chatRooms.data],
|
chatRooms: [...state.chatRooms, ...chatRoomsPaginationModel.data],
|
||||||
chatsWithUnreadMessagesCount: unreadChats,
|
chatsWithUnreadMessagesCount: unreadChats,
|
||||||
state: BaseState.success,
|
state: BaseState.success,
|
||||||
isLoadingMoreChats: false,
|
isLoadingMoreChats: false,
|
||||||
|
|||||||
@@ -32,10 +32,10 @@ import 'package:intl/intl.dart';
|
|||||||
import 'package:visibility_detector/visibility_detector.dart';
|
import 'package:visibility_detector/visibility_detector.dart';
|
||||||
|
|
||||||
class ChatScreen extends StatefulWidget {
|
class ChatScreen extends StatefulWidget {
|
||||||
final String chatId;
|
final String uuid;
|
||||||
final bool? sendInitialMessage;
|
final bool? sendInitialMessage;
|
||||||
|
|
||||||
const ChatScreen({required this.chatId, super.key, required this.sendInitialMessage});
|
const ChatScreen({required this.uuid, super.key, required this.sendInitialMessage});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ChatScreen> createState() => _ChatScreenState();
|
State<ChatScreen> createState() => _ChatScreenState();
|
||||||
@@ -64,14 +64,14 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
scrollController.addListener(_onScroll);
|
scrollController.addListener(_onScroll);
|
||||||
chatRoomCubit.initChat(widget.chatId, sendInitialMessage: widget.sendInitialMessage ?? false);
|
chatRoomCubit.initChat(widget.uuid, sendInitialMessage: widget.sendInitialMessage ?? false);
|
||||||
chatRoomCubit.setTimer();
|
chatRoomCubit.setTimer();
|
||||||
_listener = AppLifecycleListener(onHide: _onHide, onResume: _onResume);
|
_listener = AppLifecycleListener(onHide: _onHide, onResume: _onResume);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onScroll() {
|
void _onScroll() {
|
||||||
if (scrollController.offset >= scrollController.position.maxScrollExtent && !scrollController.position.outOfRange) {
|
if (scrollController.offset >= scrollController.position.maxScrollExtent && !scrollController.position.outOfRange) {
|
||||||
chatRoomCubit.loadNextPage(widget.chatId);
|
chatRoomCubit.loadNextPage(widget.uuid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
if (state is ChatRoomStateLoaded) {
|
if (state is ChatRoomStateLoaded) {
|
||||||
return _buildChat(context, state);
|
return _buildChat(context, state);
|
||||||
} else if (state is ChatRoomStateError) {
|
} else if (state is ChatRoomStateError) {
|
||||||
return Scaffold(appBar: AppBar(), body: DataErrorWidget(onReload: () => chatRoomCubit.initChat(widget.chatId)));
|
return Scaffold(appBar: AppBar(), body: DataErrorWidget(onReload: () => chatRoomCubit.initChat(widget.uuid)));
|
||||||
} else {
|
} else {
|
||||||
return const Scaffold(body: Loader());
|
return const Scaffold(body: Loader());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ class _EmptyChatScreenState extends State<EmptyChatScreen> {
|
|||||||
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
||||||
'chatScreen',
|
'chatScreen',
|
||||||
extra: {
|
extra: {
|
||||||
'chatId': chatIdOrError,
|
'uuid': chatIdOrError,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -186,7 +186,7 @@ class _EmptyChatScreenState extends State<EmptyChatScreen> {
|
|||||||
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
||||||
'chatScreen',
|
'chatScreen',
|
||||||
extra: {
|
extra: {
|
||||||
'chatId': chatIdOrError,
|
'uuid': chatIdOrError,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -214,7 +214,7 @@ class _EmptyChatScreenState extends State<EmptyChatScreen> {
|
|||||||
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
||||||
'chatScreen',
|
'chatScreen',
|
||||||
extra: {
|
extra: {
|
||||||
'chatId': chatIdOrError,
|
'uuid': chatIdOrError,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -264,7 +264,7 @@ class _EmptyChatScreenState extends State<EmptyChatScreen> {
|
|||||||
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
||||||
'chatScreen',
|
'chatScreen',
|
||||||
extra: {
|
extra: {
|
||||||
'chatId': chatIdOrError,
|
'uuid': chatIdOrError,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:dismissible_page/dismissible_page.dart';
|
import 'package:dismissible_page/dismissible_page.dart';
|
||||||
import 'package:eatvio_app/core/const.dart';
|
import 'package:eatvio_app/core/const.dart';
|
||||||
@@ -11,7 +9,7 @@ import 'package:photo_view/photo_view_gallery.dart';
|
|||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class PhotoViewScreen extends StatefulWidget {
|
class PhotoViewScreen extends StatefulWidget {
|
||||||
final List<String> imageUrls;
|
final List<dynamic> imageUrls;
|
||||||
final int index;
|
final int index;
|
||||||
final bool isLocal;
|
final bool isLocal;
|
||||||
|
|
||||||
@@ -50,14 +48,12 @@ class _PhotoViewScreenState extends State<PhotoViewScreen> {
|
|||||||
),
|
),
|
||||||
scrollPhysics: const BouncingScrollPhysics(),
|
scrollPhysics: const BouncingScrollPhysics(),
|
||||||
builder: (BuildContext context, int index) {
|
builder: (BuildContext context, int index) {
|
||||||
|
final imageProvider = widget.isLocal
|
||||||
|
? FileImage(widget.imageUrls[index]) as ImageProvider<Object>
|
||||||
|
: CachedNetworkImageProvider(widget.imageUrls[index]) as ImageProvider<Object>;
|
||||||
|
|
||||||
return PhotoViewGalleryPageOptions(
|
return PhotoViewGalleryPageOptions(
|
||||||
imageProvider: widget.isLocal
|
imageProvider: imageProvider,
|
||||||
? FileImage(
|
|
||||||
File(widget.imageUrls[index]),
|
|
||||||
) as ImageProvider
|
|
||||||
: CachedNetworkImageProvider(
|
|
||||||
widget.imageUrls[index],
|
|
||||||
),
|
|
||||||
initialScale: PhotoViewComputedScale.contained,
|
initialScale: PhotoViewComputedScale.contained,
|
||||||
minScale: PhotoViewComputedScale.contained,
|
minScale: PhotoViewComputedScale.contained,
|
||||||
heroAttributes: PhotoViewHeroAttributes(
|
heroAttributes: PhotoViewHeroAttributes(
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:dismissible_page/dismissible_page.dart';
|
import 'package:dismissible_page/dismissible_page.dart';
|
||||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||||
@@ -59,8 +61,9 @@ class ChatBubbleWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding:
|
padding: mediaData.isNotEmpty
|
||||||
mediaData.isNotEmpty ? const EdgeInsets.all(8) : EdgeInsets.only(top: 8, bottom: 8, left: isCurrentUser ? 16 : 8, right: isCurrentUser ? 8 : 16),
|
? const EdgeInsets.all(8)
|
||||||
|
: EdgeInsets.only(top: 8, bottom: 8, left: isCurrentUser ? 16 : 8, right: isCurrentUser ? 8 : 16),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: isCurrentUser ? CrossAxisAlignment.end : CrossAxisAlignment.start,
|
crossAxisAlignment: isCurrentUser ? CrossAxisAlignment.end : CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@@ -86,17 +89,17 @@ class ChatBubbleWidget extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
leading: const Icon(
|
leading: const Icon(
|
||||||
Icons.file_copy_outlined,
|
Icons.file_copy_outlined,
|
||||||
color: Colors.white,
|
color: Colors.black,
|
||||||
),
|
),
|
||||||
trailing: const Icon(
|
trailing: const Icon(
|
||||||
Icons.download_outlined,
|
Icons.download_outlined,
|
||||||
color: Colors.white,
|
color: Colors.black,
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
mediaData[index].toString().replaceAll('chat_files/', ''),
|
mediaData[index].toString().replaceAll('chat_files/', ''),
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.black,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@@ -107,8 +110,9 @@ class ChatBubbleWidget extends StatelessWidget {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
context.pushTransparentRoute(
|
context.pushTransparentRoute(
|
||||||
PhotoViewScreen(
|
PhotoViewScreen(
|
||||||
imageUrls: mediaData.map((e) => '$e').toList(),
|
imageUrls: mediaData,
|
||||||
index: index,
|
index: index,
|
||||||
|
isLocal: mediaData[index] is File ? true : false,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -117,9 +121,11 @@ class ChatBubbleWidget extends StatelessWidget {
|
|||||||
transitionOnUserGestures: true,
|
transitionOnUserGestures: true,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
child: CachedNetworkImage(
|
child: mediaData[index] is File
|
||||||
imageUrl: '${mediaData[index]}',
|
? Image.file(mediaData[index])
|
||||||
),
|
: CachedNetworkImage(
|
||||||
|
imageUrl: '${mediaData[index]}',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class _ChatEntryWidgetState extends State<ChatEntryWidget> {
|
|||||||
onTap: () => GoRouter.of(context).pushNamed(
|
onTap: () => GoRouter.of(context).pushNamed(
|
||||||
'chatScreen',
|
'chatScreen',
|
||||||
extra: {
|
extra: {
|
||||||
'chatId': widget.chatRoom.id.toString(),
|
'uuid': widget.chatRoom.id.toString(),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:eatvio_app/core/token_storage.dart';
|
||||||
import 'package:eatvio_app/features/chat/models/chat_message_model.dart';
|
import 'package:eatvio_app/features/chat/models/chat_message_model.dart';
|
||||||
import 'package:eatvio_app/features/chat/models/chat_messages_model.dart';
|
import 'package:eatvio_app/features/chat/models/chat_messages_model.dart';
|
||||||
import 'package:eatvio_app/features/chat/models/chat_room_model.dart';
|
import 'package:eatvio_app/features/chat/models/chat_room_model.dart';
|
||||||
@@ -47,10 +48,11 @@ class ChatRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await chatService.sendMessageByUserSlug(
|
await chatService.sendMessage(
|
||||||
userSlug: slug,
|
slug: slug,
|
||||||
data: data,
|
data: data,
|
||||||
mediaData: multipartFiles.isEmpty ? null : multipartFiles,
|
mediaData: multipartFiles.isEmpty ? null : multipartFiles,
|
||||||
|
socketId: (await SocketIdStorage.getSocketIdFromStorage())!,
|
||||||
);
|
);
|
||||||
} catch (e, st) {
|
} catch (e, st) {
|
||||||
log(e.toString(), stackTrace: st);
|
log(e.toString(), stackTrace: st);
|
||||||
@@ -58,10 +60,11 @@ class ChatRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ChatRoomModel> getChatRoom(String chatId) async {
|
Future<ChatRoomModel> getChatRoom(String uuid) async {
|
||||||
try {
|
try {
|
||||||
return (await chatService.getChatRoom(id: chatId))['chatRoom']!;
|
return (await chatService.getChatRoom(uuid: uuid));
|
||||||
} catch (e) {
|
} catch (e, st) {
|
||||||
|
log(e.toString(), stackTrace: st);
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,9 +77,9 @@ class ChatRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ChatMessagesModel> getChatMessages(String chatId, int page) async {
|
Future<ChatMessagesModel> getChatMessages(String id, int page) async {
|
||||||
try {
|
try {
|
||||||
return (await chatService.getChatMessages(id: chatId, page: page));
|
return (await chatService.getChatMessages(id: id, page: page));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
@@ -90,9 +93,9 @@ class ChatRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> markMessagesAsRead(String chatId, List<int> messageIds) async {
|
Future<void> markMessagesAsRead(String id, List<int> messageIds) async {
|
||||||
try {
|
try {
|
||||||
await chatService.markMessagesAsRead(id: chatId, body: {'messages_ids': messageIds});
|
await chatService.markMessagesAsRead(id: id, body: {'messages_ids': messageIds});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,32 +13,33 @@ part 'chat_service.g.dart';
|
|||||||
abstract class ChatService {
|
abstract class ChatService {
|
||||||
factory ChatService(Dio dio, {String baseUrl}) = _ChatService;
|
factory ChatService(Dio dio, {String baseUrl}) = _ChatService;
|
||||||
|
|
||||||
@GET('/api/chat/index')
|
@GET('/api/v2/chat/index')
|
||||||
Future<ChatRoomPaginationModel> getChatRooms({
|
Future<ChatRoomPaginationModel> getChatRooms({
|
||||||
@Query('page') required int page,
|
@Query('page') required int page,
|
||||||
@Query('find') String? query,
|
@Query('filters[data][\$conversation_name]') String? query,
|
||||||
});
|
});
|
||||||
|
|
||||||
@GET('/api/chat/show/{id}')
|
@GET('/api/v2/chat/show/{uuid}')
|
||||||
Future<Map<String, ChatRoomModel>> getChatRoom({
|
Future<ChatRoomModel> getChatRoom({
|
||||||
@Path() required String id,
|
@Path() required String uuid,
|
||||||
});
|
});
|
||||||
|
|
||||||
@POST('/api/v2/chat/message/send/{id}')
|
@POST('/api/v2/chat/message/send/{slug}')
|
||||||
@MultiPart()
|
@MultiPart()
|
||||||
Future<void> sendMessage({
|
Future<void> sendMessage({
|
||||||
@Path() required String id,
|
@Header('X-Socket-ID') required String socketId,
|
||||||
|
@Path() required String slug,
|
||||||
@Part() String? data,
|
@Part() String? data,
|
||||||
@Part(name: 'media_data[]', contentType: 'multipart/form-data') List<MultipartFile>? mediaData,
|
@Part(name: 'media_data[]', contentType: 'multipart/form-data') List<MultipartFile>? mediaData,
|
||||||
});
|
});
|
||||||
|
|
||||||
@GET('/api/chat/show/{id}/messages?page={page}')
|
@GET('/api/v2/chat/show/{id}/messages?page={page}')
|
||||||
Future<ChatMessagesModel> getChatMessages({
|
Future<ChatMessagesModel> getChatMessages({
|
||||||
@Path() required String id,
|
@Path() required String id,
|
||||||
@Path() required int page,
|
@Path() required int page,
|
||||||
});
|
});
|
||||||
|
|
||||||
@GET('/api/chat/get_by_user/{user}')
|
@GET('/api/v2/chat/get_by_user/{user}')
|
||||||
Future<ChatRoomModel> getChatRoomIdByUserSlug({
|
Future<ChatRoomModel> getChatRoomIdByUserSlug({
|
||||||
@Path() required String user,
|
@Path() required String user,
|
||||||
});
|
});
|
||||||
@@ -81,16 +82,8 @@ abstract class ChatService {
|
|||||||
@Body() required PlanComplainModel body,
|
@Body() required PlanComplainModel body,
|
||||||
});
|
});
|
||||||
|
|
||||||
@GET('/api/v2/chat/get_by_user/{slug}')
|
@GET('/api/chat/get_by_user/{slug}')
|
||||||
Future<HttpResponse> getChatByUserSlug({
|
Future<HttpResponse> getChatByUserSlug({
|
||||||
@Path() required String slug,
|
@Path() required String slug,
|
||||||
});
|
});
|
||||||
|
|
||||||
@POST('/api/v2/chat/message/send/{userSlug}')
|
|
||||||
@MultiPart()
|
|
||||||
Future<void> sendMessageByUserSlug({
|
|
||||||
@Path() required String userSlug,
|
|
||||||
@Part() String? data,
|
|
||||||
@Part(name: 'media_data[]', contentType: 'multipart/form-data') List<MultipartFile>? mediaData,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
|||||||
import 'package:eatvio_app/core/widgets/loader.dart';
|
import 'package:eatvio_app/core/widgets/loader.dart';
|
||||||
import 'package:eatvio_app/features/complaining/presentation/cubit/complaining_cubit.dart';
|
import 'package:eatvio_app/features/complaining/presentation/cubit/complaining_cubit.dart';
|
||||||
import 'package:eatvio_app/features/complaining/presentation/cubit/complaining_state.dart';
|
import 'package:eatvio_app/features/complaining/presentation/cubit/complaining_state.dart';
|
||||||
|
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
|
||||||
import 'package:eatvio_app/generated/l10n.dart';
|
import 'package:eatvio_app/generated/l10n.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
@@ -194,63 +195,74 @@ class _LawViolationWidgetState extends State<_LawViolationWidget> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = S.of(context);
|
final s = S.of(context);
|
||||||
return Column(
|
return BlocBuilder<ComplainingCubit, ComplainingState>(
|
||||||
mainAxisSize: MainAxisSize.min,
|
builder: (context, state) {
|
||||||
children: [
|
return Column(
|
||||||
const BottomSheetHandlerBar(),
|
mainAxisSize: MainAxisSize.min,
|
||||||
const SizedBox(height: 20),
|
children: [
|
||||||
Text(
|
const BottomSheetHandlerBar(),
|
||||||
s.lawViolation,
|
const SizedBox(height: 20),
|
||||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
Text(
|
||||||
),
|
s.lawViolation,
|
||||||
const SizedBox(height: 8),
|
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||||
SizedBox(
|
),
|
||||||
width: MediaQuery.sizeOf(context).width * 0.7,
|
const SizedBox(height: 8),
|
||||||
child: Text(
|
SizedBox(
|
||||||
s.what_do_you_want_to_complaint_about,
|
width: MediaQuery.sizeOf(context).width * 0.7,
|
||||||
textAlign: TextAlign.center,
|
child: Text(
|
||||||
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
|
s.what_do_you_want_to_complaint_about,
|
||||||
),
|
textAlign: TextAlign.center,
|
||||||
),
|
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
|
||||||
const SizedBox(height: 24),
|
),
|
||||||
...tags.mapIndexed((index, element) {
|
),
|
||||||
return Row(
|
const SizedBox(height: 24),
|
||||||
children: [
|
...tags.mapIndexed((index, element) {
|
||||||
Checkbox(
|
return Row(
|
||||||
activeColor: appTheme.blueA400,
|
children: [
|
||||||
side: BorderSide(width: 2, color: tags[index].$1 ? appTheme.blueA400 : appTheme.blueGray),
|
Checkbox(
|
||||||
value: element.$1,
|
activeColor: appTheme.blueA400,
|
||||||
onChanged: (isChecked) {
|
side: BorderSide(width: 2, color: tags[index].$1 ? appTheme.blueA400 : appTheme.blueGray),
|
||||||
setState(() {
|
value: element.$1,
|
||||||
if (isChecked == null) return;
|
onChanged: (isChecked) {
|
||||||
if (index == tags.length - 1 && !isChecked) {
|
setState(() {
|
||||||
context.read<ComplainingCubit>().toggleDescription();
|
if (isChecked == null) return;
|
||||||
}
|
if (index == tags.length - 1 && !isChecked) {
|
||||||
tags[index] = (isChecked, element.$2);
|
context.read<ComplainingCubit>().toggleDescription();
|
||||||
final activeTags = tags.where((elem) => elem.$1).map((e) => e.$2).toList();
|
}
|
||||||
context.read<ComplainingCubit>().setTags(activeTags);
|
tags[index] = (isChecked, element.$2);
|
||||||
});
|
final activeTags = tags.where((elem) => elem.$1).map((e) => e.$2).toList();
|
||||||
|
context.read<ComplainingCubit>().setTags(activeTags);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
element.$2,
|
||||||
|
style: TextStyles.textLSemibold.copyWith(color: appTheme.black),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
if (tags.last.$1) ...[
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
CustomTextFormField(
|
||||||
|
hintText: s.type_here_your_complaint,
|
||||||
|
hasError: (state.complainInfo.description?.length ?? 10) < 10,
|
||||||
|
onChanged: (value) {
|
||||||
|
context.read<ComplainingCubit>().setDescription(value);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Text(
|
if ((state.complainInfo.description?.length ?? 10) < 10) ...[
|
||||||
element.$2,
|
const SizedBox(height: 4),
|
||||||
style: TextStyles.textLSemibold.copyWith(color: appTheme.black),
|
const ValidationErrorWidget(error: 'Минимум 10 символов'),
|
||||||
),
|
]
|
||||||
],
|
],
|
||||||
);
|
const SizedBox(height: 20),
|
||||||
}),
|
_ComplainButton(
|
||||||
if (tags.last.$1) ...[
|
isDisabled: (state.complainInfo.description?.length ?? 0) < 10,
|
||||||
const SizedBox(height: 12),
|
),
|
||||||
CustomTextFormField(
|
],
|
||||||
hintText: s.type_here_your_complaint,
|
);
|
||||||
onChanged: (value) {
|
},
|
||||||
context.read<ComplainingCubit>().setDescription(value);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
const _ComplainButton(),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -515,8 +527,8 @@ class _ComplaintFailedWidget extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ComplainButton extends StatelessWidget {
|
class _ComplainButton extends StatelessWidget {
|
||||||
const _ComplainButton();
|
const _ComplainButton({this.isDisabled = false});
|
||||||
|
final bool isDisabled;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = S.of(context);
|
final s = S.of(context);
|
||||||
@@ -534,6 +546,10 @@ class _ComplainButton extends StatelessWidget {
|
|||||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
if (isDisabled) {
|
||||||
|
context.read<ComplainingCubit>().setDescription('');
|
||||||
|
return;
|
||||||
|
}
|
||||||
await context.read<ComplainingCubit>().complain();
|
await context.read<ComplainingCubit>().complain();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,10 +14,7 @@ import 'package:go_router/go_router.dart';
|
|||||||
class ConsultationCardWidget extends StatefulWidget {
|
class ConsultationCardWidget extends StatefulWidget {
|
||||||
final ConsultationModel consultationsModel;
|
final ConsultationModel consultationsModel;
|
||||||
|
|
||||||
const ConsultationCardWidget({
|
const ConsultationCardWidget({super.key, required this.consultationsModel});
|
||||||
super.key,
|
|
||||||
required this.consultationsModel,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ConsultationCardWidget> createState() => _ConsultationCardWidgetState();
|
State<ConsultationCardWidget> createState() => _ConsultationCardWidgetState();
|
||||||
@@ -32,13 +29,7 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
|
|||||||
'Дневник заполнен, прокомментирован консультантом и после этого клиент просмотрел комментарии.',
|
'Дневник заполнен, прокомментирован консультантом и после этого клиент просмотрел комментарии.',
|
||||||
];
|
];
|
||||||
|
|
||||||
final List<Color> legendColors = const [
|
final List<Color> legendColors = const [Colors.white, Color(0xFFF0C497), Color(0xFFFF9500), Color(0xFF2F80ED), Color(0xFF4CD964)];
|
||||||
Colors.white,
|
|
||||||
Color(0xFFF0C497),
|
|
||||||
Color(0xFFFF9500),
|
|
||||||
Color(0xFF2F80ED),
|
|
||||||
Color(0xFF4CD964),
|
|
||||||
];
|
|
||||||
|
|
||||||
late final List<(int, DiaryModel)> givenDaysWithDayNumber;
|
late final List<(int, DiaryModel)> givenDaysWithDayNumber;
|
||||||
final List<List<DiaryStateEnum>> legendStates = [
|
final List<List<DiaryStateEnum>> legendStates = [
|
||||||
@@ -46,7 +37,7 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
|
|||||||
[DiaryStateEnum.started, DiaryStateEnum.lastDayStarted],
|
[DiaryStateEnum.started, DiaryStateEnum.lastDayStarted],
|
||||||
[DiaryStateEnum.finished, DiaryStateEnum.lastDayFinished],
|
[DiaryStateEnum.finished, DiaryStateEnum.lastDayFinished],
|
||||||
[DiaryStateEnum.commented, DiaryStateEnum.lastDayCommented],
|
[DiaryStateEnum.commented, DiaryStateEnum.lastDayCommented],
|
||||||
[DiaryStateEnum.reviewedByUser]
|
[DiaryStateEnum.reviewedByUser],
|
||||||
];
|
];
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -72,32 +63,19 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
|
|||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
CircleAvatar(
|
CircleAvatar(radius: 30, backgroundImage: NetworkImage(baseUrl + widget.consultationsModel.userProfilePhotoUrl)),
|
||||||
radius: 30,
|
|
||||||
backgroundImage: NetworkImage(baseUrl + widget.consultationsModel.userProfilePhotoUrl),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
widget.consultationsModel.userName,
|
widget.consultationsModel.userName,
|
||||||
style: const TextStyle(
|
style: const TextStyle(height: 1, color: Color(0xFF323F4B), fontSize: 16, fontWeight: FontWeight.w600),
|
||||||
height: 1,
|
|
||||||
color: Color(0xFF323F4B),
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
widget.consultationsModel.planTitle,
|
widget.consultationsModel.planTitle,
|
||||||
style: const TextStyle(
|
style: const TextStyle(height: 1, color: Color(0xFF88949F), fontSize: 14, fontWeight: FontWeight.w500),
|
||||||
height: 1,
|
|
||||||
color: Color(0xFF88949F),
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -127,12 +105,7 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'День ${widget.consultationsModel.currentDiaryDay} из ${widget.consultationsModel.duration}',
|
'День ${widget.consultationsModel.currentDiaryDay} из ${widget.consultationsModel.duration}',
|
||||||
style: const TextStyle(
|
style: const TextStyle(height: 1, color: Color(0xFF323F4B), fontSize: 16, fontWeight: FontWeight.w500),
|
||||||
height: 1,
|
|
||||||
color: Color(0xFF323F4B),
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@@ -149,12 +122,7 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
|
|||||||
children: [
|
children: [
|
||||||
const Text(
|
const Text(
|
||||||
'Легенда',
|
'Легенда',
|
||||||
style: TextStyle(
|
style: TextStyle(height: 1, fontSize: 20, fontWeight: FontWeight.w700, color: Color(0xFF323F4B)),
|
||||||
height: 1,
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
color: Color(0xFF323F4B),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
ListView.separated(
|
ListView.separated(
|
||||||
@@ -170,11 +138,7 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: const Icon(
|
child: const Icon(Icons.help_outline_rounded, color: Color(0xFF323F4B), size: 20),
|
||||||
Icons.help_outline_rounded,
|
|
||||||
color: Color(0xFF323F4B),
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -185,7 +149,8 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
|
|||||||
children: List.generate(widget.consultationsModel.duration, (index) {
|
children: List.generate(widget.consultationsModel.duration, (index) {
|
||||||
final day = givenDaysWithDayNumber.firstWhereOrNull((e) => e.$1 == index);
|
final day = givenDaysWithDayNumber.firstWhereOrNull((e) => e.$1 == index);
|
||||||
final hasActivity = day != null && day.$2.state != DiaryStateEnum.init;
|
final hasActivity = day != null && day.$2.state != DiaryStateEnum.init;
|
||||||
final canConsultantOpenThisDay = day != null &&
|
final canConsultantOpenThisDay =
|
||||||
|
day != null &&
|
||||||
(day.$2.state != DiaryStateEnum.init && day.$2.state != DiaryStateEnum.started && day.$2.state != DiaryStateEnum.lastDayStarted);
|
(day.$2.state != DiaryStateEnum.init && day.$2.state != DiaryStateEnum.started && day.$2.state != DiaryStateEnum.lastDayStarted);
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(right: 12),
|
padding: const EdgeInsets.only(right: 12),
|
||||||
@@ -195,81 +160,74 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
|
|||||||
InkWell(
|
InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (!hasActivity || !canConsultantOpenThisDay) {
|
if (!hasActivity || !canConsultantOpenThisDay) {
|
||||||
showErrorDialog(context,
|
showErrorDialog(
|
||||||
title: s.you_can_not_open_this_day, description: '${widget.consultationsModel.userName} ${s.has_not_filled_it_yet}');
|
context,
|
||||||
|
title: s.you_can_not_open_this_day,
|
||||||
|
description: '${widget.consultationsModel.userName} ${s.has_not_filled_it_yet}',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
GoRouter.of(context).push(UserDiaryScreen.path, extra: {
|
GoRouter.of(context).push(UserDiaryScreen.path, extra: {'diaryId': day.$2.id, 'currentDiaryDay': index + 1});
|
||||||
'diaryId': day.$2.id,
|
|
||||||
'currentDiaryDay': index + 1,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
borderRadius: BorderRadius.circular(100),
|
borderRadius: BorderRadius.circular(100),
|
||||||
child: Ink(
|
child: Ink(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: hasActivity ? appTheme.dividerColor : appTheme.dividerColor.withValues(alpha: 0.4),
|
color: hasActivity ? appTheme.dividerColor : appTheme.dividerColor.withValues(alpha: 0.4),
|
||||||
borderRadius: BorderRadius.circular(100)),
|
borderRadius: BorderRadius.circular(100),
|
||||||
|
),
|
||||||
width: 58,
|
width: 58,
|
||||||
height: 32,
|
height: 32,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
(index + 1).toString(),
|
(index + 1).toString(),
|
||||||
style: TextStyles.textMSemibold.copyWith(
|
style: TextStyles.textMSemibold.copyWith(color: hasActivity ? appTheme.gray : appTheme.gray.withValues(alpha: 0.4)),
|
||||||
color: hasActivity ? appTheme.gray : appTheme.gray.withValues(alpha: 0.4),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (hasActivity) ...[
|
if (hasActivity) ...[
|
||||||
const SizedBox(
|
const SizedBox(height: 4),
|
||||||
height: 4,
|
|
||||||
),
|
|
||||||
switch (day.$2.state) {
|
switch (day.$2.state) {
|
||||||
DiaryStateEnum.init => const SizedBox.shrink(),
|
DiaryStateEnum.init => const SizedBox.shrink(),
|
||||||
DiaryStateEnum.started => Container(
|
DiaryStateEnum.started => Container(
|
||||||
width: 8,
|
width: 8,
|
||||||
height: 8,
|
height: 8,
|
||||||
decoration: BoxDecoration(shape: BoxShape.circle, color: legendColors[1]),
|
decoration: BoxDecoration(shape: BoxShape.circle, color: legendColors[1]),
|
||||||
),
|
),
|
||||||
DiaryStateEnum.lastDayStarted => Container(
|
DiaryStateEnum.lastDayStarted => Container(
|
||||||
width: 8,
|
width: 8,
|
||||||
height: 8,
|
height: 8,
|
||||||
decoration: BoxDecoration(shape: BoxShape.circle, color: legendColors[1]),
|
decoration: BoxDecoration(shape: BoxShape.circle, color: legendColors[1]),
|
||||||
),
|
),
|
||||||
DiaryStateEnum.finished => Container(
|
DiaryStateEnum.finished => Container(
|
||||||
width: 8,
|
width: 8,
|
||||||
height: 8,
|
height: 8,
|
||||||
decoration: BoxDecoration(shape: BoxShape.circle, color: legendColors[2]),
|
decoration: BoxDecoration(shape: BoxShape.circle, color: legendColors[2]),
|
||||||
),
|
),
|
||||||
DiaryStateEnum.lastDayFinished => Container(
|
DiaryStateEnum.lastDayFinished => Container(
|
||||||
width: 8,
|
width: 8,
|
||||||
height: 8,
|
height: 8,
|
||||||
decoration: BoxDecoration(shape: BoxShape.circle, color: legendColors[2]),
|
decoration: BoxDecoration(shape: BoxShape.circle, color: legendColors[2]),
|
||||||
),
|
),
|
||||||
DiaryStateEnum.commented => Container(
|
DiaryStateEnum.commented => Container(
|
||||||
width: 8,
|
width: 8,
|
||||||
height: 8,
|
height: 8,
|
||||||
decoration: BoxDecoration(shape: BoxShape.circle, color: legendColors[3]),
|
decoration: BoxDecoration(shape: BoxShape.circle, color: legendColors[3]),
|
||||||
),
|
),
|
||||||
DiaryStateEnum.lastDayCommented => Container(
|
DiaryStateEnum.lastDayCommented => Container(
|
||||||
width: 8,
|
width: 8,
|
||||||
height: 8,
|
height: 8,
|
||||||
decoration: BoxDecoration(shape: BoxShape.circle, color: legendColors[3]),
|
decoration: BoxDecoration(shape: BoxShape.circle, color: legendColors[3]),
|
||||||
),
|
),
|
||||||
DiaryStateEnum.reviewedByUser => Container(
|
DiaryStateEnum.reviewedByUser => Container(
|
||||||
width: 8,
|
width: 8,
|
||||||
height: 8,
|
height: 8,
|
||||||
decoration: BoxDecoration(shape: BoxShape.circle, color: legendColors[4]),
|
decoration: BoxDecoration(shape: BoxShape.circle, color: legendColors[4]),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
const SizedBox(
|
const SizedBox(height: 12),
|
||||||
height: 12,
|
|
||||||
),
|
|
||||||
] else
|
] else
|
||||||
const SizedBox(
|
const SizedBox(height: 24),
|
||||||
height: 24,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -280,11 +238,7 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
|
|||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
Text(
|
Text(
|
||||||
'${widget.consultationsModel.consultationOptionPrice.toStringAsFixed(0)} ₽',
|
'${widget.consultationsModel.consultationOptionPrice.toStringAsFixed(0)} ₽',
|
||||||
style: const TextStyle(
|
style: const TextStyle(color: Color(0xFF019A3E), fontSize: 20, fontWeight: FontWeight.w600),
|
||||||
color: Color(0xFF019A3E),
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Row(
|
Row(
|
||||||
@@ -293,36 +247,26 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
|
|||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
final lastActiveDay = givenDaysWithDayNumber
|
final lastActiveDay = givenDaysWithDayNumber
|
||||||
.where(
|
.where((e) => e.$2.state == DiaryStateEnum.finished || e.$2.state == DiaryStateEnum.lastDayFinished)
|
||||||
(e) => e.$2.state == DiaryStateEnum.finished || e.$2.state == DiaryStateEnum.lastDayFinished,
|
|
||||||
)
|
|
||||||
.lastOrNull;
|
.lastOrNull;
|
||||||
if (lastActiveDay != null) {
|
if (lastActiveDay != null) {
|
||||||
GoRouter.of(context).push(UserDiaryScreen.path, extra: {
|
GoRouter.of(context).push(UserDiaryScreen.path, extra: {'diaryId': lastActiveDay.$2.id, 'currentDiaryDay': lastActiveDay.$1});
|
||||||
'diaryId': lastActiveDay.$2.id,
|
|
||||||
'currentDiaryDay': lastActiveDay.$1,
|
|
||||||
});
|
|
||||||
GoRouter.of(context).push(UserDiaryScreen.path, extra: lastActiveDay.$2.id);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
showErrorDialog(context,
|
showErrorDialog(
|
||||||
title: s.you_can_not_open_this_day, description: '${widget.consultationsModel.userName} ${s.has_not_filled_it_yet}');
|
context,
|
||||||
|
title: s.you_can_not_open_this_day,
|
||||||
|
description: '${widget.consultationsModel.userName} ${s.has_not_filled_it_yet}',
|
||||||
|
);
|
||||||
},
|
},
|
||||||
child: Ink(
|
child: Ink(
|
||||||
width: MediaQuery.of(context).size.width - 32 - 52 - 8,
|
width: MediaQuery.of(context).size.width - 32 - 52 - 8,
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(borderRadius: BorderRadius.circular(20), color: appTheme.blueA400),
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
color: appTheme.blueA400,
|
|
||||||
),
|
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
'Комментировать отчёт',
|
'Комментировать отчёт',
|
||||||
style: TextStyle(
|
style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w600),
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -334,16 +278,8 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
|
|||||||
child: Ink(
|
child: Ink(
|
||||||
height: 52,
|
height: 52,
|
||||||
width: 52,
|
width: 52,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(borderRadius: BorderRadius.circular(20), color: const Color(0xFFF6F6F6)),
|
||||||
borderRadius: BorderRadius.circular(20),
|
child: const Center(child: Icon(Icons.chat_outlined, size: 24)),
|
||||||
color: const Color(0xFFF6F6F6),
|
|
||||||
),
|
|
||||||
child: const Center(
|
|
||||||
child: Icon(
|
|
||||||
Icons.chat_outlined,
|
|
||||||
size: 24,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -361,21 +297,13 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
|
|||||||
Container(
|
Container(
|
||||||
height: 20,
|
height: 20,
|
||||||
width: 20,
|
width: 20,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(shape: BoxShape.circle, color: color),
|
||||||
shape: BoxShape.circle,
|
|
||||||
color: color,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
text,
|
text,
|
||||||
style: const TextStyle(
|
style: const TextStyle(height: 1, fontSize: 16, fontWeight: FontWeight.w500, color: Color(0xFF323F4B)),
|
||||||
height: 1,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Color(0xFF323F4B),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class RecipeRepository {
|
|||||||
'type': recipe.type,
|
'type': recipe.type,
|
||||||
'state': 'active',
|
'state': 'active',
|
||||||
'title': recipe.title,
|
'title': recipe.title,
|
||||||
'tags': recipe.tags,
|
'tags[]': recipe.tags ?? [],
|
||||||
'description_md': recipe.description,
|
'description_md': recipe.description,
|
||||||
'video_url': recipe.videoUrl,
|
'video_url': recipe.videoUrl,
|
||||||
'ingredients': ingredientsMap,
|
'ingredients': ingredientsMap,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import 'package:eatvio_app/core/utils/toasts.dart';
|
|||||||
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
|
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
|
||||||
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
||||||
import 'package:eatvio_app/features/dev_tools/test_cases.dart';
|
import 'package:eatvio_app/features/dev_tools/test_cases.dart';
|
||||||
|
import 'package:eatvio_app/core/env_config.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
@@ -97,10 +98,15 @@ class _DevToolsScreenContentState extends State<DevToolsScreenContent> {
|
|||||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(padding: const EdgeInsets.all(8.0), child: Row(children: [Text('BaseUrl: $baseUrlForTest')])),
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Row(children: [Text('BaseUrl: $baseUrlForTest')]),
|
||||||
|
),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: CustomTextFormField(controller: controller, hintText: 'type new base url')),
|
Expanded(
|
||||||
|
child: CustomTextFormField(controller: controller, hintText: 'type new base url'),
|
||||||
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -116,13 +122,13 @@ class _DevToolsScreenContentState extends State<DevToolsScreenContent> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
CustomOutlinedButton(
|
CustomOutlinedButton(
|
||||||
text: baseUrl == 'https://dev2.eatvio.com' ? 'Set production base url' : 'Set dev2 base url',
|
text: baseUrl == EnvConfig.devBaseUrl ? 'Set production base url' : 'Set dev2 base url',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
if (baseUrl == 'https://eatvio.ru') {
|
if (baseUrl == EnvConfig.baseUrl) {
|
||||||
baseUrlForTest = 'https://dev2.eatvio.com';
|
baseUrlForTest = EnvConfig.devBaseUrl;
|
||||||
} else {
|
} else {
|
||||||
baseUrlForTest = 'https://eatvio.ru';
|
baseUrlForTest = EnvConfig.baseUrl;
|
||||||
}
|
}
|
||||||
baseUrl = baseUrlForTest.toLowerCase();
|
baseUrl = baseUrlForTest.toLowerCase();
|
||||||
sl<Dio>().options.baseUrl = baseUrl;
|
sl<Dio>().options.baseUrl = baseUrl;
|
||||||
@@ -132,7 +138,7 @@ class _DevToolsScreenContentState extends State<DevToolsScreenContent> {
|
|||||||
buttonStyle: const ButtonStyle(),
|
buttonStyle: const ButtonStyle(),
|
||||||
),
|
),
|
||||||
SizedBox(height: baseUrlForTest != baseUrl ? 40 : 10),
|
SizedBox(height: baseUrlForTest != baseUrl ? 40 : 10),
|
||||||
if (baseUrl.toLowerCase() != 'https://eatvio.ru')
|
if (baseUrl.toLowerCase() != EnvConfig.baseUrl.toLowerCase())
|
||||||
ListView.builder(
|
ListView.builder(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: testCases.length,
|
itemCount: testCases.length,
|
||||||
@@ -175,12 +181,7 @@ class _DevToolsScreenContentState extends State<DevToolsScreenContent> {
|
|||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
title: const Text('Test Failed'),
|
title: const Text('Test Failed'),
|
||||||
content: SingleChildScrollView(child: Text(result.error ?? 'No error details.')),
|
content: SingleChildScrollView(child: Text(result.error ?? 'No error details.')),
|
||||||
actions: [
|
actions: [TextButton(onPressed: () => Navigator.of(context).pop(), child: const Text('OK'))],
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
|
||||||
child: const Text('OK'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -197,12 +198,7 @@ class _DevToolsScreenContentState extends State<DevToolsScreenContent> {
|
|||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
title: const Text('Test Failed'),
|
title: const Text('Test Failed'),
|
||||||
content: SingleChildScrollView(child: Text('$e\n$stack')),
|
content: SingleChildScrollView(child: Text('$e\n$stack')),
|
||||||
actions: [
|
actions: [TextButton(onPressed: () => Navigator.of(context).pop(), child: const Text('OK'))],
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
|
||||||
child: const Text('OK'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -216,10 +212,10 @@ class _DevToolsScreenContentState extends State<DevToolsScreenContent> {
|
|||||||
TestState.fail => Container(width: 20, height: 20, color: Colors.red),
|
TestState.fail => Container(width: 20, height: 20, color: Colors.red),
|
||||||
TestState.notRun => Container(width: 20, height: 20, color: Colors.grey),
|
TestState.notRun => Container(width: 20, height: 20, color: Colors.grey),
|
||||||
TestState.loading => const SizedBox(
|
TestState.loading => const SizedBox(
|
||||||
width: 20,
|
width: 20,
|
||||||
height: 20,
|
height: 20,
|
||||||
child: SizedBox(height: 20, width: 20, child: Center(child: CircularProgressIndicator())),
|
child: SizedBox(height: 20, width: 20, child: Center(child: CircularProgressIndicator())),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import 'package:eatvio_app/features/recipe/services/about_recipe_service.dart';
|
|||||||
import 'package:eatvio_app/features/recipe/services/my_recipes_service.dart';
|
import 'package:eatvio_app/features/recipe/services/my_recipes_service.dart';
|
||||||
import 'package:eatvio_app/features/registration/services/registration_service.dart';
|
import 'package:eatvio_app/features/registration/services/registration_service.dart';
|
||||||
import 'package:eatvio_app/features/settings/services/settings_service.dart';
|
import 'package:eatvio_app/features/settings/services/settings_service.dart';
|
||||||
|
import 'package:eatvio_app/core/env_config.dart';
|
||||||
|
|
||||||
class TestResult {
|
class TestResult {
|
||||||
final bool success;
|
final bool success;
|
||||||
@@ -477,7 +478,9 @@ class AuthServiceTest implements TestCases {
|
|||||||
final dio = sl<Dio>();
|
final dio = sl<Dio>();
|
||||||
final authService = AuthService(dio, baseUrl: baseUrl);
|
final authService = AuthService(dio, baseUrl: baseUrl);
|
||||||
// Use dummy credentials, expecting failure but no crash
|
// Use dummy credentials, expecting failure but no crash
|
||||||
await authService.getTokenByCredentials(credentials: const CredentialsModel(email: 'reshetnick.mike+user1@gmail.com', password: '1234567890'));
|
await authService.getTokenByCredentials(
|
||||||
|
credentials: CredentialsModel(email: EnvConfig.testEmail, password: EnvConfig.testPassword),
|
||||||
|
);
|
||||||
return TestResult(false, 'Should not succeed');
|
return TestResult(false, 'Should not succeed');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return TestResult(true, 'Exception expected for dummy credentials');
|
return TestResult(true, 'Exception expected for dummy credentials');
|
||||||
|
|||||||
@@ -1,784 +0,0 @@
|
|||||||
// dart format width=80
|
|
||||||
// coverage:ignore-file
|
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
|
||||||
|
|
||||||
part of 'consultant_review_model.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// FreezedGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
// dart format off
|
|
||||||
T _$identity<T>(T value) => value;
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$ConsultantReviewModel {
|
|
||||||
String get planName;
|
|
||||||
int get planDuration;
|
|
||||||
int get currentPlanDay;
|
|
||||||
ConsultantComments get consultantComments;
|
|
||||||
|
|
||||||
/// Create a copy of ConsultantReviewModel
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$ConsultantReviewModelCopyWith<ConsultantReviewModel> get copyWith =>
|
|
||||||
_$ConsultantReviewModelCopyWithImpl<ConsultantReviewModel>(
|
|
||||||
this as ConsultantReviewModel, _$identity);
|
|
||||||
|
|
||||||
/// Serializes this ConsultantReviewModel to a JSON map.
|
|
||||||
Map<String, dynamic> toJson();
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is ConsultantReviewModel &&
|
|
||||||
(identical(other.planName, planName) ||
|
|
||||||
other.planName == planName) &&
|
|
||||||
(identical(other.planDuration, planDuration) ||
|
|
||||||
other.planDuration == planDuration) &&
|
|
||||||
(identical(other.currentPlanDay, currentPlanDay) ||
|
|
||||||
other.currentPlanDay == currentPlanDay) &&
|
|
||||||
(identical(other.consultantComments, consultantComments) ||
|
|
||||||
other.consultantComments == consultantComments));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(
|
|
||||||
runtimeType, planName, planDuration, currentPlanDay, consultantComments);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'ConsultantReviewModel(planName: $planName, planDuration: $planDuration, currentPlanDay: $currentPlanDay, consultantComments: $consultantComments)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class $ConsultantReviewModelCopyWith<$Res> {
|
|
||||||
factory $ConsultantReviewModelCopyWith(ConsultantReviewModel value,
|
|
||||||
$Res Function(ConsultantReviewModel) _then) =
|
|
||||||
_$ConsultantReviewModelCopyWithImpl;
|
|
||||||
@useResult
|
|
||||||
$Res call(
|
|
||||||
{String planName,
|
|
||||||
int planDuration,
|
|
||||||
int currentPlanDay,
|
|
||||||
ConsultantComments consultantComments});
|
|
||||||
|
|
||||||
$ConsultantCommentsCopyWith<$Res> get consultantComments;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$ConsultantReviewModelCopyWithImpl<$Res>
|
|
||||||
implements $ConsultantReviewModelCopyWith<$Res> {
|
|
||||||
_$ConsultantReviewModelCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final ConsultantReviewModel _self;
|
|
||||||
final $Res Function(ConsultantReviewModel) _then;
|
|
||||||
|
|
||||||
/// Create a copy of ConsultantReviewModel
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? planName = null,
|
|
||||||
Object? planDuration = null,
|
|
||||||
Object? currentPlanDay = null,
|
|
||||||
Object? consultantComments = null,
|
|
||||||
}) {
|
|
||||||
return _then(_self.copyWith(
|
|
||||||
planName: null == planName
|
|
||||||
? _self.planName
|
|
||||||
: planName // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
|
||||||
planDuration: null == planDuration
|
|
||||||
? _self.planDuration
|
|
||||||
: planDuration // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,
|
|
||||||
currentPlanDay: null == currentPlanDay
|
|
||||||
? _self.currentPlanDay
|
|
||||||
: currentPlanDay // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,
|
|
||||||
consultantComments: null == consultantComments
|
|
||||||
? _self.consultantComments
|
|
||||||
: consultantComments // ignore: cast_nullable_to_non_nullable
|
|
||||||
as ConsultantComments,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a copy of ConsultantReviewModel
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$ConsultantCommentsCopyWith<$Res> get consultantComments {
|
|
||||||
return $ConsultantCommentsCopyWith<$Res>(_self.consultantComments, (value) {
|
|
||||||
return _then(_self.copyWith(consultantComments: value));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
class _ConsultantReviewModel implements ConsultantReviewModel {
|
|
||||||
_ConsultantReviewModel(
|
|
||||||
{required this.planName,
|
|
||||||
required this.planDuration,
|
|
||||||
required this.currentPlanDay,
|
|
||||||
required this.consultantComments});
|
|
||||||
factory _ConsultantReviewModel.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$ConsultantReviewModelFromJson(json);
|
|
||||||
|
|
||||||
@override
|
|
||||||
final String planName;
|
|
||||||
@override
|
|
||||||
final int planDuration;
|
|
||||||
@override
|
|
||||||
final int currentPlanDay;
|
|
||||||
@override
|
|
||||||
final ConsultantComments consultantComments;
|
|
||||||
|
|
||||||
/// Create a copy of ConsultantReviewModel
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$ConsultantReviewModelCopyWith<_ConsultantReviewModel> get copyWith =>
|
|
||||||
__$ConsultantReviewModelCopyWithImpl<_ConsultantReviewModel>(
|
|
||||||
this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$ConsultantReviewModelToJson(
|
|
||||||
this,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _ConsultantReviewModel &&
|
|
||||||
(identical(other.planName, planName) ||
|
|
||||||
other.planName == planName) &&
|
|
||||||
(identical(other.planDuration, planDuration) ||
|
|
||||||
other.planDuration == planDuration) &&
|
|
||||||
(identical(other.currentPlanDay, currentPlanDay) ||
|
|
||||||
other.currentPlanDay == currentPlanDay) &&
|
|
||||||
(identical(other.consultantComments, consultantComments) ||
|
|
||||||
other.consultantComments == consultantComments));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(
|
|
||||||
runtimeType, planName, planDuration, currentPlanDay, consultantComments);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'ConsultantReviewModel(planName: $planName, planDuration: $planDuration, currentPlanDay: $currentPlanDay, consultantComments: $consultantComments)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class _$ConsultantReviewModelCopyWith<$Res>
|
|
||||||
implements $ConsultantReviewModelCopyWith<$Res> {
|
|
||||||
factory _$ConsultantReviewModelCopyWith(_ConsultantReviewModel value,
|
|
||||||
$Res Function(_ConsultantReviewModel) _then) =
|
|
||||||
__$ConsultantReviewModelCopyWithImpl;
|
|
||||||
@override
|
|
||||||
@useResult
|
|
||||||
$Res call(
|
|
||||||
{String planName,
|
|
||||||
int planDuration,
|
|
||||||
int currentPlanDay,
|
|
||||||
ConsultantComments consultantComments});
|
|
||||||
|
|
||||||
@override
|
|
||||||
$ConsultantCommentsCopyWith<$Res> get consultantComments;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$ConsultantReviewModelCopyWithImpl<$Res>
|
|
||||||
implements _$ConsultantReviewModelCopyWith<$Res> {
|
|
||||||
__$ConsultantReviewModelCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final _ConsultantReviewModel _self;
|
|
||||||
final $Res Function(_ConsultantReviewModel) _then;
|
|
||||||
|
|
||||||
/// Create a copy of ConsultantReviewModel
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$Res call({
|
|
||||||
Object? planName = null,
|
|
||||||
Object? planDuration = null,
|
|
||||||
Object? currentPlanDay = null,
|
|
||||||
Object? consultantComments = null,
|
|
||||||
}) {
|
|
||||||
return _then(_ConsultantReviewModel(
|
|
||||||
planName: null == planName
|
|
||||||
? _self.planName
|
|
||||||
: planName // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
|
||||||
planDuration: null == planDuration
|
|
||||||
? _self.planDuration
|
|
||||||
: planDuration // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,
|
|
||||||
currentPlanDay: null == currentPlanDay
|
|
||||||
? _self.currentPlanDay
|
|
||||||
: currentPlanDay // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,
|
|
||||||
consultantComments: null == consultantComments
|
|
||||||
? _self.consultantComments
|
|
||||||
: consultantComments // ignore: cast_nullable_to_non_nullable
|
|
||||||
as ConsultantComments,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a copy of ConsultantReviewModel
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$ConsultantCommentsCopyWith<$Res> get consultantComments {
|
|
||||||
return $ConsultantCommentsCopyWith<$Res>(_self.consultantComments, (value) {
|
|
||||||
return _then(_self.copyWith(consultantComments: value));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$ConsultantComments {
|
|
||||||
String? get dayComment;
|
|
||||||
List<MealComment> get mealsComments;
|
|
||||||
List<PortionComment> get portionsComments;
|
|
||||||
|
|
||||||
/// Create a copy of ConsultantComments
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$ConsultantCommentsCopyWith<ConsultantComments> get copyWith =>
|
|
||||||
_$ConsultantCommentsCopyWithImpl<ConsultantComments>(
|
|
||||||
this as ConsultantComments, _$identity);
|
|
||||||
|
|
||||||
/// Serializes this ConsultantComments to a JSON map.
|
|
||||||
Map<String, dynamic> toJson();
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is ConsultantComments &&
|
|
||||||
(identical(other.dayComment, dayComment) ||
|
|
||||||
other.dayComment == dayComment) &&
|
|
||||||
const DeepCollectionEquality()
|
|
||||||
.equals(other.mealsComments, mealsComments) &&
|
|
||||||
const DeepCollectionEquality()
|
|
||||||
.equals(other.portionsComments, portionsComments));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(
|
|
||||||
runtimeType,
|
|
||||||
dayComment,
|
|
||||||
const DeepCollectionEquality().hash(mealsComments),
|
|
||||||
const DeepCollectionEquality().hash(portionsComments));
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'ConsultantComments(dayComment: $dayComment, mealsComments: $mealsComments, portionsComments: $portionsComments)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class $ConsultantCommentsCopyWith<$Res> {
|
|
||||||
factory $ConsultantCommentsCopyWith(
|
|
||||||
ConsultantComments value, $Res Function(ConsultantComments) _then) =
|
|
||||||
_$ConsultantCommentsCopyWithImpl;
|
|
||||||
@useResult
|
|
||||||
$Res call(
|
|
||||||
{String? dayComment,
|
|
||||||
List<MealComment> mealsComments,
|
|
||||||
List<PortionComment> portionsComments});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$ConsultantCommentsCopyWithImpl<$Res>
|
|
||||||
implements $ConsultantCommentsCopyWith<$Res> {
|
|
||||||
_$ConsultantCommentsCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final ConsultantComments _self;
|
|
||||||
final $Res Function(ConsultantComments) _then;
|
|
||||||
|
|
||||||
/// Create a copy of ConsultantComments
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? dayComment = freezed,
|
|
||||||
Object? mealsComments = null,
|
|
||||||
Object? portionsComments = null,
|
|
||||||
}) {
|
|
||||||
return _then(_self.copyWith(
|
|
||||||
dayComment: freezed == dayComment
|
|
||||||
? _self.dayComment
|
|
||||||
: dayComment // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,
|
|
||||||
mealsComments: null == mealsComments
|
|
||||||
? _self.mealsComments
|
|
||||||
: mealsComments // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<MealComment>,
|
|
||||||
portionsComments: null == portionsComments
|
|
||||||
? _self.portionsComments
|
|
||||||
: portionsComments // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<PortionComment>,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
class _ConsultantComments implements ConsultantComments {
|
|
||||||
_ConsultantComments(
|
|
||||||
{required this.dayComment,
|
|
||||||
required final List<MealComment> mealsComments,
|
|
||||||
required final List<PortionComment> portionsComments})
|
|
||||||
: _mealsComments = mealsComments,
|
|
||||||
_portionsComments = portionsComments;
|
|
||||||
factory _ConsultantComments.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$ConsultantCommentsFromJson(json);
|
|
||||||
|
|
||||||
@override
|
|
||||||
final String? dayComment;
|
|
||||||
final List<MealComment> _mealsComments;
|
|
||||||
@override
|
|
||||||
List<MealComment> get mealsComments {
|
|
||||||
if (_mealsComments is EqualUnmodifiableListView) return _mealsComments;
|
|
||||||
// ignore: implicit_dynamic_type
|
|
||||||
return EqualUnmodifiableListView(_mealsComments);
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<PortionComment> _portionsComments;
|
|
||||||
@override
|
|
||||||
List<PortionComment> get portionsComments {
|
|
||||||
if (_portionsComments is EqualUnmodifiableListView)
|
|
||||||
return _portionsComments;
|
|
||||||
// ignore: implicit_dynamic_type
|
|
||||||
return EqualUnmodifiableListView(_portionsComments);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a copy of ConsultantComments
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$ConsultantCommentsCopyWith<_ConsultantComments> get copyWith =>
|
|
||||||
__$ConsultantCommentsCopyWithImpl<_ConsultantComments>(this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$ConsultantCommentsToJson(
|
|
||||||
this,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _ConsultantComments &&
|
|
||||||
(identical(other.dayComment, dayComment) ||
|
|
||||||
other.dayComment == dayComment) &&
|
|
||||||
const DeepCollectionEquality()
|
|
||||||
.equals(other._mealsComments, _mealsComments) &&
|
|
||||||
const DeepCollectionEquality()
|
|
||||||
.equals(other._portionsComments, _portionsComments));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(
|
|
||||||
runtimeType,
|
|
||||||
dayComment,
|
|
||||||
const DeepCollectionEquality().hash(_mealsComments),
|
|
||||||
const DeepCollectionEquality().hash(_portionsComments));
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'ConsultantComments(dayComment: $dayComment, mealsComments: $mealsComments, portionsComments: $portionsComments)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class _$ConsultantCommentsCopyWith<$Res>
|
|
||||||
implements $ConsultantCommentsCopyWith<$Res> {
|
|
||||||
factory _$ConsultantCommentsCopyWith(
|
|
||||||
_ConsultantComments value, $Res Function(_ConsultantComments) _then) =
|
|
||||||
__$ConsultantCommentsCopyWithImpl;
|
|
||||||
@override
|
|
||||||
@useResult
|
|
||||||
$Res call(
|
|
||||||
{String? dayComment,
|
|
||||||
List<MealComment> mealsComments,
|
|
||||||
List<PortionComment> portionsComments});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$ConsultantCommentsCopyWithImpl<$Res>
|
|
||||||
implements _$ConsultantCommentsCopyWith<$Res> {
|
|
||||||
__$ConsultantCommentsCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final _ConsultantComments _self;
|
|
||||||
final $Res Function(_ConsultantComments) _then;
|
|
||||||
|
|
||||||
/// Create a copy of ConsultantComments
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$Res call({
|
|
||||||
Object? dayComment = freezed,
|
|
||||||
Object? mealsComments = null,
|
|
||||||
Object? portionsComments = null,
|
|
||||||
}) {
|
|
||||||
return _then(_ConsultantComments(
|
|
||||||
dayComment: freezed == dayComment
|
|
||||||
? _self.dayComment
|
|
||||||
: dayComment // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String?,
|
|
||||||
mealsComments: null == mealsComments
|
|
||||||
? _self._mealsComments
|
|
||||||
: mealsComments // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<MealComment>,
|
|
||||||
portionsComments: null == portionsComments
|
|
||||||
? _self._portionsComments
|
|
||||||
: portionsComments // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<PortionComment>,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$MealComment {
|
|
||||||
String get comment;
|
|
||||||
int get typeNo;
|
|
||||||
|
|
||||||
/// Create a copy of MealComment
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$MealCommentCopyWith<MealComment> get copyWith =>
|
|
||||||
_$MealCommentCopyWithImpl<MealComment>(this as MealComment, _$identity);
|
|
||||||
|
|
||||||
/// Serializes this MealComment to a JSON map.
|
|
||||||
Map<String, dynamic> toJson();
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is MealComment &&
|
|
||||||
(identical(other.comment, comment) || other.comment == comment) &&
|
|
||||||
(identical(other.typeNo, typeNo) || other.typeNo == typeNo));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType, comment, typeNo);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'MealComment(comment: $comment, typeNo: $typeNo)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class $MealCommentCopyWith<$Res> {
|
|
||||||
factory $MealCommentCopyWith(
|
|
||||||
MealComment value, $Res Function(MealComment) _then) =
|
|
||||||
_$MealCommentCopyWithImpl;
|
|
||||||
@useResult
|
|
||||||
$Res call({String comment, int typeNo});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$MealCommentCopyWithImpl<$Res> implements $MealCommentCopyWith<$Res> {
|
|
||||||
_$MealCommentCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final MealComment _self;
|
|
||||||
final $Res Function(MealComment) _then;
|
|
||||||
|
|
||||||
/// Create a copy of MealComment
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? comment = null,
|
|
||||||
Object? typeNo = null,
|
|
||||||
}) {
|
|
||||||
return _then(_self.copyWith(
|
|
||||||
comment: null == comment
|
|
||||||
? _self.comment
|
|
||||||
: comment // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
|
||||||
typeNo: null == typeNo
|
|
||||||
? _self.typeNo
|
|
||||||
: typeNo // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
class _MealComment implements MealComment {
|
|
||||||
_MealComment({required this.comment, required this.typeNo});
|
|
||||||
factory _MealComment.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$MealCommentFromJson(json);
|
|
||||||
|
|
||||||
@override
|
|
||||||
final String comment;
|
|
||||||
@override
|
|
||||||
final int typeNo;
|
|
||||||
|
|
||||||
/// Create a copy of MealComment
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$MealCommentCopyWith<_MealComment> get copyWith =>
|
|
||||||
__$MealCommentCopyWithImpl<_MealComment>(this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$MealCommentToJson(
|
|
||||||
this,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _MealComment &&
|
|
||||||
(identical(other.comment, comment) || other.comment == comment) &&
|
|
||||||
(identical(other.typeNo, typeNo) || other.typeNo == typeNo));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType, comment, typeNo);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'MealComment(comment: $comment, typeNo: $typeNo)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class _$MealCommentCopyWith<$Res>
|
|
||||||
implements $MealCommentCopyWith<$Res> {
|
|
||||||
factory _$MealCommentCopyWith(
|
|
||||||
_MealComment value, $Res Function(_MealComment) _then) =
|
|
||||||
__$MealCommentCopyWithImpl;
|
|
||||||
@override
|
|
||||||
@useResult
|
|
||||||
$Res call({String comment, int typeNo});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$MealCommentCopyWithImpl<$Res> implements _$MealCommentCopyWith<$Res> {
|
|
||||||
__$MealCommentCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final _MealComment _self;
|
|
||||||
final $Res Function(_MealComment) _then;
|
|
||||||
|
|
||||||
/// Create a copy of MealComment
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$Res call({
|
|
||||||
Object? comment = null,
|
|
||||||
Object? typeNo = null,
|
|
||||||
}) {
|
|
||||||
return _then(_MealComment(
|
|
||||||
comment: null == comment
|
|
||||||
? _self.comment
|
|
||||||
: comment // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
|
||||||
typeNo: null == typeNo
|
|
||||||
? _self.typeNo
|
|
||||||
: typeNo // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$PortionComment {
|
|
||||||
@JsonKey(name: 'id')
|
|
||||||
int get portionId;
|
|
||||||
String get comment;
|
|
||||||
|
|
||||||
/// Create a copy of PortionComment
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$PortionCommentCopyWith<PortionComment> get copyWith =>
|
|
||||||
_$PortionCommentCopyWithImpl<PortionComment>(
|
|
||||||
this as PortionComment, _$identity);
|
|
||||||
|
|
||||||
/// Serializes this PortionComment to a JSON map.
|
|
||||||
Map<String, dynamic> toJson();
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is PortionComment &&
|
|
||||||
(identical(other.portionId, portionId) ||
|
|
||||||
other.portionId == portionId) &&
|
|
||||||
(identical(other.comment, comment) || other.comment == comment));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType, portionId, comment);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'PortionComment(portionId: $portionId, comment: $comment)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class $PortionCommentCopyWith<$Res> {
|
|
||||||
factory $PortionCommentCopyWith(
|
|
||||||
PortionComment value, $Res Function(PortionComment) _then) =
|
|
||||||
_$PortionCommentCopyWithImpl;
|
|
||||||
@useResult
|
|
||||||
$Res call({@JsonKey(name: 'id') int portionId, String comment});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$PortionCommentCopyWithImpl<$Res>
|
|
||||||
implements $PortionCommentCopyWith<$Res> {
|
|
||||||
_$PortionCommentCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final PortionComment _self;
|
|
||||||
final $Res Function(PortionComment) _then;
|
|
||||||
|
|
||||||
/// Create a copy of PortionComment
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? portionId = null,
|
|
||||||
Object? comment = null,
|
|
||||||
}) {
|
|
||||||
return _then(_self.copyWith(
|
|
||||||
portionId: null == portionId
|
|
||||||
? _self.portionId
|
|
||||||
: portionId // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,
|
|
||||||
comment: null == comment
|
|
||||||
? _self.comment
|
|
||||||
: comment // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
class _PortionComment implements PortionComment {
|
|
||||||
_PortionComment(
|
|
||||||
{@JsonKey(name: 'id') required this.portionId, required this.comment});
|
|
||||||
factory _PortionComment.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$PortionCommentFromJson(json);
|
|
||||||
|
|
||||||
@override
|
|
||||||
@JsonKey(name: 'id')
|
|
||||||
final int portionId;
|
|
||||||
@override
|
|
||||||
final String comment;
|
|
||||||
|
|
||||||
/// Create a copy of PortionComment
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$PortionCommentCopyWith<_PortionComment> get copyWith =>
|
|
||||||
__$PortionCommentCopyWithImpl<_PortionComment>(this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$PortionCommentToJson(
|
|
||||||
this,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _PortionComment &&
|
|
||||||
(identical(other.portionId, portionId) ||
|
|
||||||
other.portionId == portionId) &&
|
|
||||||
(identical(other.comment, comment) || other.comment == comment));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType, portionId, comment);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'PortionComment(portionId: $portionId, comment: $comment)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class _$PortionCommentCopyWith<$Res>
|
|
||||||
implements $PortionCommentCopyWith<$Res> {
|
|
||||||
factory _$PortionCommentCopyWith(
|
|
||||||
_PortionComment value, $Res Function(_PortionComment) _then) =
|
|
||||||
__$PortionCommentCopyWithImpl;
|
|
||||||
@override
|
|
||||||
@useResult
|
|
||||||
$Res call({@JsonKey(name: 'id') int portionId, String comment});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$PortionCommentCopyWithImpl<$Res>
|
|
||||||
implements _$PortionCommentCopyWith<$Res> {
|
|
||||||
__$PortionCommentCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final _PortionComment _self;
|
|
||||||
final $Res Function(_PortionComment) _then;
|
|
||||||
|
|
||||||
/// Create a copy of PortionComment
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$Res call({
|
|
||||||
Object? portionId = null,
|
|
||||||
Object? comment = null,
|
|
||||||
}) {
|
|
||||||
return _then(_PortionComment(
|
|
||||||
portionId: null == portionId
|
|
||||||
? _self.portionId
|
|
||||||
: portionId // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,
|
|
||||||
comment: null == comment
|
|
||||||
? _self.comment
|
|
||||||
: comment // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// dart format on
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
|
|
||||||
part of 'consultant_review_model.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// JsonSerializableGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
_ConsultantReviewModel _$ConsultantReviewModelFromJson(
|
|
||||||
Map<String, dynamic> json) =>
|
|
||||||
_ConsultantReviewModel(
|
|
||||||
planName: json['planName'] as String,
|
|
||||||
planDuration: (json['planDuration'] as num).toInt(),
|
|
||||||
currentPlanDay: (json['currentPlanDay'] as num).toInt(),
|
|
||||||
consultantComments: ConsultantComments.fromJson(
|
|
||||||
json['consultantComments'] as Map<String, dynamic>),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$ConsultantReviewModelToJson(
|
|
||||||
_ConsultantReviewModel instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'planName': instance.planName,
|
|
||||||
'planDuration': instance.planDuration,
|
|
||||||
'currentPlanDay': instance.currentPlanDay,
|
|
||||||
'consultantComments': instance.consultantComments,
|
|
||||||
};
|
|
||||||
|
|
||||||
_ConsultantComments _$ConsultantCommentsFromJson(Map<String, dynamic> json) =>
|
|
||||||
_ConsultantComments(
|
|
||||||
dayComment: json['dayComment'] as String?,
|
|
||||||
mealsComments: (json['mealsComments'] as List<dynamic>)
|
|
||||||
.map((e) => MealComment.fromJson(e as Map<String, dynamic>))
|
|
||||||
.toList(),
|
|
||||||
portionsComments: (json['portionsComments'] as List<dynamic>)
|
|
||||||
.map((e) => PortionComment.fromJson(e as Map<String, dynamic>))
|
|
||||||
.toList(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$ConsultantCommentsToJson(_ConsultantComments instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'dayComment': instance.dayComment,
|
|
||||||
'mealsComments': instance.mealsComments,
|
|
||||||
'portionsComments': instance.portionsComments,
|
|
||||||
};
|
|
||||||
|
|
||||||
_MealComment _$MealCommentFromJson(Map<String, dynamic> json) => _MealComment(
|
|
||||||
comment: json['comment'] as String,
|
|
||||||
typeNo: (json['typeNo'] as num).toInt(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$MealCommentToJson(_MealComment instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'comment': instance.comment,
|
|
||||||
'typeNo': instance.typeNo,
|
|
||||||
};
|
|
||||||
|
|
||||||
_PortionComment _$PortionCommentFromJson(Map<String, dynamic> json) =>
|
|
||||||
_PortionComment(
|
|
||||||
portionId: (json['id'] as num).toInt(),
|
|
||||||
comment: json['comment'] as String,
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$PortionCommentToJson(_PortionComment instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'id': instance.portionId,
|
|
||||||
'comment': instance.comment,
|
|
||||||
};
|
|
||||||
@@ -179,11 +179,14 @@ class DiaryCubit extends Cubit<DiaryState> {
|
|||||||
int? currentPlanDay;
|
int? currentPlanDay;
|
||||||
String? planName;
|
String? planName;
|
||||||
if (diaryDay.chosenPlanId != null) {
|
if (diaryDay.chosenPlanId != null) {
|
||||||
consultantReview = await diaryRepository.getDiaryComments(planId: diaryDay.chosenPlanId!, diaryId: diaryDay.id);
|
|
||||||
final chosenPlanModel = await chatRepository.getChosenPlan(id: diaryDay.chosenPlanId!);
|
final chosenPlanModel = await chatRepository.getChosenPlan(id: diaryDay.chosenPlanId!);
|
||||||
|
if (diaryDay.typeOfDiary.diaryState == DiaryStateEnum.lastDayCommented || diaryDay.typeOfDiary.diaryState == DiaryStateEnum.commented) {
|
||||||
|
await diaryRepository.viewCommentOfDiaryDayByConsultant(diaryId: diaryDay.id);
|
||||||
|
}
|
||||||
|
consultantReview = await diaryRepository.getDiaryComments(planId: diaryDay.chosenPlanId!, diaryId: diaryDay.id);
|
||||||
planDuration = chosenPlanModel.plan.duration;
|
planDuration = chosenPlanModel.plan.duration;
|
||||||
planName = chosenPlanModel.plan.name;
|
planName = chosenPlanModel.plan.name;
|
||||||
currentPlanDay = currentDay ?? DateTime.now().difference(chosenPlanModel.startDate).inDays + 1;
|
currentPlanDay = consultantReview.currentPlanDay;
|
||||||
}
|
}
|
||||||
final nutrition = await getNutrition(diaryDay.id);
|
final nutrition = await getNutrition(diaryDay.id);
|
||||||
|
|
||||||
@@ -194,20 +197,19 @@ class DiaryCubit extends Cubit<DiaryState> {
|
|||||||
diaryDay.typeOfDiary.diaryState == DiaryStateEnum.reviewedByUser;
|
diaryDay.typeOfDiary.diaryState == DiaryStateEnum.reviewedByUser;
|
||||||
emit(
|
emit(
|
||||||
DiaryState.loaded(
|
DiaryState.loaded(
|
||||||
diary: diaryDay,
|
diary: diaryDay,
|
||||||
nutrition: nutrition,
|
nutrition: nutrition,
|
||||||
selectedDay: selectedDay,
|
selectedDay: selectedDay,
|
||||||
selectedMeal: selectedMeal,
|
selectedMeal: selectedMeal,
|
||||||
isConsultantView: this.diaryId != null,
|
isConsultantView: this.diaryId != null,
|
||||||
consultantReview: consultantReview,
|
consultantReview: consultantReview,
|
||||||
isDiaryFinished: isDiaryFinished,
|
isDiaryFinished: isDiaryFinished,
|
||||||
isDiaryCommented: isDiaryCommented,
|
isDiaryCommented: isDiaryCommented,
|
||||||
chosenPlanCurrentDay: currentPlanDay,
|
chosenPlanCurrentDay: currentPlanDay,
|
||||||
chosenPlanDuration: planDuration,
|
chosenPlanDuration: planDuration,
|
||||||
chosenPlanName: planName,
|
chosenPlanName: planName,
|
||||||
isMoodEnabled: isMoodEnabled,
|
isMoodEnabled: isMoodEnabled,
|
||||||
isWakeUpAndRetireEnabled: isWakeUpAndRetireTimeEnabled
|
isWakeUpAndRetireEnabled: isWakeUpAndRetireTimeEnabled),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
} catch (e, st) {
|
} catch (e, st) {
|
||||||
log(e.toString(), stackTrace: st);
|
log(e.toString(), stackTrace: st);
|
||||||
@@ -241,6 +243,7 @@ class DiaryCubit extends Cubit<DiaryState> {
|
|||||||
required int quantity,
|
required int quantity,
|
||||||
int? productNutritionalValueId,
|
int? productNutritionalValueId,
|
||||||
}) async {
|
}) async {
|
||||||
|
emit((state as DiaryStateLoaded).copyWith(isLoadingAddingProductInMeal: true));
|
||||||
await diaryRepository.addPortionToMeal(
|
await diaryRepository.addPortionToMeal(
|
||||||
diaryId: diaryId,
|
diaryId: diaryId,
|
||||||
mealId: mealId,
|
mealId: mealId,
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ abstract class DiaryState with _$DiaryState {
|
|||||||
@Default(false) bool isDiaryCommented,
|
@Default(false) bool isDiaryCommented,
|
||||||
@Default(false) bool isWakeUpAndRetireEnabled,
|
@Default(false) bool isWakeUpAndRetireEnabled,
|
||||||
@Default(false) bool isMoodEnabled,
|
@Default(false) bool isMoodEnabled,
|
||||||
|
@Default(false) bool isLoadingAddingProductInMeal,
|
||||||
}) = DiaryStateLoaded;
|
}) = DiaryStateLoaded;
|
||||||
|
|
||||||
const factory DiaryState.loadedTomorrowWithDiet({
|
const factory DiaryState.loadedTomorrowWithDiet({
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ProductLoaderWidget extends StatefulWidget {
|
||||||
|
const ProductLoaderWidget({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ProductLoaderWidget> createState() => _ProductLoaderWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ProductLoaderWidgetState extends State<ProductLoaderWidget> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ListTile(
|
||||||
|
horizontalTitleGap: 0,
|
||||||
|
contentPadding: const EdgeInsets.only(right: 24, left: 12),
|
||||||
|
title: const CupertinoActivityIndicator(),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
tileColor: const Color(0xFFF6FAFF),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
|
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
|
||||||
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
|
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
|
||||||
|
import 'package:eatvio_app/features/diary/presentation/widgets/product_loader_widget.dart';
|
||||||
import 'package:eatvio_app/features/diary/presentation/widgets/product_widget.dart';
|
import 'package:eatvio_app/features/diary/presentation/widgets/product_widget.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
@@ -25,6 +26,11 @@ class _ProductsListWidgetState extends State<ProductsListWidget> {
|
|||||||
ProductWidget(portion: state.diary.meals[mealIndex].portions[i]),
|
ProductWidget(portion: state.diary.meals[mealIndex].portions[i]),
|
||||||
i < state.diary.meals[mealIndex].portions.length - 1 ? const SizedBox(height: 8) : const SizedBox.shrink(),
|
i < state.diary.meals[mealIndex].portions.length - 1 ? const SizedBox(height: 8) : const SizedBox.shrink(),
|
||||||
],
|
],
|
||||||
|
if (state.isLoadingAddingProductInMeal)
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.only(top: 8),
|
||||||
|
child: ProductLoaderWidget(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -297,4 +297,12 @@ class DiaryRepository {
|
|||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> viewCommentOfDiaryDayByConsultant({required int diaryId}) async {
|
||||||
|
try {
|
||||||
|
await diaryService.viewCommentOfDiaryDayByConsultant(diaryId: diaryId);
|
||||||
|
} catch (e) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,4 +150,9 @@ abstract class DiaryService {
|
|||||||
@Path() required int planId,
|
@Path() required int planId,
|
||||||
@Path() required int diaryId,
|
@Path() required int diaryId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@POST('/api/diary/{diaryId}/consultant/comment/view')
|
||||||
|
Future<void> viewCommentOfDiaryDayByConsultant({
|
||||||
|
@Path() required int diaryId,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:easy_debounce/easy_debounce.dart';
|
import 'package:easy_debounce/easy_debounce.dart';
|
||||||
|
import 'package:eatvio_app/core/const.dart';
|
||||||
import 'package:eatvio_app/core/widgets/add_item_button.dart';
|
import 'package:eatvio_app/core/widgets/add_item_button.dart';
|
||||||
import 'package:eatvio_app/core/widgets/custom_search_bar.dart';
|
import 'package:eatvio_app/core/widgets/custom_search_bar.dart';
|
||||||
import 'package:eatvio_app/features/create_diet/presentation/create_diet_screen.dart';
|
import 'package:eatvio_app/features/create_diet/presentation/create_diet_screen.dart';
|
||||||
@@ -32,24 +33,12 @@ class FeedScreen extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MultiBlocProvider(
|
return MultiBlocProvider(
|
||||||
providers: [
|
providers: [
|
||||||
BlocProvider<RecipesCubit>(
|
BlocProvider<RecipesCubit>(create: (context) => sl<RecipesCubit>()..loadRecipesList()),
|
||||||
create: (context) => sl<RecipesCubit>()..loadRecipesList(),
|
BlocProvider<FeedProfileCubit>(create: (context) => sl<FeedProfileCubit>()..getProfile()),
|
||||||
),
|
BlocProvider<FeedCubit>(create: (context) => sl<FeedCubit>()..loadFeedBlogPostList()),
|
||||||
BlocProvider<FeedProfileCubit>(
|
BlocProvider<DietsCubit>(create: (context) => sl<DietsCubit>()..loadDiets()),
|
||||||
create: (context) => sl<FeedProfileCubit>()..getProfile(),
|
if (!hidePlans) BlocProvider<PlansCubit>(create: (context) => sl<PlansCubit>()..loadPlans()),
|
||||||
),
|
BlocProvider<ConsultantsCubit>(create: (context) => sl<ConsultantsCubit>()..getConsultants()),
|
||||||
BlocProvider<FeedCubit>(
|
|
||||||
create: (context) => sl<FeedCubit>()..loadFeedBlogPostList(),
|
|
||||||
),
|
|
||||||
BlocProvider<DietsCubit>(
|
|
||||||
create: (context) => sl<DietsCubit>()..loadDiets(),
|
|
||||||
),
|
|
||||||
BlocProvider<PlansCubit>(
|
|
||||||
create: (context) => sl<PlansCubit>()..loadPlans(),
|
|
||||||
),
|
|
||||||
BlocProvider<ConsultantsCubit>(
|
|
||||||
create: (context) => sl<ConsultantsCubit>()..getConsultants(),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
child: const FeedScreenContent(),
|
child: const FeedScreenContent(),
|
||||||
);
|
);
|
||||||
@@ -69,7 +58,8 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
|
|||||||
final dietsController = TextEditingController();
|
final dietsController = TextEditingController();
|
||||||
final plansController = TextEditingController();
|
final plansController = TextEditingController();
|
||||||
final consultantsController = TextEditingController();
|
final consultantsController = TextEditingController();
|
||||||
final List<String> chipsLabelList = ['Все', 'Рецепты', 'Диеты', 'Планы питания', 'Консультанты'];
|
|
||||||
|
late final List<String> chipsLabelList;
|
||||||
int _selectedChipIndex = 0;
|
int _selectedChipIndex = 0;
|
||||||
final blogPostsScrollController = ScrollController();
|
final blogPostsScrollController = ScrollController();
|
||||||
final recipesScrollController = ScrollController();
|
final recipesScrollController = ScrollController();
|
||||||
@@ -79,12 +69,13 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
// Initialize chipsLabelList based on hidePlans setting
|
||||||
|
chipsLabelList = hidePlans ? ['Все', 'Рецепты', 'Диеты', 'Консультанты'] : ['Все', 'Рецепты', 'Диеты', 'Планы питания', 'Консультанты'];
|
||||||
|
|
||||||
consultantsScrollController.addListener(_onScrollConsultants);
|
consultantsScrollController.addListener(_onScrollConsultants);
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _onScrollConsultants() {
|
void _onScrollConsultants() {
|
||||||
if (consultantsScrollController.offset >= consultantsScrollController.position.maxScrollExtent && !consultantsScrollController.position.outOfRange) {}
|
if (consultantsScrollController.offset >= consultantsScrollController.position.maxScrollExtent && !consultantsScrollController.position.outOfRange) {}
|
||||||
}
|
}
|
||||||
@@ -108,45 +99,36 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
|
|||||||
heroTag: 'recipesTagsFilter',
|
heroTag: 'recipesTagsFilter',
|
||||||
backgroundColor: appTheme.blueA400,
|
backgroundColor: appTheme.blueA400,
|
||||||
shape: const CircleBorder(),
|
shape: const CircleBorder(),
|
||||||
child: const Icon(
|
child: const Icon(Icons.filter_list_rounded, color: Colors.white),
|
||||||
Icons.filter_list_rounded,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final recipesCubit = context.read<RecipesCubit>();
|
final recipesCubit = context.read<RecipesCubit>();
|
||||||
await showModalBottomSheet(
|
await showModalBottomSheet(
|
||||||
useRootNavigator: true,
|
useRootNavigator: true,
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => TagsModalWidget(
|
builder: (context) => TagsModalWidget(recipesCubit: recipesCubit),
|
||||||
recipesCubit: recipesCubit,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
recipesCubit.loadRecipesList();
|
recipesCubit.loadRecipesList();
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
appBar: AppBar(
|
appBar: AppBar(backgroundColor: Colors.white, toolbarHeight: 0),
|
||||||
backgroundColor: Colors.white,
|
|
||||||
toolbarHeight: 0,
|
|
||||||
),
|
|
||||||
backgroundColor: Colors.grey.shade200,
|
backgroundColor: Colors.grey.shade200,
|
||||||
body: NestedScrollView(
|
body: NestedScrollView(
|
||||||
controller: switch (_selectedChipIndex) {
|
controller: switch (_selectedChipIndex) {
|
||||||
0 => blogPostsScrollController,
|
0 => blogPostsScrollController,
|
||||||
1 => recipesScrollController,
|
1 => recipesScrollController,
|
||||||
2 => dietsScrollController,
|
2 => dietsScrollController,
|
||||||
3 => plansScrollController,
|
3 when !hidePlans => plansScrollController,
|
||||||
4 => consultantsScrollController,
|
3 when hidePlans => consultantsScrollController,
|
||||||
_ => blogPostsScrollController
|
4 when !hidePlans => consultantsScrollController,
|
||||||
|
_ => blogPostsScrollController,
|
||||||
},
|
},
|
||||||
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
|
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
|
||||||
return [
|
return [
|
||||||
BlocBuilder<FeedProfileCubit, ProfileState>(
|
BlocBuilder<FeedProfileCubit, ProfileState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return SliverAppBar(
|
return SliverAppBar(
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(bottom: Radius.circular(12))),
|
||||||
borderRadius: BorderRadius.vertical(bottom: Radius.circular(12)),
|
|
||||||
),
|
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
@@ -172,78 +154,71 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
|
|||||||
searchController: switch (_selectedChipIndex) {
|
searchController: switch (_selectedChipIndex) {
|
||||||
1 => recipesController,
|
1 => recipesController,
|
||||||
2 => dietsController,
|
2 => dietsController,
|
||||||
3 => plansController,
|
3 when !hidePlans => plansController,
|
||||||
4 => consultantsController,
|
3 when hidePlans => consultantsController,
|
||||||
|
4 when !hidePlans => consultantsController,
|
||||||
_ => postsController,
|
_ => postsController,
|
||||||
},
|
},
|
||||||
onChanged: (query) {
|
onChanged: (query) {
|
||||||
if (query.length > 2) {
|
if (query.length > 2) {
|
||||||
EasyDebounce.debounce(
|
EasyDebounce.debounce('searchFeed', const Duration(milliseconds: 500), () {
|
||||||
'searchFeed',
|
switch (_selectedChipIndex) {
|
||||||
const Duration(milliseconds: 500),
|
case 0:
|
||||||
() {
|
context.read<FeedCubit>().loadFeedBlogPostList(query: query);
|
||||||
switch (_selectedChipIndex) {
|
break;
|
||||||
case 0:
|
case 1:
|
||||||
context.read<FeedCubit>().loadFeedBlogPostList(query: query);
|
context.read<RecipesCubit>().loadRecipesList(query: query);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 2:
|
||||||
context.read<RecipesCubit>().loadRecipesList(query: query);
|
context.read<DietsCubit>().loadDiets(query: query);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 3 when !hidePlans:
|
||||||
context.read<DietsCubit>().loadDiets(query: query);
|
context.read<PlansCubit>().loadPlans(query: query);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3 when hidePlans:
|
||||||
context.read<PlansCubit>().loadPlans(query: query);
|
case 4 when !hidePlans:
|
||||||
break;
|
context.read<ConsultantsCubit>().getConsultants();
|
||||||
case 4:
|
break;
|
||||||
context.read<ConsultantsCubit>().getConsultants();
|
}
|
||||||
break;
|
});
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
EasyDebounce.debounce(
|
EasyDebounce.debounce('searchFeed', const Duration(milliseconds: 500), () {
|
||||||
'searchFeed',
|
switch (_selectedChipIndex) {
|
||||||
const Duration(milliseconds: 500),
|
case 0:
|
||||||
() {
|
context.read<FeedCubit>().loadFeedBlogPostList();
|
||||||
switch (_selectedChipIndex) {
|
break;
|
||||||
case 0:
|
case 1:
|
||||||
context.read<FeedCubit>().loadFeedBlogPostList();
|
context.read<RecipesCubit>().loadRecipesList();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 2:
|
||||||
context.read<RecipesCubit>().loadRecipesList();
|
context.read<DietsCubit>().loadDiets();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 3 when !hidePlans:
|
||||||
context.read<DietsCubit>().loadDiets();
|
context.read<PlansCubit>().loadPlans();
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3 when hidePlans:
|
||||||
context.read<PlansCubit>().loadPlans();
|
case 4 when !hidePlans:
|
||||||
break;
|
context.read<ConsultantsCubit>().getConsultants();
|
||||||
case 4:
|
break;
|
||||||
context.read<ConsultantsCubit>().getConsultants();
|
}
|
||||||
break;
|
});
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
switch (_selectedChipIndex) {
|
switch (_selectedChipIndex) {
|
||||||
1 => AddItemButton(
|
1 => AddItemButton(onTap: () => GoRouter.of(context).pushNamed(RecipeCreateScreen.name)),
|
||||||
onTap: () => GoRouter.of(context).pushNamed(RecipeCreateScreen.name),
|
|
||||||
),
|
|
||||||
2 => AddItemButton(
|
2 => AddItemButton(
|
||||||
onTap: state.profile?.roles.contains('consultant') ?? false
|
onTap: state.profile?.roles.contains('consultant') ?? false
|
||||||
? () async {
|
? () async {
|
||||||
await GoRouter.of(context).pushNamed(CreateDietScreen.name);
|
await GoRouter.of(context).pushNamed(CreateDietScreen.name);
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
3 => AddItemButton(
|
3 when !hidePlans => AddItemButton(
|
||||||
onTap: state.profile?.roles.contains('consultant') ?? false ? () => GoRouter.of(context).pushNamed(CreatePlanScreen.name) : null,
|
onTap: state.profile?.roles.contains('consultant') ?? false ? () => GoRouter.of(context).pushNamed(CreatePlanScreen.name) : null,
|
||||||
),
|
),
|
||||||
_ => const AddItemButton()
|
_ => const AddItemButton(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -256,8 +231,9 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
|
|||||||
0 => const BlogPosts(),
|
0 => const BlogPosts(),
|
||||||
1 => const Recipes(),
|
1 => const Recipes(),
|
||||||
2 => const Diets(),
|
2 => const Diets(),
|
||||||
3 => const Plans(),
|
3 when !hidePlans => const Plans(),
|
||||||
4 => const Consultants(),
|
3 when hidePlans => const Consultants(),
|
||||||
|
4 when !hidePlans => const Consultants(),
|
||||||
_ => Center(child: Text(chipsLabelList[_selectedChipIndex])),
|
_ => Center(child: Text(chipsLabelList[_selectedChipIndex])),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,12 +2,7 @@ import 'package:eatvio_app/features/feed/presentation/widgets/choice_chip_widget
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
class ChipGroup extends StatelessWidget {
|
class ChipGroup extends StatelessWidget {
|
||||||
const ChipGroup({
|
const ChipGroup({super.key, required this.selectedChipIndex, required this.chipsLabelList, required this.setSelectedChipIndex});
|
||||||
super.key,
|
|
||||||
required this.selectedChipIndex,
|
|
||||||
required this.chipsLabelList,
|
|
||||||
required this.setSelectedChipIndex,
|
|
||||||
});
|
|
||||||
final int selectedChipIndex;
|
final int selectedChipIndex;
|
||||||
final List<String> chipsLabelList;
|
final List<String> chipsLabelList;
|
||||||
final void Function(int) setSelectedChipIndex;
|
final void Function(int) setSelectedChipIndex;
|
||||||
@@ -22,7 +17,7 @@ class ChipGroup extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
...List.generate(
|
...List.generate(
|
||||||
5,
|
chipsLabelList.length,
|
||||||
(index) => Padding(
|
(index) => Padding(
|
||||||
padding: const EdgeInsets.only(right: 8.0),
|
padding: const EdgeInsets.only(right: 8.0),
|
||||||
child: ChoiceChipWidget(
|
child: ChoiceChipWidget(
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:eatvio_app/core/env_config.dart';
|
||||||
|
|
||||||
class InternetConnectionCheckerScreen extends StatefulWidget {
|
class InternetConnectionCheckerScreen extends StatefulWidget {
|
||||||
const InternetConnectionCheckerScreen({super.key, required this.child});
|
const InternetConnectionCheckerScreen({super.key, required this.child});
|
||||||
@@ -66,12 +67,7 @@ class _InternetConnectionCheckerScreenState extends State<InternetConnectionChec
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final uris = [
|
final uris = [Uri.parse(EnvConfig.googleCheckUrl), Uri.parse(EnvConfig.yandexCheckUrl), Uri.parse(EnvConfig.devBaseUrl), Uri.parse(EnvConfig.baseUrl)];
|
||||||
Uri.parse('https://google.com'),
|
|
||||||
Uri.parse('https://yandex.ru'),
|
|
||||||
Uri.parse('https://dev2.eatvio.com'),
|
|
||||||
Uri.parse('https://eatvio.ru'),
|
|
||||||
];
|
|
||||||
for (final uri in uris) {
|
for (final uri in uris) {
|
||||||
try {
|
try {
|
||||||
final response = await http.get(uri).timeout(const Duration(seconds: 3));
|
final response = await http.get(uri).timeout(const Duration(seconds: 3));
|
||||||
@@ -98,13 +94,9 @@ class _InternetConnectionCheckerScreenState extends State<InternetConnectionChec
|
|||||||
if (!_hasInternet)
|
if (!_hasInternet)
|
||||||
NoInternetScreen(
|
NoInternetScreen(
|
||||||
onTryAgain: () async {
|
onTryAgain: () async {
|
||||||
EasyDebounce.debounce(
|
EasyDebounce.debounce('internet connection check', const Duration(seconds: 2), () async {
|
||||||
'internet connection check',
|
await _checkConnection();
|
||||||
const Duration(seconds: 2),
|
});
|
||||||
() async {
|
|
||||||
await _checkConnection();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -129,9 +121,7 @@ class NoInternetScreen extends StatelessWidget {
|
|||||||
child: Text(
|
child: Text(
|
||||||
'Отсутствует подключение \nк интернету',
|
'Отсутствует подключение \nк интернету',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyles.headingXLSemibold.copyWith(
|
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||||
color: appTheme.black,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
@@ -140,9 +130,7 @@ class NoInternetScreen extends StatelessWidget {
|
|||||||
child: Text(
|
child: Text(
|
||||||
'Проверьте соединение с сетью\nи попробуйте ещё раз',
|
'Проверьте соединение с сетью\nи попробуйте ещё раз',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyles.textMMedium.copyWith(
|
style: TextStyles.textMMedium.copyWith(color: appTheme.gray),
|
||||||
color: appTheme.gray,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
@@ -153,10 +141,7 @@ class NoInternetScreen extends StatelessWidget {
|
|||||||
height: 52,
|
height: 52,
|
||||||
text: 'Попробовать ещё раз',
|
text: 'Попробовать ещё раз',
|
||||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
||||||
buttonStyle: ButtonStyle(
|
buttonStyle: ButtonStyle(backgroundColor: WidgetStatePropertyAll(appTheme.blueA400), side: const WidgetStatePropertyAll(BorderSide.none)),
|
||||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
|
||||||
side: const WidgetStatePropertyAll(BorderSide.none),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
|
|||||||
@@ -37,18 +37,20 @@ class MenuScreen extends StatelessWidget {
|
|||||||
if (profileModel.roles.contains('consultant')) _divider(context),
|
if (profileModel.roles.contains('consultant')) _divider(context),
|
||||||
if (profileModel.roles.contains('consultant'))
|
if (profileModel.roles.contains('consultant'))
|
||||||
_tile(leading: ImageConstant.icnPlans, title: 'Мои планы', onTap: () => GoRouter.of(context).push(MyPlansScreen.path)),
|
_tile(leading: ImageConstant.icnPlans, title: 'Мои планы', onTap: () => GoRouter.of(context).push(MyPlansScreen.path)),
|
||||||
_divider(context),
|
if (!hidePlans) ...[
|
||||||
_tile(
|
_divider(context),
|
||||||
leading: 'assets/icons/finance.svg',
|
_tile(
|
||||||
title: 'Мои финансы',
|
leading: 'assets/icons/finance.svg',
|
||||||
onTap: () {
|
title: 'Мои финансы',
|
||||||
if (profileModel.roles.contains('consultant')) {
|
onTap: () {
|
||||||
GoRouter.of(context).push(ConsultantFinancesScreen.path);
|
if (profileModel.roles.contains('consultant')) {
|
||||||
return;
|
GoRouter.of(context).push(ConsultantFinancesScreen.path);
|
||||||
}
|
return;
|
||||||
GoRouter.of(context).push(UserPurchasesScreen.path);
|
}
|
||||||
},
|
GoRouter.of(context).push(UserPurchasesScreen.path);
|
||||||
),
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
_divider(context),
|
_divider(context),
|
||||||
_tile(leading: ImageConstant.icnGlossary, title: 'Глоссарий', onTap: () => GoRouter.of(context).push('/glossary-screen')),
|
_tile(leading: ImageConstant.icnGlossary, title: 'Глоссарий', onTap: () => GoRouter.of(context).push('/glossary-screen')),
|
||||||
_divider(context),
|
_divider(context),
|
||||||
|
|||||||
@@ -107,7 +107,6 @@ class DiaryReminderNotification implements BaseNotificationModel {
|
|||||||
@override
|
@override
|
||||||
final bool isRead;
|
final bool isRead;
|
||||||
|
|
||||||
|
|
||||||
DiaryReminderNotification({required this.type, required this.isRead});
|
DiaryReminderNotification({required this.type, required this.isRead});
|
||||||
|
|
||||||
factory DiaryReminderNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead}) {
|
factory DiaryReminderNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead}) {
|
||||||
@@ -297,6 +296,25 @@ class ChosenPlanFinishedToConsultantNotification implements BaseNotificationMode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ChosenPlanLastDayFinishedForUser implements BaseNotificationModel {
|
||||||
|
@override
|
||||||
|
final NotificationType type;
|
||||||
|
@override
|
||||||
|
final bool isRead;
|
||||||
|
|
||||||
|
ChosenPlanLastDayFinishedForUser({
|
||||||
|
required this.type,
|
||||||
|
required this.isRead,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory ChosenPlanLastDayFinishedForUser.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead}) {
|
||||||
|
return ChosenPlanLastDayFinishedForUser(
|
||||||
|
type: type,
|
||||||
|
isRead: isRead ?? false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class UnknownNotification implements BaseNotificationModel {
|
class UnknownNotification implements BaseNotificationModel {
|
||||||
@override
|
@override
|
||||||
final NotificationType type;
|
final NotificationType type;
|
||||||
@@ -323,6 +341,7 @@ enum NotificationType {
|
|||||||
chosenPlanTomorrowReminderToConsultantNotification,
|
chosenPlanTomorrowReminderToConsultantNotification,
|
||||||
planSubscriptionPaidToAuthor,
|
planSubscriptionPaidToAuthor,
|
||||||
chosenPlanFinishedToConsultant,
|
chosenPlanFinishedToConsultant,
|
||||||
|
chosenPlanLastDayFinishedForUser,
|
||||||
unknown;
|
unknown;
|
||||||
|
|
||||||
static NotificationType getTypeFromString(String type) => switch (type) {
|
static NotificationType getTypeFromString(String type) => switch (type) {
|
||||||
@@ -339,6 +358,7 @@ enum NotificationType {
|
|||||||
'App\\Notifications\\Plan\\ChosenPlanTomorrowReminderToConsultantNotification' => NotificationType.chosenPlanTomorrowReminderToConsultantNotification,
|
'App\\Notifications\\Plan\\ChosenPlanTomorrowReminderToConsultantNotification' => NotificationType.chosenPlanTomorrowReminderToConsultantNotification,
|
||||||
'App\\Notifications\\Plan\\PlanSubscriptionPaidToAuthor' => NotificationType.planSubscriptionPaidToAuthor,
|
'App\\Notifications\\Plan\\PlanSubscriptionPaidToAuthor' => NotificationType.planSubscriptionPaidToAuthor,
|
||||||
'App\\Notifications\\Plan\\ChosenPlanFinishedToConsultant' => NotificationType.chosenPlanFinishedToConsultant,
|
'App\\Notifications\\Plan\\ChosenPlanFinishedToConsultant' => NotificationType.chosenPlanFinishedToConsultant,
|
||||||
|
'App\\Notifications\\ChosenPlan\\LastDayFinishedForUser' => NotificationType.chosenPlanLastDayFinishedForUser,
|
||||||
_ => NotificationType.unknown,
|
_ => NotificationType.unknown,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ class NotificationsCubit extends Cubit<NotificationsState> {
|
|||||||
ChosenPlanTomorrowReminderToConsultantNotification.fromJson(notificationJson, type),
|
ChosenPlanTomorrowReminderToConsultantNotification.fromJson(notificationJson, type),
|
||||||
NotificationType.planSubscriptionPaidToAuthor => PlanSubscriptionPaidToAuthorNotification.fromJson(notificationJson, type),
|
NotificationType.planSubscriptionPaidToAuthor => PlanSubscriptionPaidToAuthorNotification.fromJson(notificationJson, type),
|
||||||
NotificationType.chosenPlanFinishedToConsultant => ChosenPlanFinishedToConsultantNotification.fromJson(notificationJson, type),
|
NotificationType.chosenPlanFinishedToConsultant => ChosenPlanFinishedToConsultantNotification.fromJson(notificationJson, type),
|
||||||
|
NotificationType.chosenPlanLastDayFinishedForUser => ChosenPlanLastDayFinishedForUser.fromJson(notificationJson, type),
|
||||||
NotificationType.unknown => UnknownNotification(type: type),
|
NotificationType.unknown => UnknownNotification(type: type),
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -66,7 +67,7 @@ class NotificationsCubit extends Cubit<NotificationsState> {
|
|||||||
|
|
||||||
Future<void> getNotifications() async {
|
Future<void> getNotifications() async {
|
||||||
try {
|
try {
|
||||||
final notifications = await notificationsRepository.getNotifications(page: 2);
|
final notifications = await notificationsRepository.getNotifications(page: 1);
|
||||||
_notifications.clear();
|
_notifications.clear();
|
||||||
_notifications.addAll(notifications ?? []);
|
_notifications.addAll(notifications ?? []);
|
||||||
emit(
|
emit(
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ class NotificationWidget extends StatelessWidget {
|
|||||||
ChosenPlanFinishedToConsultantNotification() => _ChosenPlanFinishedToConsultantWidget(
|
ChosenPlanFinishedToConsultantNotification() => _ChosenPlanFinishedToConsultantWidget(
|
||||||
notification: notification as ChosenPlanFinishedToConsultantNotification,
|
notification: notification as ChosenPlanFinishedToConsultantNotification,
|
||||||
),
|
),
|
||||||
|
ChosenPlanLastDayFinishedForUser() => Text((notification as ChosenPlanLastDayFinishedForUser).type.toString()),
|
||||||
UnknownNotification() => const SizedBox.shrink(),
|
UnknownNotification() => const SizedBox.shrink(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -67,7 +68,7 @@ class _PlanSubscriptionStartedToAuthorWidget extends StatelessWidget {
|
|||||||
|
|
||||||
return _BaseNotificationWidget(
|
return _BaseNotificationWidget(
|
||||||
isRead: notification.isRead,
|
isRead: notification.isRead,
|
||||||
notificationsText: s.fillYasterday,
|
notificationsText: s.userSubscribedToPlan(notification.customerName, notification.planName),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,7 +82,7 @@ class _PlanSubscriptionCanceledToAuthorWidget extends StatelessWidget {
|
|||||||
|
|
||||||
return _BaseNotificationWidget(
|
return _BaseNotificationWidget(
|
||||||
isRead: notification.isRead,
|
isRead: notification.isRead,
|
||||||
notificationsText: s.fillYasterday,
|
notificationsText: s.userCanceldThePlanSubscription(notification.customerName, notification.planName),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -95,7 +96,7 @@ class _ChosenPlanStartedToClientWidget extends StatelessWidget {
|
|||||||
|
|
||||||
return _BaseNotificationWidget(
|
return _BaseNotificationWidget(
|
||||||
isRead: notification.isRead,
|
isRead: notification.isRead,
|
||||||
notificationsText: s.fillYasterday,
|
notificationsText: s.planStarted(notification.planName),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,7 +110,7 @@ class _ChosenPlanTomorrowReminderToClientWidget extends StatelessWidget {
|
|||||||
|
|
||||||
return _BaseNotificationWidget(
|
return _BaseNotificationWidget(
|
||||||
isRead: notification.isRead,
|
isRead: notification.isRead,
|
||||||
notificationsText: s.fillYasterday,
|
notificationsText: s.tomorrowYourPlanWillStart(notification.planName),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,11 @@ class NotificationsService {
|
|||||||
PlanSubscriptionPaidToAuthorNotification.fromJson(notificationJson['data'], type, isRead: readDate != null),
|
PlanSubscriptionPaidToAuthorNotification.fromJson(notificationJson['data'], type, isRead: readDate != null),
|
||||||
NotificationType.chosenPlanFinishedToConsultant =>
|
NotificationType.chosenPlanFinishedToConsultant =>
|
||||||
ChosenPlanFinishedToConsultantNotification.fromJson(notificationJson['data'], type, isRead: readDate != null),
|
ChosenPlanFinishedToConsultantNotification.fromJson(notificationJson['data'], type, isRead: readDate != null),
|
||||||
|
NotificationType.chosenPlanLastDayFinishedForUser => ChosenPlanLastDayFinishedForUser.fromJson(
|
||||||
|
notificationJson['data'],
|
||||||
|
type,
|
||||||
|
isRead: readDate != null,
|
||||||
|
),
|
||||||
NotificationType.unknown => UnknownNotification(type: type),
|
NotificationType.unknown => UnknownNotification(type: type),
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ class _AboutPlanScreenState extends State<AboutPlanScreen> {
|
|||||||
GoRouter.of(context).pushReplacementNamed(
|
GoRouter.of(context).pushReplacementNamed(
|
||||||
'chatScreen',
|
'chatScreen',
|
||||||
extra: {
|
extra: {
|
||||||
'chatId': chosenPlan.chatId,
|
'uuid': chosenPlan.chatId,
|
||||||
'sendInitialMessage': true,
|
'sendInitialMessage': true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,9 +14,11 @@ abstract class ProfileModel with _$ProfileModel {
|
|||||||
@JsonKey(name: "email") required String email,
|
@JsonKey(name: "email") required String email,
|
||||||
@JsonKey(name: "rating_place") int? ratingPlace,
|
@JsonKey(name: "rating_place") int? ratingPlace,
|
||||||
@JsonKey(name: "email_verified_at") DateTime? emailVerifiedAt,
|
@JsonKey(name: "email_verified_at") DateTime? emailVerifiedAt,
|
||||||
|
@JsonKey(name: "phone_confirmation_state") required String phoneConfirmationState,
|
||||||
@JsonKey(name: "created_at") DateTime? createdAt,
|
@JsonKey(name: "created_at") DateTime? createdAt,
|
||||||
@JsonKey(name: "updated_at") DateTime? updatedAt,
|
@JsonKey(name: "updated_at") DateTime? updatedAt,
|
||||||
@JsonKey(name: "is_public_profile", fromJson: parseBool) required bool isPublicProfile,
|
@JsonKey(name: "is_public_profile", fromJson: parseBool) required bool isPublicProfile,
|
||||||
|
@JsonKey(name: "is_blocked_by_me", fromJson: parseBool) required bool isBlockedByMe,
|
||||||
@JsonKey(name: 'is_entity_subscribed_by_me') required bool isEntitySubscribedByMe,
|
@JsonKey(name: 'is_entity_subscribed_by_me') required bool isEntitySubscribedByMe,
|
||||||
@JsonKey(name: "slug") required String slug,
|
@JsonKey(name: "slug") required String slug,
|
||||||
@JsonKey(name: "profile_photo_url") required String profilePhotoUrl,
|
@JsonKey(name: "profile_photo_url") required String profilePhotoUrl,
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class ProfileCubit extends Cubit<ProfileState> {
|
|||||||
}) : super(const ProfileState.loading());
|
}) : super(const ProfileState.loading());
|
||||||
|
|
||||||
Future<void> getProfileBySlug(String slug) async {
|
Future<void> getProfileBySlug(String slug) async {
|
||||||
|
emit(const ProfileState.loading());
|
||||||
try {
|
try {
|
||||||
final profile = await profileRepository.getProfileBySlug(slug);
|
final profile = await profileRepository.getProfileBySlug(slug);
|
||||||
emit(
|
emit(
|
||||||
@@ -44,6 +45,7 @@ class ProfileCubit extends Cubit<ProfileState> {
|
|||||||
Future<void> getMyProfile({
|
Future<void> getMyProfile({
|
||||||
String period = '1',
|
String period = '1',
|
||||||
}) async {
|
}) async {
|
||||||
|
emit(const ProfileState.loading());
|
||||||
_currentPeriod = period;
|
_currentPeriod = period;
|
||||||
try {
|
try {
|
||||||
final profile = await profileRepository.getMyProfile();
|
final profile = await profileRepository.getMyProfile();
|
||||||
@@ -123,7 +125,7 @@ class ProfileCubit extends Cubit<ProfileState> {
|
|||||||
AppRouter.navigatorKey.currentContext?.pushNamed(
|
AppRouter.navigatorKey.currentContext?.pushNamed(
|
||||||
'chatScreen',
|
'chatScreen',
|
||||||
extra: {
|
extra: {
|
||||||
'chatId': chatIdOrError,
|
'uuid': chatIdOrError,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return chatIdOrError;
|
return chatIdOrError;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -69,7 +69,7 @@ class _SubActionButtonWidgetState extends State<SubActionButtonWidget> {
|
|||||||
GoRouter.of(context).pushNamed(
|
GoRouter.of(context).pushNamed(
|
||||||
'chatScreen',
|
'chatScreen',
|
||||||
extra: {
|
extra: {
|
||||||
'chatId': await sl<ProfileCubit>().getChatRoomIdBySlug(
|
'uuid': await sl<ProfileCubit>().getChatRoomIdBySlug(
|
||||||
author: Author(
|
author: Author(
|
||||||
authorSlug: widget.slug,
|
authorSlug: widget.slug,
|
||||||
authorName: widget.name,
|
authorName: widget.name,
|
||||||
|
|||||||
@@ -203,4 +203,12 @@ class ProfileRepository {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> deleteAccount() async {
|
||||||
|
try {
|
||||||
|
await profileService.deleteAccount();
|
||||||
|
} catch (e) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,4 +119,7 @@ abstract class ProfileService {
|
|||||||
Future<void> tryVerifyEmailByCode({
|
Future<void> tryVerifyEmailByCode({
|
||||||
@Body() required String code,
|
@Body() required String code,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@DELETE('/api/profile/delete_my_account')
|
||||||
|
Future<void> deleteAccount();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:eatvio_app/core/app_router.dart';
|
import 'package:eatvio_app/core/app_router.dart';
|
||||||
|
import 'package:eatvio_app/core/firebase_api.dart';
|
||||||
|
import 'package:eatvio_app/core/locator_service.dart';
|
||||||
|
|
||||||
import 'package:eatvio_app/core/token_storage.dart';
|
import 'package:eatvio_app/core/token_storage.dart';
|
||||||
import 'package:eatvio_app/core/widgets/show_error_dialog.dart';
|
|
||||||
import 'package:eatvio_app/features/registration/models/user_registration_model.dart';
|
import 'package:eatvio_app/features/registration/models/user_registration_model.dart';
|
||||||
import 'package:eatvio_app/features/registration/presentation/bloc/registration_cubit/registration_state.dart';
|
import 'package:eatvio_app/features/registration/presentation/bloc/registration_cubit/registration_state.dart';
|
||||||
import 'package:eatvio_app/features/registration/repositories/registration_repository.dart';
|
import 'package:eatvio_app/features/registration/repositories/registration_repository.dart';
|
||||||
@@ -51,25 +52,19 @@ class RegistrationCubit extends Cubit<RegistrationState> {
|
|||||||
void updateSheet(UserRegistrationSheet sheet) => emit(RegistrationState(sheet: sheet));
|
void updateSheet(UserRegistrationSheet sheet) => emit(RegistrationState(sheet: sheet));
|
||||||
|
|
||||||
Future<void> tryRegistration() async {
|
Future<void> tryRegistration() async {
|
||||||
if (await registrationRepository.emailIsReachable(state.sheet.email)) {
|
try {
|
||||||
try {
|
final token = await registrationRepository.tryRegister(
|
||||||
final token = await registrationRepository.tryRegister(
|
state.sheet.copyWith(
|
||||||
state.sheet.copyWith(
|
phone: state.sheet.phone.replaceAll(' ', '').replaceAll('(', '').replaceAll(')', ''),
|
||||||
phone: state.sheet.phone.replaceAll(' ', '').replaceAll('(', '').replaceAll(')', ''),
|
responseBody: '{}',
|
||||||
responseBody: '{}',
|
),
|
||||||
),
|
|
||||||
);
|
|
||||||
await TokenStorage.saveTokenInStorage(token);
|
|
||||||
AppRouter.navigatorKey.currentContext?.pushNamed('verifyEmailScreen');
|
|
||||||
} catch (e) {
|
|
||||||
log(e.toString());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
showErrorDialog(
|
|
||||||
AppRouter.navigatorKey.currentContext!,
|
|
||||||
title: 'Ошибка регистрации',
|
|
||||||
description: 'Несуществующая почта.',
|
|
||||||
);
|
);
|
||||||
|
await TokenStorage.saveTokenInStorage(token);
|
||||||
|
await sl<FirebaseApi>().registerPushToken();
|
||||||
|
AppRouter.navigatorKey.currentContext?.replaceNamed('verifyEmailScreen');
|
||||||
|
} catch (e, st) {
|
||||||
|
log(e.toString(), stackTrace: st);
|
||||||
|
emit(RegistrationState(sheet: state.sheet, error: e.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,5 +7,6 @@ part 'registration_state.freezed.dart';
|
|||||||
abstract class RegistrationState with _$RegistrationState {
|
abstract class RegistrationState with _$RegistrationState {
|
||||||
const factory RegistrationState({
|
const factory RegistrationState({
|
||||||
required UserRegistrationSheet sheet,
|
required UserRegistrationSheet sheet,
|
||||||
|
String? error,
|
||||||
}) = _RegistrationState;
|
}) = _RegistrationState;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,142 +43,161 @@ class _RegistrationScreenState extends State<RegistrationScreen> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider<RegistrationCubit>(
|
return BlocProvider<RegistrationCubit>(
|
||||||
create: (BuildContext context) => registrationCubit,
|
create: (BuildContext context) => registrationCubit,
|
||||||
child: BlocBuilder<RegistrationCubit, RegistrationState>(
|
child: BlocListener<RegistrationCubit, RegistrationState>(
|
||||||
builder: (BuildContext context, RegistrationState state) {
|
listener: (context, state) {
|
||||||
return Scaffold(
|
if (state.error != null && state.error!.isNotEmpty) {
|
||||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
|
showDialog(
|
||||||
floatingActionButton: Row(
|
context: context,
|
||||||
children: [
|
builder: (context) => AlertDialog(
|
||||||
Container(
|
title: const Text('Ошибка при регистрации'),
|
||||||
height: 48,
|
content: Text(state.error!),
|
||||||
width: MediaQuery.of(context).size.width * 0.2,
|
actions: [
|
||||||
padding: const EdgeInsets.only(left: 16, right: 4),
|
TextButton(
|
||||||
child: FloatingActionButton.extended(
|
onPressed: () => Navigator.pop(context),
|
||||||
heroTag: 'backButtonRegistration',
|
child: const Text('OK'),
|
||||||
highlightElevation: 0,
|
|
||||||
focusElevation: 0,
|
|
||||||
elevation: 0,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
|
||||||
backgroundColor: const Color(0xFFEAF2FD),
|
|
||||||
onPressed: () async {
|
|
||||||
setState(() {
|
|
||||||
pageController.previousPage(
|
|
||||||
duration: const Duration(milliseconds: 350),
|
|
||||||
curve: Curves.easeInOut,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
label: Icon(
|
|
||||||
Icons.arrow_back_ios_new_rounded,
|
|
||||||
color: appTheme.blueA400,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
Container(
|
),
|
||||||
height: 48,
|
);
|
||||||
width: MediaQuery.of(context).size.width * 0.8,
|
}
|
||||||
padding: const EdgeInsets.only(left: 4, right: 16),
|
},
|
||||||
child: FloatingActionButton.extended(
|
child: BlocBuilder<RegistrationCubit, RegistrationState>(
|
||||||
heroTag: 'nextButtonRegistration',
|
builder: (BuildContext context, RegistrationState state) {
|
||||||
highlightElevation: 0,
|
return Scaffold(
|
||||||
elevation: 0,
|
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
|
||||||
focusElevation: 0,
|
floatingActionButton: Row(
|
||||||
shape: RoundedRectangleBorder(
|
children: [
|
||||||
borderRadius: BorderRadius.circular(20),
|
Container(
|
||||||
),
|
height: 48,
|
||||||
backgroundColor: appTheme.blueA400,
|
width: MediaQuery.of(context).size.width * 0.2,
|
||||||
onPressed: () async {
|
padding: const EdgeInsets.only(left: 16, right: 4),
|
||||||
if (await canGoNext(state, context)) {
|
child: FloatingActionButton.extended(
|
||||||
|
heroTag: 'backButtonRegistration',
|
||||||
|
highlightElevation: 0,
|
||||||
|
focusElevation: 0,
|
||||||
|
elevation: 0,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
backgroundColor: const Color(0xFFEAF2FD),
|
||||||
|
onPressed: () async {
|
||||||
setState(() {
|
setState(() {
|
||||||
pageController.nextPage(
|
pageController.previousPage(
|
||||||
duration: const Duration(milliseconds: 350),
|
duration: const Duration(milliseconds: 350),
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
},
|
label: Icon(
|
||||||
label: Text(
|
Icons.arrow_back_ios_new_rounded,
|
||||||
'Продолжить',
|
color: appTheme.blueA400,
|
||||||
style: theme.textTheme.titleSmall!.copyWith(color: Colors.white),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Container(
|
||||||
],
|
height: 48,
|
||||||
),
|
width: MediaQuery.of(context).size.width * 0.8,
|
||||||
appBar: AppBar(
|
padding: const EdgeInsets.only(left: 4, right: 16),
|
||||||
automaticallyImplyLeading: false,
|
child: FloatingActionButton.extended(
|
||||||
leading: Builder(
|
heroTag: 'nextButtonRegistration',
|
||||||
builder: (context) => IconButton(
|
highlightElevation: 0,
|
||||||
icon: const Icon(Icons.close_rounded),
|
elevation: 0,
|
||||||
onPressed: () => GoRouter.of(context).pop(),
|
focusElevation: 0,
|
||||||
),
|
shape: RoundedRectangleBorder(
|
||||||
),
|
borderRadius: BorderRadius.circular(20),
|
||||||
centerTitle: true,
|
),
|
||||||
title: SvgPicture.asset(
|
backgroundColor: appTheme.blueA400,
|
||||||
ImageConstant.imgLogoEatvio,
|
onPressed: () async {
|
||||||
height: 48,
|
if (await canGoNext(state, context)) {
|
||||||
fit: BoxFit.contain,
|
setState(() {
|
||||||
),
|
pageController.nextPage(
|
||||||
actions: [
|
duration: const Duration(milliseconds: 350),
|
||||||
Padding(
|
curve: Curves.easeInOut,
|
||||||
padding: const EdgeInsets.only(right: 16),
|
);
|
||||||
child: Text('шаг $step из 15'),
|
});
|
||||||
),
|
}
|
||||||
],
|
},
|
||||||
bottom: PreferredSize(
|
label: Text(
|
||||||
preferredSize: Size(MediaQuery.of(context).size.width, 32),
|
'Продолжить',
|
||||||
child: Padding(
|
style: theme.textTheme.titleSmall!.copyWith(color: Colors.white),
|
||||||
padding: const EdgeInsets.all(16),
|
),
|
||||||
child: TweenAnimationBuilder<double>(
|
|
||||||
duration: const Duration(milliseconds: 350),
|
|
||||||
curve: Curves.easeInOut,
|
|
||||||
tween: Tween<double>(
|
|
||||||
begin: 1 / 15,
|
|
||||||
end: progressValue,
|
|
||||||
),
|
),
|
||||||
builder: (context, value, _) => LinearProgressIndicator(
|
),
|
||||||
value: value,
|
],
|
||||||
borderRadius: BorderRadius.circular(16),
|
),
|
||||||
minHeight: 8,
|
appBar: AppBar(
|
||||||
backgroundColor: Colors.grey.shade100,
|
automaticallyImplyLeading: false,
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(appTheme.blueA400),
|
leading: Builder(
|
||||||
|
builder: (context) => IconButton(
|
||||||
|
icon: const Icon(Icons.close_rounded),
|
||||||
|
onPressed: () => GoRouter.of(context).pop(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
centerTitle: true,
|
||||||
|
title: SvgPicture.asset(
|
||||||
|
ImageConstant.imgLogoEatvio,
|
||||||
|
height: 48,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 16),
|
||||||
|
child: Text('шаг $step из 15'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
bottom: PreferredSize(
|
||||||
|
preferredSize: Size(MediaQuery.of(context).size.width, 32),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: TweenAnimationBuilder<double>(
|
||||||
|
duration: const Duration(milliseconds: 350),
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
tween: Tween<double>(
|
||||||
|
begin: 1 / 15,
|
||||||
|
end: progressValue,
|
||||||
|
),
|
||||||
|
builder: (context, value, _) => LinearProgressIndicator(
|
||||||
|
value: value,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
minHeight: 8,
|
||||||
|
backgroundColor: Colors.grey.shade100,
|
||||||
|
valueColor: AlwaysStoppedAnimation<Color>(appTheme.blueA400),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
body: PageView(
|
||||||
body: PageView(
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
controller: pageController,
|
||||||
controller: pageController,
|
onPageChanged: (index) {
|
||||||
onPageChanged: (index) {
|
setState(() {
|
||||||
setState(() {
|
step = index + 1;
|
||||||
step = index + 1;
|
progressValue = step / 15;
|
||||||
progressValue = step / 15;
|
});
|
||||||
});
|
},
|
||||||
},
|
children: [
|
||||||
children: [
|
// LanguageSelectScreen(sheet: state.sheet),
|
||||||
// LanguageSelectScreen(sheet: state.sheet),
|
PhoneScreen(sheet: state.sheet),
|
||||||
PhoneScreen(sheet: state.sheet),
|
CredentialsScreen(sheet: state.sheet),
|
||||||
CredentialsScreen(sheet: state.sheet),
|
NameScreen(sheet: state.sheet),
|
||||||
NameScreen(sheet: state.sheet),
|
GenderScreen(sheet: state.sheet),
|
||||||
GenderScreen(sheet: state.sheet),
|
DateOfBirthScreen(sheet: state.sheet),
|
||||||
DateOfBirthScreen(sheet: state.sheet),
|
CityOfLivingScreen(sheet: state.sheet),
|
||||||
CityOfLivingScreen(sheet: state.sheet),
|
BodyCircumferencesScreen(sheet: state.sheet),
|
||||||
BodyCircumferencesScreen(sheet: state.sheet),
|
WeightScreen(sheet: state.sheet),
|
||||||
WeightScreen(sheet: state.sheet),
|
HeightScreen(sheet: state.sheet),
|
||||||
HeightScreen(sheet: state.sheet),
|
BloodPressureScreen(sheet: state.sheet),
|
||||||
BloodPressureScreen(sheet: state.sheet),
|
AllergiesScreen(sheet: state.sheet),
|
||||||
AllergiesScreen(sheet: state.sheet),
|
MedicineIntakeScreen(sheet: state.sheet),
|
||||||
MedicineIntakeScreen(sheet: state.sheet),
|
FavoriteSportsScreen(sheet: state.sheet),
|
||||||
FavoriteSportsScreen(sheet: state.sheet),
|
FavoriteFoodsScreen(sheet: state.sheet),
|
||||||
FavoriteFoodsScreen(sheet: state.sheet),
|
AdditionalInformationScreen(sheet: state.sheet),
|
||||||
AdditionalInformationScreen(sheet: state.sheet),
|
],
|
||||||
],
|
),
|
||||||
),
|
);
|
||||||
);
|
},
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:eatvio_app/core/const.dart';
|
import 'package:eatvio_app/core/const.dart';
|
||||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||||
|
import 'package:eatvio_app/core/widgets/loader.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:lottie/lottie.dart';
|
import 'package:lottie/lottie.dart';
|
||||||
@@ -19,6 +20,7 @@ class VerifyScreen extends StatefulWidget {
|
|||||||
|
|
||||||
class _VerifyScreenState extends State<VerifyScreen> {
|
class _VerifyScreenState extends State<VerifyScreen> {
|
||||||
late final WebViewController _controller;
|
late final WebViewController _controller;
|
||||||
|
bool _isLoading = true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -27,6 +29,11 @@ class _VerifyScreenState extends State<VerifyScreen> {
|
|||||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||||
..setNavigationDelegate(
|
..setNavigationDelegate(
|
||||||
NavigationDelegate(
|
NavigationDelegate(
|
||||||
|
onPageFinished: (url) {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
onNavigationRequest: (NavigationRequest request) async {
|
onNavigationRequest: (NavigationRequest request) async {
|
||||||
if (request.url.contains('user_detailz') && !request.url.contains('change-phone-verify') || request.url.contains('email/verify')) {
|
if (request.url.contains('user_detailz') && !request.url.contains('change-phone-verify') || request.url.contains('email/verify')) {
|
||||||
GoRouter.of(context).pop();
|
GoRouter.of(context).pop();
|
||||||
@@ -105,11 +112,17 @@ class _VerifyScreenState extends State<VerifyScreen> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(toolbarHeight: 0),
|
||||||
toolbarHeight: 0,
|
|
||||||
),
|
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: WebViewWidget(controller: _controller),
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
WebViewWidget(controller: _controller),
|
||||||
|
if (_isLoading)
|
||||||
|
const Center(
|
||||||
|
child: Loader(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:eatvio_app/core/locator_service.dart';
|
|||||||
import 'package:eatvio_app/features/registration/models/user_registration_model.dart';
|
import 'package:eatvio_app/features/registration/models/user_registration_model.dart';
|
||||||
import 'package:eatvio_app/features/registration/models/location_model.dart';
|
import 'package:eatvio_app/features/registration/models/location_model.dart';
|
||||||
import 'package:eatvio_app/features/registration/services/registration_service.dart';
|
import 'package:eatvio_app/features/registration/services/registration_service.dart';
|
||||||
|
import 'package:eatvio_app/core/env_config.dart';
|
||||||
|
|
||||||
class RegistrationRepository {
|
class RegistrationRepository {
|
||||||
final RegistrationService registrationService;
|
final RegistrationService registrationService;
|
||||||
@@ -11,8 +12,56 @@ class RegistrationRepository {
|
|||||||
|
|
||||||
Future<String> tryRegister(UserRegistrationSheet sheet) async {
|
Future<String> tryRegister(UserRegistrationSheet sheet) async {
|
||||||
try {
|
try {
|
||||||
return (await registrationService.register(sheet: sheet.toJson()))['token']!;
|
final response = await registrationService.register(sheet: sheet.toJson());
|
||||||
} catch (e) {
|
if (response.containsKey('token')) {
|
||||||
|
return response['token']!;
|
||||||
|
} else {
|
||||||
|
// Если нет токена, но есть message или errors
|
||||||
|
String errorMsg = '';
|
||||||
|
if (response.containsKey('message')) {
|
||||||
|
errorMsg += '${response['message']!}\n';
|
||||||
|
}
|
||||||
|
if (response.containsKey('errors')) {
|
||||||
|
final errors = response['errors'];
|
||||||
|
if (errors != null && errors is Map) {
|
||||||
|
(errors as Map).forEach((key, value) {
|
||||||
|
if (value is List && value.isNotEmpty) {
|
||||||
|
errorMsg += '${value.join('\n')}\n';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (errorMsg.isNotEmpty) {
|
||||||
|
throw errorMsg.trim();
|
||||||
|
} else {
|
||||||
|
throw 'Ошибка регистрации: $response';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} on DioException catch (e) {
|
||||||
|
if (e.response != null && e.response?.statusCode == 422) {
|
||||||
|
final data = e.response?.data;
|
||||||
|
String errorMsg = '';
|
||||||
|
if (data is Map<String, dynamic>) {
|
||||||
|
if (data.containsKey('message')) {
|
||||||
|
errorMsg += data['message'] + '\n';
|
||||||
|
}
|
||||||
|
if (data.containsKey('errors')) {
|
||||||
|
final errors = data['errors'];
|
||||||
|
if (errors != null && errors is Map) {
|
||||||
|
(errors).forEach((key, value) {
|
||||||
|
if (value is List && value.isNotEmpty) {
|
||||||
|
errorMsg += '${value.join('\n')}\n';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (errorMsg.isNotEmpty) {
|
||||||
|
throw errorMsg.trim();
|
||||||
|
} else {
|
||||||
|
throw 'Ошибка регистрации: $data';
|
||||||
|
}
|
||||||
|
}
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -35,7 +84,7 @@ class RegistrationRepository {
|
|||||||
|
|
||||||
Future<bool> emailIsReachable(String email) async {
|
Future<bool> emailIsReachable(String email) async {
|
||||||
try {
|
try {
|
||||||
final response = await sl<Dio>().getUri(Uri.parse('https://api.validate.email/validate?email=$email'));
|
final response = await sl<Dio>().getUri(Uri.parse('${EnvConfig.emailValidationApiUrl}?email=$email'));
|
||||||
if (response.data['result']['reachable'] != 'invalid') {
|
if (response.data['result']['reachable'] != 'invalid') {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -22,7 +22,11 @@ class SettingsCubit extends Cubit<SettingsState> {
|
|||||||
final profile = await profileRepository.getMyProfile();
|
final profile = await profileRepository.getMyProfile();
|
||||||
late final ConsultationRequestModel? consultantInfo;
|
late final ConsultationRequestModel? consultantInfo;
|
||||||
try {
|
try {
|
||||||
consultantInfo = await consultantRepository.getMyRequestStatus(profile.slug);
|
if (profile.phoneConfirmationState.contains('confirmed')) {
|
||||||
|
consultantInfo = await consultantRepository.getMyRequestStatus(profile.slug);
|
||||||
|
} else {
|
||||||
|
consultantInfo = null;
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
consultantInfo = null;
|
consultantInfo = null;
|
||||||
}
|
}
|
||||||
@@ -67,4 +71,13 @@ class SettingsCubit extends Cubit<SettingsState> {
|
|||||||
await init();
|
await init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> deleteAccount() async {
|
||||||
|
try {
|
||||||
|
await profileRepository.deleteAccount();
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
|
import 'package:eatvio_app/core/app_router.dart';
|
||||||
import 'package:eatvio_app/core/const.dart';
|
import 'package:eatvio_app/core/const.dart';
|
||||||
|
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||||
|
import 'package:eatvio_app/core/widgets/bottom_sheet_handler_bar.dart';
|
||||||
|
import 'package:eatvio_app/core/widgets/bottom_sheet_with_blur.dart';
|
||||||
|
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
|
||||||
import 'package:eatvio_app/core/widgets/data_error_widget.dart';
|
import 'package:eatvio_app/core/widgets/data_error_widget.dart';
|
||||||
import 'package:eatvio_app/core/widgets/loader.dart';
|
import 'package:eatvio_app/core/widgets/loader.dart';
|
||||||
|
import 'package:eatvio_app/core/widgets/show_error_dialog.dart';
|
||||||
import 'package:eatvio_app/features/consultant/become_consultant/models/consultation_request_model.dart';
|
import 'package:eatvio_app/features/consultant/become_consultant/models/consultation_request_model.dart';
|
||||||
import 'package:eatvio_app/features/settings/entities/settings_entity.dart';
|
import 'package:eatvio_app/features/settings/entities/settings_entity.dart';
|
||||||
import 'package:eatvio_app/features/settings/presentation/bloc/settings_cubit/settings_cubit.dart';
|
import 'package:eatvio_app/features/settings/presentation/bloc/settings_cubit/settings_cubit.dart';
|
||||||
import 'package:eatvio_app/features/settings/presentation/bloc/settings_cubit/settings_state.dart';
|
import 'package:eatvio_app/features/settings/presentation/bloc/settings_cubit/settings_state.dart';
|
||||||
import 'package:eatvio_app/core/locator_service.dart';
|
import 'package:eatvio_app/core/locator_service.dart';
|
||||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||||
|
import 'package:eatvio_app/generated/l10n.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
@@ -60,7 +67,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
_aboutEatvio(state),
|
_aboutEatvio(state),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
_becomeConsultantButton(state),
|
_becomeConsultantButton(state),
|
||||||
_deleteAccountButton(state),
|
_deleteAccountButton(state, settingsCubit),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -428,11 +435,20 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
Padding _deleteAccountButton(SettingsStateLoaded state) {
|
Padding _deleteAccountButton(SettingsStateLoaded state, SettingsCubit settingsCubit) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {},
|
onTap: () async {
|
||||||
|
showBottomSheetWithBlur(
|
||||||
|
userRootNavigator: true,
|
||||||
|
context: context,
|
||||||
|
height: null,
|
||||||
|
child: DeleteAccountBottomSheet(
|
||||||
|
cubit: settingsCubit,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
child: Ink(
|
child: Ink(
|
||||||
height: 52,
|
height: 52,
|
||||||
@@ -495,3 +511,73 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DeleteAccountBottomSheet extends StatefulWidget {
|
||||||
|
const DeleteAccountBottomSheet({super.key, required this.cubit});
|
||||||
|
final SettingsCubit cubit;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<DeleteAccountBottomSheet> createState() => _DeleteAccountBottomSheet();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DeleteAccountBottomSheet extends State<DeleteAccountBottomSheet> {
|
||||||
|
bool? isFailure;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final s = S.of(context);
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
const BottomSheetHandlerBar(),
|
||||||
|
const SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
Text(s.accountDeleting, style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black)),
|
||||||
|
const SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
s.youShureYouWantToDeleteAccount,
|
||||||
|
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
s.itCantBeRecovered,
|
||||||
|
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
CustomOutlinedButton(
|
||||||
|
text: s.delete,
|
||||||
|
height: 52,
|
||||||
|
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
||||||
|
buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.blueA400)),
|
||||||
|
onPressed: () async {
|
||||||
|
final result = await widget.cubit.deleteAccount();
|
||||||
|
if (result == true) {
|
||||||
|
AppRouter.navigatorKey.currentContext?.go('/auth-screen');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AppRouter.navigatorKey.currentContext?.pop();
|
||||||
|
if (!context.mounted) return;
|
||||||
|
showErrorDialog(context, description: s.cannotDeleteAccount);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 40,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
import 'package:eatvio_app/core/env_config.dart';
|
||||||
|
|
||||||
class SupportScreen extends StatelessWidget {
|
class SupportScreen extends StatelessWidget {
|
||||||
const SupportScreen({super.key});
|
const SupportScreen({super.key});
|
||||||
@@ -15,48 +16,28 @@ class SupportScreen extends StatelessWidget {
|
|||||||
final s = S.of(context);
|
final s = S.of(context);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(title: const Text('Поддержка')),
|
||||||
title: const Text('Поддержка'),
|
|
||||||
),
|
|
||||||
body: Column(
|
body: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.all(16),
|
padding: EdgeInsets.all(16),
|
||||||
child: Text(
|
child: Text('Если у вас есть вопросы по работе нашего приложения, вы можете задать их, перейдя по ссылке:', style: TextStyle(fontSize: 16)),
|
||||||
'Если у вас есть вопросы по работе нашего приложения, вы можете задать их, перейдя по ссылке:',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
launchUrl(
|
launchUrl(Uri.parse(EnvConfig.telegramSupportUrl), mode: LaunchMode.externalApplication);
|
||||||
Uri.parse('https://t.me/eatvio'),
|
|
||||||
mode: LaunchMode.externalApplication,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
child: Ink(
|
child: Ink(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(borderRadius: BorderRadius.circular(16)),
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
SvgPicture.asset(
|
SvgPicture.asset('assets/icons/telegram.svg', colorFilter: const ColorFilter.mode(Colors.black, BlendMode.srcIn)),
|
||||||
'assets/icons/telegram.svg',
|
|
||||||
colorFilter: const ColorFilter.mode(Colors.black, BlendMode.srcIn),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
const Text(
|
Text('Telegram: ${EnvConfig.telegramSupportUrl.replaceFirst('https://', '')}', style: const TextStyle(fontSize: 16)),
|
||||||
'Telegram: t.me/eatvio',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -64,26 +45,19 @@ class SupportScreen extends StatelessWidget {
|
|||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Clipboard.setData(const ClipboardData(text: 'help@eatv.io'));
|
Clipboard.setData(ClipboardData(text: EnvConfig.supportEmail));
|
||||||
showInfoToast(s.mail_was_copied);
|
showInfoToast(s.mail_was_copied);
|
||||||
},
|
},
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
child: Ink(
|
child: Ink(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(borderRadius: BorderRadius.circular(16)),
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: const Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.alternate_email_rounded),
|
const Icon(Icons.alternate_email_rounded),
|
||||||
SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Text(
|
Text('E-mail: ${EnvConfig.supportEmail}', style: const TextStyle(fontSize: 16)),
|
||||||
'E-mail: help@eatv.io',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -92,13 +66,7 @@ class SupportScreen extends StatelessWidget {
|
|||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
const Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: Text(
|
child: Text('Правовые документы', style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500)),
|
||||||
'Правовые документы',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
InkWell(
|
InkWell(
|
||||||
@@ -106,12 +74,7 @@ class SupportScreen extends StatelessWidget {
|
|||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
child: const Padding(
|
child: const Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||||
child: Text(
|
child: Text('Публичная офёрта', style: TextStyle(fontSize: 16)),
|
||||||
'Публичная офёрта',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
@@ -120,12 +83,7 @@ class SupportScreen extends StatelessWidget {
|
|||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
child: const Padding(
|
child: const Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||||
child: Text(
|
child: Text('Политика конфиденциальности', style: TextStyle(fontSize: 16)),
|
||||||
'Политика конфиденциальности',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
@@ -134,12 +92,7 @@ class SupportScreen extends StatelessWidget {
|
|||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
child: const Padding(
|
child: const Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||||
child: Text(
|
child: Text('О компании', style: TextStyle(fontSize: 16)),
|
||||||
'О компании',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:flutter_svg/flutter_svg.dart';
|
|||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
import '../../../../core/theme/theme_helper.dart';
|
import '../../../../core/theme/theme_helper.dart';
|
||||||
|
import '../../../../core/env_config.dart';
|
||||||
|
|
||||||
class UpdateScreen extends StatelessWidget {
|
class UpdateScreen extends StatelessWidget {
|
||||||
const UpdateScreen({super.key});
|
const UpdateScreen({super.key});
|
||||||
@@ -17,16 +18,7 @@ class UpdateScreen extends StatelessWidget {
|
|||||||
body: Stack(
|
body: Stack(
|
||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
children: [
|
children: [
|
||||||
Positioned(
|
Positioned(left: -20, top: -20, right: -20, bottom: -20, child: Image.asset('assets/images/bg_welcome.png', fit: BoxFit.cover)),
|
||||||
left: -20,
|
|
||||||
top: -20,
|
|
||||||
right: -20,
|
|
||||||
bottom: -20,
|
|
||||||
child: Image.asset(
|
|
||||||
'assets/images/bg_welcome.png',
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@@ -48,47 +40,26 @@ class UpdateScreen extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
const Text(
|
const Text('Пора обновиться! ⚡', style: TextStyle(fontSize: 28, fontWeight: FontWeight.w800, height: 1)),
|
||||||
'Пора обновиться! ⚡',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 28,
|
|
||||||
fontWeight: FontWeight.w800,
|
|
||||||
height: 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
const Text(
|
const Text(
|
||||||
'Мы выпустили важное обновление\nс новыми функциями и исправлениями.\nЧтобы продолжить пользоваться\nприложением без сбоев, обновите\nего прямо сейчас.',
|
'Мы выпустили важное обновление\nс новыми функциями и исправлениями.\nЧтобы продолжить пользоваться\nприложением без сбоев, обновите\nего прямо сейчас.',
|
||||||
style: TextStyle(
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, height: 1.15),
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
height: 1.15,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 32),
|
const SizedBox(height: 32),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final Uri url = Platform.isAndroid
|
final Uri url = Platform.isAndroid ? Uri.parse(EnvConfig.rustoreUrl) : Uri.parse(EnvConfig.appStoreUrl);
|
||||||
? Uri.parse('https://www.rustore.ru/catalog/app/com.eatvioapp.app')
|
|
||||||
: Uri.parse('https://apps.apple.com/app/idYOUR_APP_ID');
|
|
||||||
await launchUrl(url);
|
await launchUrl(url);
|
||||||
},
|
},
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
child: Ink(
|
child: Ink(
|
||||||
height: 52,
|
height: 52,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(color: appTheme.blueA400, borderRadius: BorderRadius.circular(8)),
|
||||||
color: appTheme.blueA400,
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
'Обновить',
|
'Обновить',
|
||||||
style: TextStyle(
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, height: 1, color: Colors.white),
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
height: 1,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -20,19 +20,11 @@ class UserDetailsCubit extends Cubit<UserDetailsState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void getChartData({
|
void getChartData({String period = '1', required String slug}) async {
|
||||||
String period = '1',
|
|
||||||
required String slug,
|
|
||||||
}) async {
|
|
||||||
try {
|
try {
|
||||||
final weightPoints = await profileRepository.getWeight(period: period);
|
final weightPoints = await profileRepository.getWeight(period: period, slug: slug);
|
||||||
final circumferencePoints = await profileRepository.getCircumferences(period: period);
|
final circumferencePoints = await profileRepository.getCircumferences(period: period, slug: slug);
|
||||||
emit(
|
emit(state.copyWith(weightPoints: weightPoints, circumferencePoints: circumferencePoints));
|
||||||
state.copyWith(
|
|
||||||
weightPoints: weightPoints,
|
|
||||||
circumferencePoints: circumferencePoints,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
log(e.toString(), stackTrace: stackTrace);
|
log(e.toString(), stackTrace: stackTrace);
|
||||||
emit(state.copyWith(error: e.toString(), isLoading: false));
|
emit(state.copyWith(error: e.toString(), isLoading: false));
|
||||||
|
|||||||
@@ -31,9 +31,7 @@ class UserDetailsScreen extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => sl<UserDetailsCubit>()..getUserDetails(slug),
|
create: (context) => sl<UserDetailsCubit>()..getUserDetails(slug),
|
||||||
child: UserDetailsContent(
|
child: UserDetailsContent(slug: slug),
|
||||||
slug: slug,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,27 +77,15 @@ class _UserDetailsContentState extends State<UserDetailsContent> {
|
|||||||
actions: [
|
actions: [
|
||||||
PortalTarget(
|
PortalTarget(
|
||||||
visible: isMoreOptionsVisible,
|
visible: isMoreOptionsVisible,
|
||||||
portalFollower: MoreOptions(
|
portalFollower: MoreOptions(path: 'profile/${widget.slug}', complainedContentType: ComplainedContent.profile, slug: widget.slug),
|
||||||
path: 'profile/${widget.slug}',
|
anchor: const Aligned(shiftToWithinBound: AxisFlag(x: true, y: true), follower: Alignment.topRight, target: Alignment.bottomRight),
|
||||||
complainedContentType: ComplainedContent.profile,
|
|
||||||
slug: widget.slug,
|
|
||||||
),
|
|
||||||
anchor: const Aligned(
|
|
||||||
shiftToWithinBound: AxisFlag(x: true, y: true),
|
|
||||||
follower: Alignment.topRight,
|
|
||||||
target: Alignment.bottomRight,
|
|
||||||
),
|
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
isMoreOptionsVisible = !isMoreOptionsVisible;
|
isMoreOptionsVisible = !isMoreOptionsVisible;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
icon: SvgPicture.asset(
|
icon: SvgPicture.asset('assets/icons/more_button.svg', width: 20, height: 20),
|
||||||
'assets/icons/more_button.svg',
|
|
||||||
width: 20,
|
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -124,25 +110,16 @@ class _UserDetailsContentState extends State<UserDetailsContent> {
|
|||||||
initiallyExpanded: true,
|
initiallyExpanded: true,
|
||||||
title: s.general_info,
|
title: s.general_info,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(s.date_of_birth, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
|
||||||
s.date_of_birth,
|
|
||||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
CustomTextFormField(
|
CustomTextFormField(
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
controller: TextEditingController(text: DateTime.parse(profileDetails.birthdate).format('dd.MM.yyyy')),
|
controller: TextEditingController(text: DateTime.parse(profileDetails.birthdate).format('dd.MM.yyyy')),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Text(
|
Text(s.sex, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
|
||||||
s.sex,
|
|
||||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
CustomTextFormField(
|
CustomTextFormField(readOnly: true, controller: TextEditingController(text: profileDetails.gender.parseGender())),
|
||||||
readOnly: true,
|
|
||||||
controller: TextEditingController(text: profileDetails.gender.parseGender()),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -155,15 +132,9 @@ class _UserDetailsContentState extends State<UserDetailsContent> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(s.height, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
|
||||||
s.height,
|
|
||||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
CustomTextFormField(
|
CustomTextFormField(readOnly: true, controller: TextEditingController(text: profileDetails.height.toString())),
|
||||||
readOnly: true,
|
|
||||||
controller: TextEditingController(text: profileDetails.height.toString()),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -172,15 +143,9 @@ class _UserDetailsContentState extends State<UserDetailsContent> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(s.weight, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
|
||||||
s.weight,
|
|
||||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
CustomTextFormField(
|
CustomTextFormField(readOnly: true, controller: TextEditingController(text: profileDetails.weight.toStringAsFixed(0))),
|
||||||
readOnly: true,
|
|
||||||
controller: TextEditingController(text: profileDetails.weight.toStringAsFixed(0)),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -193,15 +158,9 @@ class _UserDetailsContentState extends State<UserDetailsContent> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(s.chest_circumference, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
|
||||||
s.chest_circumference,
|
|
||||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
CustomTextFormField(
|
CustomTextFormField(readOnly: true, controller: TextEditingController(text: profileDetails.chestCircumference.toString())),
|
||||||
readOnly: true,
|
|
||||||
controller: TextEditingController(text: profileDetails.chestCircumference.toString()),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -210,15 +169,9 @@ class _UserDetailsContentState extends State<UserDetailsContent> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(s.waist_circumference, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
|
||||||
s.waist_circumference,
|
|
||||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
CustomTextFormField(
|
CustomTextFormField(readOnly: true, controller: TextEditingController(text: profileDetails.waistCircumference.toString())),
|
||||||
readOnly: true,
|
|
||||||
controller: TextEditingController(text: profileDetails.waistCircumference.toString()),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -227,15 +180,9 @@ class _UserDetailsContentState extends State<UserDetailsContent> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(s.hip_circumference, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
|
||||||
s.hip_circumference,
|
|
||||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
CustomTextFormField(
|
CustomTextFormField(readOnly: true, controller: TextEditingController(text: profileDetails.hipCircumference.toString())),
|
||||||
readOnly: true,
|
|
||||||
controller: TextEditingController(text: profileDetails.hipCircumference.toString()),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -253,15 +200,9 @@ class _UserDetailsContentState extends State<UserDetailsContent> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(s.upper, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
|
||||||
s.upper,
|
|
||||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
CustomTextFormField(
|
CustomTextFormField(readOnly: true, controller: TextEditingController(text: profileDetails.bloodPressureUp.toString())),
|
||||||
readOnly: true,
|
|
||||||
controller: TextEditingController(text: profileDetails.bloodPressureUp.toString()),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -270,10 +211,7 @@ class _UserDetailsContentState extends State<UserDetailsContent> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(s.lower, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
|
||||||
s.lower,
|
|
||||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
CustomTextFormField(
|
CustomTextFormField(
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
@@ -291,15 +229,9 @@ class _UserDetailsContentState extends State<UserDetailsContent> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(s.has_hypo, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
|
||||||
s.has_hypo,
|
|
||||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
CustomTextFormField(
|
CustomTextFormField(readOnly: true, controller: TextEditingController(text: profileDetails.hasHypo.toString())),
|
||||||
readOnly: true,
|
|
||||||
controller: TextEditingController(text: profileDetails.hasHypo.toString()),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -308,15 +240,9 @@ class _UserDetailsContentState extends State<UserDetailsContent> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(s.has_hyper, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
|
||||||
s.has_hyper,
|
|
||||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
CustomTextFormField(
|
CustomTextFormField(readOnly: true, controller: TextEditingController(text: profileDetails.hasHyper.toString())),
|
||||||
readOnly: true,
|
|
||||||
controller: TextEditingController(text: profileDetails.hasHyper.toString()),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -328,60 +254,27 @@ class _UserDetailsContentState extends State<UserDetailsContent> {
|
|||||||
ExpandableBouble(
|
ExpandableBouble(
|
||||||
title: s.other_info,
|
title: s.other_info,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(s.has_alergy, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
|
||||||
s.has_alergy,
|
|
||||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
CustomTextFormField(
|
CustomTextFormField(readOnly: true, controller: TextEditingController(text: profileDetails.aboutAlergy)),
|
||||||
readOnly: true,
|
|
||||||
controller: TextEditingController(text: profileDetails.aboutAlergy),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Text(
|
Text(s.takes_pills, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
|
||||||
s.takes_pills,
|
|
||||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
CustomTextFormField(
|
CustomTextFormField(readOnly: true, controller: TextEditingController(text: profileDetails.aboutTakingPils)),
|
||||||
readOnly: true,
|
|
||||||
controller: TextEditingController(text: profileDetails.aboutTakingPils),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Text(
|
Text(s.sport_hobbies, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
|
||||||
s.sport_hobbies,
|
|
||||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
CustomTextFormField(
|
CustomTextFormField(readOnly: true, controller: TextEditingController(text: profileDetails.aboutSportHobbies)),
|
||||||
readOnly: true,
|
|
||||||
controller: TextEditingController(text: profileDetails.aboutSportHobbies),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Text(
|
Text(s.liked_groceries, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
|
||||||
s.liked_groceries,
|
|
||||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
CustomTextFormField(
|
CustomTextFormField(readOnly: true, controller: TextEditingController(text: profileDetails.favoriteFoods)),
|
||||||
readOnly: true,
|
|
||||||
controller: TextEditingController(text: profileDetails.favoriteFoods),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Text(
|
Text(s.not_liked_groceries, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
|
||||||
s.not_liked_groceries,
|
|
||||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
CustomTextFormField(
|
CustomTextFormField(readOnly: true, controller: TextEditingController(text: profileDetails.unfavoriteFoods)),
|
||||||
readOnly: true,
|
|
||||||
controller: TextEditingController(text: profileDetails.unfavoriteFoods),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Text(
|
Text(s.additional_information, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
|
||||||
s.additional_information,
|
|
||||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 112,
|
height: 112,
|
||||||
@@ -413,10 +306,7 @@ class _UserDetailsContentState extends State<UserDetailsContent> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return Scaffold(
|
return Scaffold(appBar: AppBar(), body: const Loader());
|
||||||
appBar: AppBar(),
|
|
||||||
body: const Loader(),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -432,31 +322,17 @@ class _UserDetailsContentState extends State<UserDetailsContent> {
|
|||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
child: Row(
|
child: Row(children: [_choiceChipWidget(0), const SizedBox(width: 8), _choiceChipWidget(1), const SizedBox(width: 8), _choiceChipWidget(2)]),
|
||||||
children: [
|
|
||||||
_choiceChipWidget(0),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
_choiceChipWidget(1),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
_choiceChipWidget(2),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Builder(
|
Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
switch (_selectedChipIndex) {
|
switch (_selectedChipIndex) {
|
||||||
case 0:
|
case 0:
|
||||||
return WeightChartWidget(
|
return WeightChartWidget(weightPoints: weightPoints);
|
||||||
weightPoints: weightPoints,
|
|
||||||
);
|
|
||||||
case 1:
|
case 1:
|
||||||
return CircumferenceChartWidget(
|
return CircumferenceChartWidget(circumferencePoints: circumferencePoints);
|
||||||
circumferencePoints: circumferencePoints,
|
|
||||||
);
|
|
||||||
case 2:
|
case 2:
|
||||||
return ImtChartWidget(
|
return ImtChartWidget(weightPoints: weightPoints);
|
||||||
weightPoints: weightPoints,
|
|
||||||
);
|
|
||||||
default:
|
default:
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
@@ -489,16 +365,10 @@ class _UserDetailsContentState extends State<UserDetailsContent> {
|
|||||||
pressElevation: 0,
|
pressElevation: 0,
|
||||||
showCheckmark: false,
|
showCheckmark: false,
|
||||||
side: BorderSide.none,
|
side: BorderSide.none,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
label: Text(chipsIntervalLabelList[index]),
|
label: Text(chipsIntervalLabelList[index]),
|
||||||
labelStyle: TextStyle(
|
labelStyle: TextStyle(color: appTheme.gray600, fontWeight: FontWeight.w500, fontSize: 12),
|
||||||
color: appTheme.gray600,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
selected: _selectedIntervalChipIndex == index,
|
selected: _selectedIntervalChipIndex == index,
|
||||||
onSelected: (bool selected) {
|
onSelected: (bool selected) {
|
||||||
context.read<UserDetailsCubit>().getChartData(period: chipsIntervalList[index], slug: widget.slug);
|
context.read<UserDetailsCubit>().getChartData(period: chipsIntervalList[index], slug: widget.slug);
|
||||||
@@ -515,16 +385,10 @@ class _UserDetailsContentState extends State<UserDetailsContent> {
|
|||||||
pressElevation: 0,
|
pressElevation: 0,
|
||||||
showCheckmark: false,
|
showCheckmark: false,
|
||||||
side: BorderSide.none,
|
side: BorderSide.none,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
label: Text(chipsLabelList[index]),
|
label: Text(chipsLabelList[index]),
|
||||||
labelStyle: TextStyle(
|
labelStyle: TextStyle(color: index == _selectedChipIndex ? Colors.white : appTheme.gray600, fontWeight: FontWeight.w500, fontSize: 12),
|
||||||
color: index == _selectedChipIndex ? Colors.white : appTheme.gray600,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
selected: _selectedChipIndex == index,
|
selected: _selectedChipIndex == index,
|
||||||
backgroundColor: appTheme.gray100,
|
backgroundColor: appTheme.gray100,
|
||||||
onSelected: (bool selected) {
|
onSelected: (bool selected) {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// File generated by FlutterFire CLI.
|
// File generated by FlutterFire CLI.
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
|
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
|
||||||
import 'package:flutter/foundation.dart'
|
import 'package:flutter/foundation.dart' show defaultTargetPlatform, kIsWeb, TargetPlatform;
|
||||||
show defaultTargetPlatform, kIsWeb, TargetPlatform;
|
import 'package:eatvio_app/core/env_config.dart';
|
||||||
|
|
||||||
/// Default [FirebaseOptions] for use with your Firebase apps.
|
/// Default [FirebaseOptions] for use with your Firebase apps.
|
||||||
///
|
///
|
||||||
@@ -43,28 +43,26 @@ class DefaultFirebaseOptions {
|
|||||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
throw UnsupportedError(
|
throw UnsupportedError('DefaultFirebaseOptions are not supported for this platform.');
|
||||||
'DefaultFirebaseOptions are not supported for this platform.',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const FirebaseOptions android = FirebaseOptions(
|
static FirebaseOptions get android => FirebaseOptions(
|
||||||
apiKey: 'AIzaSyBUe1UqAAMfgMcd4hWdRoLF1Os-yn-HE9k',
|
apiKey: EnvConfig.firebaseAndroidApiKey,
|
||||||
appId: '1:613980912922:android:4de95e5f2d50ba69ecc0a3',
|
appId: EnvConfig.firebaseAndroidAppId,
|
||||||
messagingSenderId: '613980912922',
|
messagingSenderId: EnvConfig.firebaseMessagingSenderId,
|
||||||
projectId: 'eatvio-3467b',
|
projectId: EnvConfig.firebaseProjectId,
|
||||||
databaseURL: 'https://eatvio-3467b-default-rtdb.europe-west1.firebasedatabase.app',
|
databaseURL: EnvConfig.firebaseDatabaseUrl,
|
||||||
storageBucket: 'eatvio-3467b.firebasestorage.app',
|
storageBucket: EnvConfig.firebaseStorageBucket,
|
||||||
);
|
);
|
||||||
|
|
||||||
static const FirebaseOptions ios = FirebaseOptions(
|
static FirebaseOptions get ios => FirebaseOptions(
|
||||||
apiKey: 'AIzaSyBnE8tQjL9_1HuXrTnqNy6qbXpoenO36yQ',
|
apiKey: EnvConfig.firebaseIosApiKey,
|
||||||
appId: '1:613980912922:ios:c557d140a1212e63ecc0a3',
|
appId: EnvConfig.firebaseIosAppId,
|
||||||
messagingSenderId: '613980912922',
|
messagingSenderId: EnvConfig.firebaseMessagingSenderId,
|
||||||
projectId: 'eatvio-3467b',
|
projectId: EnvConfig.firebaseProjectId,
|
||||||
databaseURL: 'https://eatvio-3467b-default-rtdb.europe-west1.firebasedatabase.app',
|
databaseURL: EnvConfig.firebaseDatabaseUrl,
|
||||||
storageBucket: 'eatvio-3467b.firebasestorage.app',
|
storageBucket: EnvConfig.firebaseStorageBucket,
|
||||||
iosBundleId: 'com.eatvioapp.app',
|
iosBundleId: EnvConfig.firebaseIosBundleId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"about_diet": MessageLookupByLibrary.simpleMessage("Об этой диете"),
|
"about_diet": MessageLookupByLibrary.simpleMessage("Об этой диете"),
|
||||||
"about_plan": MessageLookupByLibrary.simpleMessage("Об этом плане"),
|
"about_plan": MessageLookupByLibrary.simpleMessage("Об этом плане"),
|
||||||
"about_recipe": MessageLookupByLibrary.simpleMessage("Об этом рецепте"),
|
"about_recipe": MessageLookupByLibrary.simpleMessage("Об этом рецепте"),
|
||||||
|
"accountDeleting": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Удаление аккаунта",
|
||||||
|
),
|
||||||
"active": MessageLookupByLibrary.simpleMessage("Активен"),
|
"active": MessageLookupByLibrary.simpleMessage("Активен"),
|
||||||
"active_consultations_count": MessageLookupByLibrary.simpleMessage(
|
"active_consultations_count": MessageLookupByLibrary.simpleMessage(
|
||||||
"Кол-во активных консультаций",
|
"Кол-во активных консультаций",
|
||||||
@@ -124,6 +127,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
),
|
),
|
||||||
"cancel_diet": MessageLookupByLibrary.simpleMessage("Отменить диету?"),
|
"cancel_diet": MessageLookupByLibrary.simpleMessage("Отменить диету?"),
|
||||||
"cancel_order": MessageLookupByLibrary.simpleMessage("Отменить заказ"),
|
"cancel_order": MessageLookupByLibrary.simpleMessage("Отменить заказ"),
|
||||||
|
"cannotDeleteAccount": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Не получилось удалить ваш аккаунт",
|
||||||
|
),
|
||||||
"carb_type": MessageLookupByLibrary.simpleMessage("Мучное"),
|
"carb_type": MessageLookupByLibrary.simpleMessage("Мучное"),
|
||||||
"carb_type_desc": MessageLookupByLibrary.simpleMessage(
|
"carb_type_desc": MessageLookupByLibrary.simpleMessage(
|
||||||
"Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.",
|
"Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.",
|
||||||
@@ -256,6 +262,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
),
|
),
|
||||||
"days": MessageLookupByLibrary.simpleMessage("дней"),
|
"days": MessageLookupByLibrary.simpleMessage("дней"),
|
||||||
"deception": MessageLookupByLibrary.simpleMessage("Обман"),
|
"deception": MessageLookupByLibrary.simpleMessage("Обман"),
|
||||||
|
"delete": MessageLookupByLibrary.simpleMessage("Удалить"),
|
||||||
"deleteComment": MessageLookupByLibrary.simpleMessage(
|
"deleteComment": MessageLookupByLibrary.simpleMessage(
|
||||||
"Удалить комментарий",
|
"Удалить комментарий",
|
||||||
),
|
),
|
||||||
@@ -411,6 +418,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"invited_people_count": MessageLookupByLibrary.simpleMessage(
|
"invited_people_count": MessageLookupByLibrary.simpleMessage(
|
||||||
"Количество приглашенных",
|
"Количество приглашенных",
|
||||||
),
|
),
|
||||||
|
"itCantBeRecovered": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Его нельзя будет восстановить",
|
||||||
|
),
|
||||||
"label": MessageLookupByLibrary.simpleMessage("Название"),
|
"label": MessageLookupByLibrary.simpleMessage("Название"),
|
||||||
"language": MessageLookupByLibrary.simpleMessage("Язык"),
|
"language": MessageLookupByLibrary.simpleMessage("Язык"),
|
||||||
"last_name": MessageLookupByLibrary.simpleMessage("Фамилия"),
|
"last_name": MessageLookupByLibrary.simpleMessage("Фамилия"),
|
||||||
@@ -780,6 +790,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"youShureYouWantToCancelOrder": MessageLookupByLibrary.simpleMessage(
|
"youShureYouWantToCancelOrder": MessageLookupByLibrary.simpleMessage(
|
||||||
"Вы уверены, что хотите отменить заказ?",
|
"Вы уверены, что хотите отменить заказ?",
|
||||||
),
|
),
|
||||||
|
"youShureYouWantToDeleteAccount": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Вы уверены, что хотите удалить аккаунт?",
|
||||||
|
),
|
||||||
"you_can_not_open_this_day": MessageLookupByLibrary.simpleMessage(
|
"you_can_not_open_this_day": MessageLookupByLibrary.simpleMessage(
|
||||||
"Вы не можете перейти в этот день",
|
"Вы не можете перейти в этот день",
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -3414,6 +3414,51 @@ class S {
|
|||||||
args: [],
|
args: [],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `Удаление аккаунта`
|
||||||
|
String get accountDeleting {
|
||||||
|
return Intl.message(
|
||||||
|
'Удаление аккаунта',
|
||||||
|
name: 'accountDeleting',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Вы уверены, что хотите удалить аккаунт?`
|
||||||
|
String get youShureYouWantToDeleteAccount {
|
||||||
|
return Intl.message(
|
||||||
|
'Вы уверены, что хотите удалить аккаунт?',
|
||||||
|
name: 'youShureYouWantToDeleteAccount',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Его нельзя будет восстановить`
|
||||||
|
String get itCantBeRecovered {
|
||||||
|
return Intl.message(
|
||||||
|
'Его нельзя будет восстановить',
|
||||||
|
name: 'itCantBeRecovered',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Удалить`
|
||||||
|
String get delete {
|
||||||
|
return Intl.message('Удалить', name: 'delete', desc: '', args: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Не получилось удалить ваш аккаунт`
|
||||||
|
String get cannotDeleteAccount {
|
||||||
|
return Intl.message(
|
||||||
|
'Не получилось удалить ваш аккаунт',
|
||||||
|
name: 'cannotDeleteAccount',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AppLocalizationDelegate extends LocalizationsDelegate<S> {
|
class AppLocalizationDelegate extends LocalizationsDelegate<S> {
|
||||||
|
|||||||
@@ -402,10 +402,15 @@
|
|||||||
"allowAcessToGaleryDescription": "Предоставление доступа позволит вам добавлять фото к записям",
|
"allowAcessToGaleryDescription": "Предоставление доступа позволит вам добавлять фото к записям",
|
||||||
"allowAccessToCamera": "Разрешите доступ к камере",
|
"allowAccessToCamera": "Разрешите доступ к камере",
|
||||||
"allowAccessToCameraDescription": "Предоставление доступа позволит вам делать фотографии и добавлять к записям",
|
"allowAccessToCameraDescription": "Предоставление доступа позволит вам делать фотографии и добавлять к записям",
|
||||||
"allowAccess": "Разрешить доступ",
|
"allowAccess": "Разрешить доступ" ,
|
||||||
"copy": "Скопировать",
|
"copy": "Скопировать",
|
||||||
"reply": "Ответить",
|
"reply": "Ответить",
|
||||||
"deleteComment": "Удалить комментарий"
|
"deleteComment": "Удалить комментарий",
|
||||||
|
"accountDeleting": "Удаление аккаунта",
|
||||||
|
"youShureYouWantToDeleteAccount": "Вы уверены, что хотите удалить аккаунт?",
|
||||||
|
"itCantBeRecovered": "Его нельзя будет восстановить",
|
||||||
|
"delete": "Удалить",
|
||||||
|
"cannotDeleteAccount": "Не получилось удалить ваш аккаунт"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:io' as io;
|
||||||
import 'package:eatvio_app/core/app_router.dart';
|
import 'package:eatvio_app/core/app_router.dart';
|
||||||
import 'package:eatvio_app/core/locator_service.dart' as di;
|
import 'package:eatvio_app/core/locator_service.dart' as di;
|
||||||
import 'package:eatvio_app/core/locator_service.dart';
|
import 'package:eatvio_app/core/locator_service.dart';
|
||||||
@@ -11,6 +12,8 @@ import 'package:eatvio_app/features/internet_connection_checker/internet_connect
|
|||||||
import 'package:eatvio_app/firebase_options.dart';
|
import 'package:eatvio_app/firebase_options.dart';
|
||||||
import 'package:eatvio_app/generated/l10n.dart';
|
import 'package:eatvio_app/generated/l10n.dart';
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
@@ -18,22 +21,28 @@ import 'package:flutter_native_splash/flutter_native_splash.dart';
|
|||||||
import 'package:flutter_portal/flutter_portal.dart';
|
import 'package:flutter_portal/flutter_portal.dart';
|
||||||
import 'package:keyboard_dismisser/keyboard_dismisser.dart';
|
import 'package:keyboard_dismisser/keyboard_dismisser.dart';
|
||||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||||
|
import 'package:eatvio_app/core/env_config.dart';
|
||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
WidgetsBinding widgetsBinding = SentryWidgetsFlutterBinding.ensureInitialized();
|
WidgetsBinding widgetsBinding = SentryWidgetsFlutterBinding.ensureInitialized();
|
||||||
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
|
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
|
||||||
|
|
||||||
|
await dotenv.load(fileName: "env/.env");
|
||||||
|
|
||||||
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
||||||
|
await FirebaseMessaging.instance.requestPermission();
|
||||||
await di.init();
|
await di.init();
|
||||||
|
|
||||||
await SentryFlutter.init(
|
if (kDebugMode) {
|
||||||
(options) {
|
io.HttpClient.enableTimelineLogging = true;
|
||||||
options.dsn = 'http://ba7f2653067776f3034e6e89850e85a1@sentry.md-desk.ru:9000/2';
|
}
|
||||||
options.tracesSampleRate = 1.0;
|
|
||||||
options.debug = false;
|
await SentryFlutter.init((options) {
|
||||||
},
|
options.dsn = EnvConfig.sentryDsn;
|
||||||
appRunner: () => runApp(const MyApp()),
|
options.tracesSampleRate = 1.0;
|
||||||
);
|
options.debug = false;
|
||||||
|
}, appRunner: () => runApp(const MyApp()));
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
|
|||||||
38
pubspec.yaml
38
pubspec.yaml
@@ -1,10 +1,10 @@
|
|||||||
name: eatvio_app
|
name: eatvio_app
|
||||||
description: A new Flutter project.
|
description: A new Flutter project.
|
||||||
version: 1.1.16+46
|
version: 1.2.6+64
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.3.4 <4.0.0'
|
sdk: '>=3.8.0 <4.0.0'
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
@@ -18,28 +18,28 @@ dependencies:
|
|||||||
cached_network_image: ^3.4.1
|
cached_network_image: ^3.4.1
|
||||||
flutter_svg: ^2.2.0
|
flutter_svg: ^2.2.0
|
||||||
equatable: ^2.0.7
|
equatable: ^2.0.7
|
||||||
freezed_annotation: ^3.0.0
|
freezed_annotation: ^3.1.0
|
||||||
json_annotation: ^4.9.0
|
json_annotation: ^4.9.0
|
||||||
validators: ^3.0.0
|
validators: ^3.0.0
|
||||||
path_provider: ^2.1.5
|
path_provider: ^2.1.5
|
||||||
carousel_slider_plus: ^7.1.1
|
carousel_slider_plus: ^7.1.1
|
||||||
flutter_form_builder: ^10.0.1
|
flutter_form_builder: ^10.1.0
|
||||||
image_picker: ^1.1.2
|
image_picker: ^1.1.2
|
||||||
flutter_typeahead: ^5.2.0
|
flutter_typeahead: ^5.2.0
|
||||||
go_router: ^16.0.0
|
go_router: ^16.0.0
|
||||||
keyboard_dismisser: ^3.0.0
|
keyboard_dismisser: ^3.0.0
|
||||||
retrofit: ^4.5.0
|
retrofit: ^4.7.0
|
||||||
get_it: ^8.0.3
|
get_it: ^8.1.0
|
||||||
dio: ^5.8.0+1
|
dio: ^5.8.0+1
|
||||||
dart_pusher_channels: ^1.2.3
|
dart_pusher_channels: ^1.2.3
|
||||||
photo_view: ^0.15.0
|
photo_view: ^0.15.0
|
||||||
dismissible_page: ^1.0.2
|
dismissible_page: ^1.0.2
|
||||||
url_launcher: ^6.3.1
|
url_launcher: ^6.3.2
|
||||||
easy_debounce: ^2.0.3
|
easy_debounce: ^2.0.3
|
||||||
badges: ^3.1.2
|
badges: ^3.1.2
|
||||||
google_fonts: ^6.2.1
|
google_fonts: ^6.2.1
|
||||||
syncfusion_flutter_charts: ^30.1.38
|
syncfusion_flutter_charts: ^30.1.42
|
||||||
syncfusion_flutter_gauges: ^30.1.38
|
syncfusion_flutter_gauges: ^30.1.42
|
||||||
table_calendar: ^3.2.0
|
table_calendar: ^3.2.0
|
||||||
flutter_keyboard_visibility: ^6.0.0
|
flutter_keyboard_visibility: ^6.0.0
|
||||||
share_plus: ^11.0.0
|
share_plus: ^11.0.0
|
||||||
@@ -58,11 +58,11 @@ dependencies:
|
|||||||
http: ^1.4.0
|
http: ^1.4.0
|
||||||
flutter_native_splash: ^2.4.6
|
flutter_native_splash: ^2.4.6
|
||||||
webview_flutter: ^4.13.0
|
webview_flutter: ^4.13.0
|
||||||
sentry_flutter: ^8.14.2
|
sentry_flutter: ^9.5.0
|
||||||
connectivity_plus: ^6.1.4
|
connectivity_plus: ^6.1.4
|
||||||
visibility_detector: ^0.4.0+2
|
visibility_detector: ^0.4.0+2
|
||||||
firebase_core: ^3.15.0
|
firebase_core: ^3.15.2
|
||||||
firebase_messaging: ^15.2.8
|
firebase_messaging: ^15.2.10
|
||||||
http_parser: ^4.1.2
|
http_parser: ^4.1.2
|
||||||
path: ^1.9.1
|
path: ^1.9.1
|
||||||
collection: ^1.19.1
|
collection: ^1.19.1
|
||||||
@@ -70,15 +70,16 @@ dependencies:
|
|||||||
version: ^3.0.2
|
version: ^3.0.2
|
||||||
package_info_plus: ^8.3.0
|
package_info_plus: ^8.3.0
|
||||||
device_info_plus: ^11.5.0
|
device_info_plus: ^11.5.0
|
||||||
permission_handler: ^12.0.0+1
|
permission_handler: ^12.0.1
|
||||||
qr_code_scanner_plus: ^2.0.10+1
|
qr_code_scanner_plus: ^2.0.10+1
|
||||||
|
flutter_dotenv: ^5.1.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.5.4
|
build_runner: ^2.6.0
|
||||||
freezed: ^3.0.6
|
freezed: ^3.2.0
|
||||||
json_serializable: ^6.9.5
|
json_serializable: ^6.10.0
|
||||||
retrofit_generator: ^9.2.0
|
retrofit_generator: ^10.0.1
|
||||||
mockito: ^5.4.6
|
mockito: ^5.5.0
|
||||||
|
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
@@ -88,6 +89,7 @@ dev_dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
assets:
|
assets:
|
||||||
|
- env/.env
|
||||||
- assets/
|
- assets/
|
||||||
- assets/images/
|
- assets/images/
|
||||||
- assets/audio/
|
- assets/audio/
|
||||||
|
|||||||
@@ -19,8 +19,10 @@ void main() {
|
|||||||
id: 0,
|
id: 0,
|
||||||
name: 'Alexander Avanesyan',
|
name: 'Alexander Avanesyan',
|
||||||
email: 'mso7nueh@mail.ru',
|
email: 'mso7nueh@mail.ru',
|
||||||
|
phoneConfirmationState: '',
|
||||||
isPublicProfile: true,
|
isPublicProfile: true,
|
||||||
isEntitySubscribedByMe: false,
|
isEntitySubscribedByMe: false,
|
||||||
|
isBlockedByMe: false,
|
||||||
slug: 'slug',
|
slug: 'slug',
|
||||||
profilePhotoUrl: '',
|
profilePhotoUrl: '',
|
||||||
roles: ['user'],
|
roles: ['user'],
|
||||||
|
|||||||
@@ -1,526 +0,0 @@
|
|||||||
// Mocks generated by Mockito 5.4.6 from annotations
|
|
||||||
// in eatvio_app/test/features/profile/services/profile_service_test.dart.
|
|
||||||
// Do not manually edit this file.
|
|
||||||
|
|
||||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
|
||||||
import 'dart:async' as _i6;
|
|
||||||
import 'dart:io' as _i10;
|
|
||||||
|
|
||||||
import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart'
|
|
||||||
as _i7;
|
|
||||||
import 'package:eatvio_app/features/profile/models/chosen_diets_pagination_model.dart'
|
|
||||||
as _i4;
|
|
||||||
import 'package:eatvio_app/features/profile/models/circumference_model.dart'
|
|
||||||
as _i9;
|
|
||||||
import 'package:eatvio_app/features/profile/models/profile_model.dart' as _i2;
|
|
||||||
import 'package:eatvio_app/features/profile/models/sub_model.dart' as _i3;
|
|
||||||
import 'package:eatvio_app/features/profile/models/weight_model.dart' as _i8;
|
|
||||||
import 'package:eatvio_app/features/profile/services/profile_service.dart'
|
|
||||||
as _i5;
|
|
||||||
import 'package:mockito/mockito.dart' as _i1;
|
|
||||||
import 'package:mockito/src/dummies.dart' as _i11;
|
|
||||||
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
// ignore_for_file: avoid_redundant_argument_values
|
|
||||||
// ignore_for_file: avoid_setters_without_getters
|
|
||||||
// ignore_for_file: comment_references
|
|
||||||
// ignore_for_file: deprecated_member_use
|
|
||||||
// ignore_for_file: deprecated_member_use_from_same_package
|
|
||||||
// ignore_for_file: implementation_imports
|
|
||||||
// ignore_for_file: invalid_use_of_visible_for_testing_member
|
|
||||||
// ignore_for_file: must_be_immutable
|
|
||||||
// ignore_for_file: prefer_const_constructors
|
|
||||||
// ignore_for_file: unnecessary_parenthesis
|
|
||||||
// ignore_for_file: camel_case_types
|
|
||||||
// ignore_for_file: subtype_of_sealed_class
|
|
||||||
|
|
||||||
class _FakeProfileModel_0 extends _i1.SmartFake implements _i2.ProfileModel {
|
|
||||||
_FakeProfileModel_0(
|
|
||||||
Object parent,
|
|
||||||
Invocation parentInvocation,
|
|
||||||
) : super(
|
|
||||||
parent,
|
|
||||||
parentInvocation,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class _FakeProfileDetailsModel_1 extends _i1.SmartFake
|
|
||||||
implements _i2.ProfileDetailsModel {
|
|
||||||
_FakeProfileDetailsModel_1(
|
|
||||||
Object parent,
|
|
||||||
Invocation parentInvocation,
|
|
||||||
) : super(
|
|
||||||
parent,
|
|
||||||
parentInvocation,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class _FakeSubPaginationModel_2 extends _i1.SmartFake
|
|
||||||
implements _i3.SubPaginationModel {
|
|
||||||
_FakeSubPaginationModel_2(
|
|
||||||
Object parent,
|
|
||||||
Invocation parentInvocation,
|
|
||||||
) : super(
|
|
||||||
parent,
|
|
||||||
parentInvocation,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class _FakeChosenDietsPaginationModel_3 extends _i1.SmartFake
|
|
||||||
implements _i4.ChosenDietsPaginationModel {
|
|
||||||
_FakeChosenDietsPaginationModel_3(
|
|
||||||
Object parent,
|
|
||||||
Invocation parentInvocation,
|
|
||||||
) : super(
|
|
||||||
parent,
|
|
||||||
parentInvocation,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A class which mocks [ProfileService].
|
|
||||||
///
|
|
||||||
/// See the documentation for Mockito's code generation for more information.
|
|
||||||
class MockProfileService extends _i1.Mock implements _i5.ProfileService {
|
|
||||||
@override
|
|
||||||
_i6.Future<_i2.ProfileModel> getProfileBySlug({required String? slug}) =>
|
|
||||||
(super.noSuchMethod(
|
|
||||||
Invocation.method(
|
|
||||||
#getProfileBySlug,
|
|
||||||
[],
|
|
||||||
{#slug: slug},
|
|
||||||
),
|
|
||||||
returnValue: _i6.Future<_i2.ProfileModel>.value(_FakeProfileModel_0(
|
|
||||||
this,
|
|
||||||
Invocation.method(
|
|
||||||
#getProfileBySlug,
|
|
||||||
[],
|
|
||||||
{#slug: slug},
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
returnValueForMissingStub:
|
|
||||||
_i6.Future<_i2.ProfileModel>.value(_FakeProfileModel_0(
|
|
||||||
this,
|
|
||||||
Invocation.method(
|
|
||||||
#getProfileBySlug,
|
|
||||||
[],
|
|
||||||
{#slug: slug},
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
) as _i6.Future<_i2.ProfileModel>);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_i6.Future<_i2.ProfileModel> getMyProfile() => (super.noSuchMethod(
|
|
||||||
Invocation.method(
|
|
||||||
#getMyProfile,
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
returnValue: _i6.Future<_i2.ProfileModel>.value(_FakeProfileModel_0(
|
|
||||||
this,
|
|
||||||
Invocation.method(
|
|
||||||
#getMyProfile,
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
returnValueForMissingStub:
|
|
||||||
_i6.Future<_i2.ProfileModel>.value(_FakeProfileModel_0(
|
|
||||||
this,
|
|
||||||
Invocation.method(
|
|
||||||
#getMyProfile,
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
) as _i6.Future<_i2.ProfileModel>);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_i6.Future<_i2.ProfileDetailsModel> getMyProfileDetails() =>
|
|
||||||
(super.noSuchMethod(
|
|
||||||
Invocation.method(
|
|
||||||
#getMyProfileDetails,
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
returnValue: _i6.Future<_i2.ProfileDetailsModel>.value(
|
|
||||||
_FakeProfileDetailsModel_1(
|
|
||||||
this,
|
|
||||||
Invocation.method(
|
|
||||||
#getMyProfileDetails,
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
returnValueForMissingStub: _i6.Future<_i2.ProfileDetailsModel>.value(
|
|
||||||
_FakeProfileDetailsModel_1(
|
|
||||||
this,
|
|
||||||
Invocation.method(
|
|
||||||
#getMyProfileDetails,
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
) as _i6.Future<_i2.ProfileDetailsModel>);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_i6.Future<_i2.ProfileDetailsModel> getProfileDetailsBySlug(
|
|
||||||
{required String? slug}) =>
|
|
||||||
(super.noSuchMethod(
|
|
||||||
Invocation.method(
|
|
||||||
#getProfileDetailsBySlug,
|
|
||||||
[],
|
|
||||||
{#slug: slug},
|
|
||||||
),
|
|
||||||
returnValue: _i6.Future<_i2.ProfileDetailsModel>.value(
|
|
||||||
_FakeProfileDetailsModel_1(
|
|
||||||
this,
|
|
||||||
Invocation.method(
|
|
||||||
#getProfileDetailsBySlug,
|
|
||||||
[],
|
|
||||||
{#slug: slug},
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
returnValueForMissingStub: _i6.Future<_i2.ProfileDetailsModel>.value(
|
|
||||||
_FakeProfileDetailsModel_1(
|
|
||||||
this,
|
|
||||||
Invocation.method(
|
|
||||||
#getProfileDetailsBySlug,
|
|
||||||
[],
|
|
||||||
{#slug: slug},
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
) as _i6.Future<_i2.ProfileDetailsModel>);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_i6.Future<void> addCircumference({
|
|
||||||
required int? chestCircumference,
|
|
||||||
required int? waistCircumference,
|
|
||||||
required int? hipCircumference,
|
|
||||||
}) =>
|
|
||||||
(super.noSuchMethod(
|
|
||||||
Invocation.method(
|
|
||||||
#addCircumference,
|
|
||||||
[],
|
|
||||||
{
|
|
||||||
#chestCircumference: chestCircumference,
|
|
||||||
#waistCircumference: waistCircumference,
|
|
||||||
#hipCircumference: hipCircumference,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
returnValue: _i6.Future<void>.value(),
|
|
||||||
returnValueForMissingStub: _i6.Future<void>.value(),
|
|
||||||
) as _i6.Future<void>);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_i6.Future<void> addWeight({required double? weight}) => (super.noSuchMethod(
|
|
||||||
Invocation.method(
|
|
||||||
#addWeight,
|
|
||||||
[],
|
|
||||||
{#weight: weight},
|
|
||||||
),
|
|
||||||
returnValue: _i6.Future<void>.value(),
|
|
||||||
returnValueForMissingStub: _i6.Future<void>.value(),
|
|
||||||
) as _i6.Future<void>);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_i6.Future<List<_i7.FeedBlogPostModel>> getBlogPostByAuthorSlug(
|
|
||||||
{required String? slug}) =>
|
|
||||||
(super.noSuchMethod(
|
|
||||||
Invocation.method(
|
|
||||||
#getBlogPostByAuthorSlug,
|
|
||||||
[],
|
|
||||||
{#slug: slug},
|
|
||||||
),
|
|
||||||
returnValue: _i6.Future<List<_i7.FeedBlogPostModel>>.value(
|
|
||||||
<_i7.FeedBlogPostModel>[]),
|
|
||||||
returnValueForMissingStub:
|
|
||||||
_i6.Future<List<_i7.FeedBlogPostModel>>.value(
|
|
||||||
<_i7.FeedBlogPostModel>[]),
|
|
||||||
) as _i6.Future<List<_i7.FeedBlogPostModel>>);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_i6.Future<List<_i8.WeightModel>> getWeight({required String? period}) =>
|
|
||||||
(super.noSuchMethod(
|
|
||||||
Invocation.method(
|
|
||||||
#getWeight,
|
|
||||||
[],
|
|
||||||
{#period: period},
|
|
||||||
),
|
|
||||||
returnValue:
|
|
||||||
_i6.Future<List<_i8.WeightModel>>.value(<_i8.WeightModel>[]),
|
|
||||||
returnValueForMissingStub:
|
|
||||||
_i6.Future<List<_i8.WeightModel>>.value(<_i8.WeightModel>[]),
|
|
||||||
) as _i6.Future<List<_i8.WeightModel>>);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_i6.Future<List<_i8.WeightModel>> getWeightBySlug({
|
|
||||||
required String? period,
|
|
||||||
required String? slug,
|
|
||||||
}) =>
|
|
||||||
(super.noSuchMethod(
|
|
||||||
Invocation.method(
|
|
||||||
#getWeightBySlug,
|
|
||||||
[],
|
|
||||||
{
|
|
||||||
#period: period,
|
|
||||||
#slug: slug,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
returnValue:
|
|
||||||
_i6.Future<List<_i8.WeightModel>>.value(<_i8.WeightModel>[]),
|
|
||||||
returnValueForMissingStub:
|
|
||||||
_i6.Future<List<_i8.WeightModel>>.value(<_i8.WeightModel>[]),
|
|
||||||
) as _i6.Future<List<_i8.WeightModel>>);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_i6.Future<List<_i9.CircumferenceModel>> getCircumferences(
|
|
||||||
{required String? period}) =>
|
|
||||||
(super.noSuchMethod(
|
|
||||||
Invocation.method(
|
|
||||||
#getCircumferences,
|
|
||||||
[],
|
|
||||||
{#period: period},
|
|
||||||
),
|
|
||||||
returnValue: _i6.Future<List<_i9.CircumferenceModel>>.value(
|
|
||||||
<_i9.CircumferenceModel>[]),
|
|
||||||
returnValueForMissingStub:
|
|
||||||
_i6.Future<List<_i9.CircumferenceModel>>.value(
|
|
||||||
<_i9.CircumferenceModel>[]),
|
|
||||||
) as _i6.Future<List<_i9.CircumferenceModel>>);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_i6.Future<List<_i9.CircumferenceModel>> getCircumferencesBySlug({
|
|
||||||
required String? period,
|
|
||||||
required String? slug,
|
|
||||||
}) =>
|
|
||||||
(super.noSuchMethod(
|
|
||||||
Invocation.method(
|
|
||||||
#getCircumferencesBySlug,
|
|
||||||
[],
|
|
||||||
{
|
|
||||||
#period: period,
|
|
||||||
#slug: slug,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
returnValue: _i6.Future<List<_i9.CircumferenceModel>>.value(
|
|
||||||
<_i9.CircumferenceModel>[]),
|
|
||||||
returnValueForMissingStub:
|
|
||||||
_i6.Future<List<_i9.CircumferenceModel>>.value(
|
|
||||||
<_i9.CircumferenceModel>[]),
|
|
||||||
) as _i6.Future<List<_i9.CircumferenceModel>>);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_i6.Future<_i3.SubPaginationModel> getSubscribers({
|
|
||||||
required String? slug,
|
|
||||||
String? query,
|
|
||||||
}) =>
|
|
||||||
(super.noSuchMethod(
|
|
||||||
Invocation.method(
|
|
||||||
#getSubscribers,
|
|
||||||
[],
|
|
||||||
{
|
|
||||||
#slug: slug,
|
|
||||||
#query: query,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
returnValue:
|
|
||||||
_i6.Future<_i3.SubPaginationModel>.value(_FakeSubPaginationModel_2(
|
|
||||||
this,
|
|
||||||
Invocation.method(
|
|
||||||
#getSubscribers,
|
|
||||||
[],
|
|
||||||
{
|
|
||||||
#slug: slug,
|
|
||||||
#query: query,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
returnValueForMissingStub:
|
|
||||||
_i6.Future<_i3.SubPaginationModel>.value(_FakeSubPaginationModel_2(
|
|
||||||
this,
|
|
||||||
Invocation.method(
|
|
||||||
#getSubscribers,
|
|
||||||
[],
|
|
||||||
{
|
|
||||||
#slug: slug,
|
|
||||||
#query: query,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
) as _i6.Future<_i3.SubPaginationModel>);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_i6.Future<_i3.SubPaginationModel> getSubscriptions({
|
|
||||||
required String? slug,
|
|
||||||
String? query,
|
|
||||||
}) =>
|
|
||||||
(super.noSuchMethod(
|
|
||||||
Invocation.method(
|
|
||||||
#getSubscriptions,
|
|
||||||
[],
|
|
||||||
{
|
|
||||||
#slug: slug,
|
|
||||||
#query: query,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
returnValue:
|
|
||||||
_i6.Future<_i3.SubPaginationModel>.value(_FakeSubPaginationModel_2(
|
|
||||||
this,
|
|
||||||
Invocation.method(
|
|
||||||
#getSubscriptions,
|
|
||||||
[],
|
|
||||||
{
|
|
||||||
#slug: slug,
|
|
||||||
#query: query,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
returnValueForMissingStub:
|
|
||||||
_i6.Future<_i3.SubPaginationModel>.value(_FakeSubPaginationModel_2(
|
|
||||||
this,
|
|
||||||
Invocation.method(
|
|
||||||
#getSubscriptions,
|
|
||||||
[],
|
|
||||||
{
|
|
||||||
#slug: slug,
|
|
||||||
#query: query,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
) as _i6.Future<_i3.SubPaginationModel>);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_i6.Future<void> subscribe({required String? slug}) => (super.noSuchMethod(
|
|
||||||
Invocation.method(
|
|
||||||
#subscribe,
|
|
||||||
[],
|
|
||||||
{#slug: slug},
|
|
||||||
),
|
|
||||||
returnValue: _i6.Future<void>.value(),
|
|
||||||
returnValueForMissingStub: _i6.Future<void>.value(),
|
|
||||||
) as _i6.Future<void>);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_i6.Future<void> unsubscribe({required String? slug}) => (super.noSuchMethod(
|
|
||||||
Invocation.method(
|
|
||||||
#unsubscribe,
|
|
||||||
[],
|
|
||||||
{#slug: slug},
|
|
||||||
),
|
|
||||||
returnValue: _i6.Future<void>.value(),
|
|
||||||
returnValueForMissingStub: _i6.Future<void>.value(),
|
|
||||||
) as _i6.Future<void>);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_i6.Future<void> uploadPhoto({required _i10.File? photo}) =>
|
|
||||||
(super.noSuchMethod(
|
|
||||||
Invocation.method(
|
|
||||||
#uploadPhoto,
|
|
||||||
[],
|
|
||||||
{#photo: photo},
|
|
||||||
),
|
|
||||||
returnValue: _i6.Future<void>.value(),
|
|
||||||
returnValueForMissingStub: _i6.Future<void>.value(),
|
|
||||||
) as _i6.Future<void>);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_i6.Future<void> deletePhoto() => (super.noSuchMethod(
|
|
||||||
Invocation.method(
|
|
||||||
#deletePhoto,
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
returnValue: _i6.Future<void>.value(),
|
|
||||||
returnValueForMissingStub: _i6.Future<void>.value(),
|
|
||||||
) as _i6.Future<void>);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_i6.Future<_i4.ChosenDietsPaginationModel> getChosenDiets() =>
|
|
||||||
(super.noSuchMethod(
|
|
||||||
Invocation.method(
|
|
||||||
#getChosenDiets,
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
returnValue: _i6.Future<_i4.ChosenDietsPaginationModel>.value(
|
|
||||||
_FakeChosenDietsPaginationModel_3(
|
|
||||||
this,
|
|
||||||
Invocation.method(
|
|
||||||
#getChosenDiets,
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
returnValueForMissingStub:
|
|
||||||
_i6.Future<_i4.ChosenDietsPaginationModel>.value(
|
|
||||||
_FakeChosenDietsPaginationModel_3(
|
|
||||||
this,
|
|
||||||
Invocation.method(
|
|
||||||
#getChosenDiets,
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
) as _i6.Future<_i4.ChosenDietsPaginationModel>);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_i6.Future<String> getPassKey({required String? json}) => (super.noSuchMethod(
|
|
||||||
Invocation.method(
|
|
||||||
#getPassKey,
|
|
||||||
[],
|
|
||||||
{#json: json},
|
|
||||||
),
|
|
||||||
returnValue: _i6.Future<String>.value(_i11.dummyValue<String>(
|
|
||||||
this,
|
|
||||||
Invocation.method(
|
|
||||||
#getPassKey,
|
|
||||||
[],
|
|
||||||
{#json: json},
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
returnValueForMissingStub:
|
|
||||||
_i6.Future<String>.value(_i11.dummyValue<String>(
|
|
||||||
this,
|
|
||||||
Invocation.method(
|
|
||||||
#getPassKey,
|
|
||||||
[],
|
|
||||||
{#json: json},
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
) as _i6.Future<String>);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_i6.Future<String> getMyPhone() => (super.noSuchMethod(
|
|
||||||
Invocation.method(
|
|
||||||
#getMyPhone,
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
returnValue: _i6.Future<String>.value(_i11.dummyValue<String>(
|
|
||||||
this,
|
|
||||||
Invocation.method(
|
|
||||||
#getMyPhone,
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
returnValueForMissingStub:
|
|
||||||
_i6.Future<String>.value(_i11.dummyValue<String>(
|
|
||||||
this,
|
|
||||||
Invocation.method(
|
|
||||||
#getMyPhone,
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
) as _i6.Future<String>);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_i6.Future<void> sendEmailCode() => (super.noSuchMethod(
|
|
||||||
Invocation.method(
|
|
||||||
#sendEmailCode,
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
returnValue: _i6.Future<void>.value(),
|
|
||||||
returnValueForMissingStub: _i6.Future<void>.value(),
|
|
||||||
) as _i6.Future<void>);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_i6.Future<void> tryVerifyEmailByCode({required String? code}) =>
|
|
||||||
(super.noSuchMethod(
|
|
||||||
Invocation.method(
|
|
||||||
#tryVerifyEmailByCode,
|
|
||||||
[],
|
|
||||||
{#code: code},
|
|
||||||
),
|
|
||||||
returnValue: _i6.Future<void>.value(),
|
|
||||||
returnValueForMissingStub: _i6.Future<void>.value(),
|
|
||||||
) as _i6.Future<void>);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user