forked from eatvio/eatvio_app
Compare commits
2 Commits
issue-635
...
notificati
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26ff548752 | ||
|
|
43e9098d8d |
@@ -11,30 +11,6 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Checkout env repository
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
repository: mso7nueh/eatvio_app_env
|
|
||||||
path: env_repo
|
|
||||||
token: ${{ vars.MSO7NUEH_TOKEN }}
|
|
||||||
ref: main
|
|
||||||
|
|
||||||
- name: Copy .env file
|
|
||||||
run: |
|
|
||||||
mkdir -p env
|
|
||||||
cp env_repo/.env env/.env
|
|
||||||
|
|
||||||
- name: Load environment variables from .env
|
|
||||||
run: |
|
|
||||||
set -a
|
|
||||||
source env/.env
|
|
||||||
set +a
|
|
||||||
# Export all variables to GITHUB_ENV
|
|
||||||
while IFS= read -r line; do
|
|
||||||
if [[ $line =~ ^[A-Za-z_][A-Za-z0-9_]*= ]]; then
|
|
||||||
echo "$line" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
done < env/.env
|
|
||||||
|
|
||||||
- name: Unlock login keychain
|
- name: Unlock login keychain
|
||||||
run: |
|
run: |
|
||||||
@@ -42,17 +18,6 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
KEYCHAIN_PASSWORD: ${{ vars.IOS_RUNNER_CI_PASSWORD }}
|
KEYCHAIN_PASSWORD: ${{ vars.IOS_RUNNER_CI_PASSWORD }}
|
||||||
|
|
||||||
- name: Setup API key
|
|
||||||
run: |
|
|
||||||
echo "${API_APPSTORE_KEY}" | tr -d '\n' | base64 --decode > ios/AuthKey.p8
|
|
||||||
chmod 600 ios/AuthKey.p8
|
|
||||||
ls -la ios/AuthKey.p8
|
|
||||||
echo "File size: $(wc -c < ios/AuthKey.p8) bytes"
|
|
||||||
echo "Contents of ios directory:"
|
|
||||||
ls -la ios/
|
|
||||||
env:
|
|
||||||
API_APPSTORE_KEY: ${{ vars.API_APPSTORE_KEY }}
|
|
||||||
|
|
||||||
- name: Flutter build
|
- name: Flutter build
|
||||||
run: |
|
run: |
|
||||||
flutter clean
|
flutter clean
|
||||||
@@ -66,8 +31,6 @@ 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.8"
|
flutter_version: "3.32.5"
|
||||||
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/"
|
||||||
@@ -16,31 +16,6 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Checkout env repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
repository: mso7nueh/eatvio_app_env
|
|
||||||
path: env_repo
|
|
||||||
token: ${{ vars.MSO7NUEH_TOKEN }}
|
|
||||||
ref: main
|
|
||||||
|
|
||||||
- name: Copy .env file
|
|
||||||
run: |
|
|
||||||
mkdir -p env
|
|
||||||
cp env_repo/.env env/.env
|
|
||||||
|
|
||||||
- name: Load environment variables from .env
|
|
||||||
run: |
|
|
||||||
set -a
|
|
||||||
source env/.env
|
|
||||||
set +a
|
|
||||||
# Export all variables to GITHUB_ENV
|
|
||||||
while IFS= read -r line; do
|
|
||||||
if [[ $line =~ ^[A-Za-z_][A-Za-z0-9_]*= ]]; then
|
|
||||||
echo "$line" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
done < env/.env
|
|
||||||
|
|
||||||
- name: Create key.properties file
|
- name: Create key.properties file
|
||||||
run: |
|
run: |
|
||||||
cat > android/key.properties <<EOF
|
cat > android/key.properties <<EOF
|
||||||
@@ -144,7 +119,7 @@ jobs:
|
|||||||
run: flutter test
|
run: flutter test
|
||||||
|
|
||||||
- name: Build apk
|
- name: Build apk
|
||||||
run: flutter build apk --release --dart-define=HIDE_PLANS=false
|
run: flutter build apk --release
|
||||||
|
|
||||||
- name: Rename and move apk
|
- name: Rename and move apk
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -12,10 +12,5 @@
|
|||||||
/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
|
|
||||||
env/.env
|
|
||||||
|
|||||||
84
ENV_SETUP.md
84
ENV_SETUP.md
@@ -1,84 +0,0 @@
|
|||||||
# Настройка переменных окружения
|
|
||||||
|
|
||||||
Этот проект использует переменные окружения для хранения секретных данных и конфигурации.
|
|
||||||
|
|
||||||
## Установка
|
|
||||||
|
|
||||||
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_11
|
sourceCompatibility = JavaVersion.VERSION_21
|
||||||
targetCompatibility = JavaVersion.VERSION_11
|
targetCompatibility = JavaVersion.VERSION_21
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = JavaVersion.VERSION_11.toString()
|
jvmTarget = 21
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.eatvioapp.app"
|
applicationId = "com.eatvioapp.app"
|
||||||
minSdk = 23
|
minSdk = flutter.minSdkVersion
|
||||||
targetSdk = flutter.targetSdkVersion
|
targetSdk = flutter.targetSdkVersion
|
||||||
versionCode = flutter.versionCode
|
versionCode = flutter.versionCode
|
||||||
versionName = flutter.versionName
|
versionName = flutter.versionName
|
||||||
|
|||||||
@@ -2,12 +2,9 @@
|
|||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
|
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="Итвио"
|
android:label="Eatvio"
|
||||||
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=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
|
org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -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.12-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-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.1.0" apply false
|
id "org.jetbrains.kotlin.android" version "2.0.21" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
include ":app"
|
include ":app"
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
<svg width="19" height="14" viewBox="0 0 19 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M3.56039 6.73215L7.06827 10.2925C7.22519 10.452 7.3026 10.6385 7.30049 10.8521C7.29855 11.0656 7.21569 11.2555 7.0519 11.4218C6.88812 11.577 6.70269 11.6573 6.49563 11.6629C6.28874 11.6682 6.1034 11.5879 5.93961 11.4218L1.19568 6.60653C1.0048 6.41278 0.909363 6.1868 0.909363 5.92858C0.909363 5.67037 1.0048 5.44439 1.19568 5.25064L5.93961 0.435368C6.08581 0.287154 6.26675 0.211261 6.48243 0.207689C6.69829 0.204297 6.88812 0.280189 7.0519 0.435368C7.21569 0.601618 7.29759 0.792601 7.29759 1.00831C7.29759 1.22385 7.21569 1.41474 7.0519 1.58099L3.56039 5.12501H13.4583C14.8441 5.12501 16.0261 5.62162 17.0045 6.61483C17.983 7.60787 18.4722 8.80769 18.4722 10.2143V13.1607C18.4722 13.3888 18.3964 13.5797 18.2448 13.7334C18.0933 13.8873 17.9052 13.9643 17.6806 13.9643C17.4559 13.9643 17.2678 13.8873 17.1164 13.7334C16.9647 13.5797 16.8889 13.3888 16.8889 13.1607V10.2143C16.8889 9.25269 16.5539 8.43197 15.884 7.75215C15.2143 7.07215 14.4057 6.73215 13.4583 6.73215H3.56039Z" fill="#2F80ED"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,3 +1,3 @@
|
|||||||
source "https://rubygems.org"
|
source "https://rubygems.org"
|
||||||
|
|
||||||
gem "fastlane", "~> 2.217"
|
gem "fastlane"
|
||||||
|
|||||||
@@ -213,14 +213,10 @@
|
|||||||
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";
|
||||||
@@ -287,14 +283,10 @@
|
|||||||
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";
|
||||||
@@ -396,7 +388,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 = 67;
|
CURRENT_PROJECT_VERSION = 39;
|
||||||
DEVELOPMENT_TEAM = VL68C36HYS;
|
DEVELOPMENT_TEAM = VL68C36HYS;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
@@ -414,7 +406,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.2.3;
|
MARKETING_VERSION = 1.0.50;
|
||||||
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";
|
||||||
@@ -546,7 +538,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 = 67;
|
CURRENT_PROJECT_VERSION = 39;
|
||||||
DEVELOPMENT_TEAM = VL68C36HYS;
|
DEVELOPMENT_TEAM = VL68C36HYS;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
@@ -564,7 +556,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.2.3;
|
MARKETING_VERSION = 1.0.50;
|
||||||
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";
|
||||||
@@ -584,7 +576,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 = 67;
|
CURRENT_PROJECT_VERSION = 39;
|
||||||
DEVELOPMENT_TEAM = VL68C36HYS;
|
DEVELOPMENT_TEAM = VL68C36HYS;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
@@ -602,7 +594,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.2.3;
|
MARKETING_VERSION = 1.0.50;
|
||||||
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>Итвио</string>
|
<string>eatvio_app</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>67</string>
|
<string>39</string>
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
|||||||
@@ -18,66 +18,9 @@ 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
|
||||||
UI.message "Current working directory: #{Dir.pwd}"
|
match(type: "appstore")
|
||||||
UI.message "Checking API key file..."
|
|
||||||
|
|
||||||
possible_paths = [
|
|
||||||
"../AuthKey.p8",
|
|
||||||
"./AuthKey.p8",
|
|
||||||
"../../AuthKey.p8",
|
|
||||||
File.expand_path("../AuthKey.p8"),
|
|
||||||
File.expand_path("./AuthKey.p8")
|
|
||||||
]
|
|
||||||
|
|
||||||
auth_key_path = nil
|
|
||||||
possible_paths.each do |path|
|
|
||||||
if File.exist?(path)
|
|
||||||
UI.message "Found AuthKey.p8 at: #{path}"
|
|
||||||
auth_key_path = File.expand_path(path)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if auth_key_path.nil?
|
|
||||||
UI.user_error!("API key file not found. Tried paths: #{possible_paths.join(', ')}")
|
|
||||||
end
|
|
||||||
|
|
||||||
UI.message "Using auth_key_path: #{auth_key_path}"
|
|
||||||
|
|
||||||
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(
|
upload_to_testflight
|
||||||
workspace: "Runner.xcworkspace",
|
|
||||||
scheme: "Runner",
|
|
||||||
export_method: "app-store",
|
|
||||||
codesigning_identity: "Apple Distribution",
|
|
||||||
xcargs: "--dart-define=HIDE_PLANS=true"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
app_store_connect_api_key(
|
|
||||||
key_id: "325JS9JCZH",
|
|
||||||
issuer_id: "dfa27d1d-2bb8-4d4d-847e-0b32f9b880b3",
|
|
||||||
key_filepath: auth_key_path,
|
|
||||||
duration: 1200,
|
|
||||||
in_house: false
|
|
||||||
)
|
|
||||||
|
|
||||||
begin
|
|
||||||
upload_to_testflight(
|
|
||||||
skip_waiting_for_build_processing: true,
|
|
||||||
skip_submission: true,
|
|
||||||
distribute_external: false,
|
|
||||||
notify_external_testers: false,
|
|
||||||
changelog: "TestFlight build #{get_build_number(xcodeproj: 'Runner.xcodeproj')}",
|
|
||||||
verbose: true
|
|
||||||
)
|
|
||||||
rescue => ex
|
|
||||||
UI.error "Upload to TestFlight failed: #{ex.message}"
|
|
||||||
UI.error "Full error: #{ex.backtrace.join("\n")}"
|
|
||||||
raise ex
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,24 +5,22 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000227">
|
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000206">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="1: match" time="8.292217">
|
<testcase classname="fastlane.lanes" name="1: increment_build_number" time="0.195115">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="2: increment_build_number" time="0.201631">
|
<testcase classname="fastlane.lanes" name="2: build_app" time="116.686863">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="3: build_app" time="9.870647">
|
<testcase classname="fastlane.lanes" name="3: upload_to_testflight" time="338.642203">
|
||||||
|
|
||||||
<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,3 +1,5 @@
|
|||||||
|
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';
|
||||||
@@ -119,9 +121,7 @@ class AppRouter {
|
|||||||
GoRoute(
|
GoRoute(
|
||||||
path: 'forget-password-screen',
|
path: 'forget-password-screen',
|
||||||
name: 'forgetPasswordScreen',
|
name: 'forgetPasswordScreen',
|
||||||
builder: (context, state) => ForgetPasswordScreen(
|
builder: (context, state) => const ForgetPasswordScreen(),
|
||||||
isAuth: state.extra as bool,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: 'scan-qr-code-screen',
|
path: 'scan-qr-code-screen',
|
||||||
@@ -205,9 +205,21 @@ 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()) {
|
||||||
@@ -216,7 +228,7 @@ class AppRouter {
|
|||||||
return '/auth-screen';
|
return '/auth-screen';
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return '/feed-screen';
|
rethrow;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -361,10 +373,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 uuid = data['uuid'] as String;
|
final chatId = data['chatId'] as String;
|
||||||
final sendInitialMessage = data['sendInitialMessage'] as bool?;
|
final sendInitialMessage = data['sendInitialMessage'] as bool?;
|
||||||
return ChatScreen(
|
return ChatScreen(
|
||||||
uuid: uuid,
|
chatId: chatId,
|
||||||
sendInitialMessage: sendInitialMessage,
|
sendInitialMessage: sendInitialMessage,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
||||||
|
|
||||||
part 'comment_update_model.freezed.dart';
|
|
||||||
part 'comment_update_model.g.dart';
|
|
||||||
|
|
||||||
@freezed
|
|
||||||
abstract class CommentUpdateModel with _$CommentUpdateModel {
|
|
||||||
|
|
||||||
factory CommentUpdateModel({required String body}) = _CommentUpdateModel;
|
|
||||||
|
|
||||||
factory CommentUpdateModel.fromJson(Map<String, dynamic> json) => _$CommentUpdateModelFromJson(json);
|
|
||||||
}
|
|
||||||
@@ -1,204 +0,0 @@
|
|||||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
|
||||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
|
||||||
import 'package:eatvio_app/core/widgets/bottom_sheet_handler_bar.dart';
|
|
||||||
import 'package:eatvio_app/core/widgets/custom_divider.dart';
|
|
||||||
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
|
||||||
import 'package:eatvio_app/generated/l10n.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_svg/svg.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
|
|
||||||
void showCommentsActionsBottomSheet(
|
|
||||||
BuildContext context, {
|
|
||||||
required void Function()? reply,
|
|
||||||
required void Function() copy,
|
|
||||||
required void Function(String text) edit,
|
|
||||||
required void Function() delete,
|
|
||||||
required void Function() complain,
|
|
||||||
required String commentText,
|
|
||||||
required bool isMyComment,
|
|
||||||
}) {
|
|
||||||
showModalBottomSheet(
|
|
||||||
context: context,
|
|
||||||
isScrollControlled: true,
|
|
||||||
backgroundColor: Colors.transparent,
|
|
||||||
builder: (context) => Container(
|
|
||||||
margin: EdgeInsets.only(left: 8, right: 8, bottom: MediaQuery.of(context).padding.bottom + 20),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
color: appTheme.whiteA700,
|
|
||||||
),
|
|
||||||
child: CommentsActionsBottomSheet(
|
|
||||||
isMyComment: isMyComment,
|
|
||||||
reply: reply,
|
|
||||||
copy: copy,
|
|
||||||
edit: edit,
|
|
||||||
delete: delete,
|
|
||||||
complain: complain,
|
|
||||||
commentText: commentText,
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
class CommentsActionsBottomSheet extends StatefulWidget {
|
|
||||||
const CommentsActionsBottomSheet({
|
|
||||||
super.key,
|
|
||||||
required this.isMyComment,
|
|
||||||
required this.reply,
|
|
||||||
required this.copy,
|
|
||||||
required this.edit,
|
|
||||||
required this.delete,
|
|
||||||
required this.complain,
|
|
||||||
required this.commentText,
|
|
||||||
});
|
|
||||||
final bool isMyComment;
|
|
||||||
final void Function()? reply;
|
|
||||||
final void Function() copy;
|
|
||||||
final void Function(String text) edit;
|
|
||||||
final void Function() delete;
|
|
||||||
final void Function() complain;
|
|
||||||
final String commentText;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<CommentsActionsBottomSheet> createState() => _CommentsActionsBottomSheetState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CommentsActionsBottomSheetState extends State<CommentsActionsBottomSheet> {
|
|
||||||
bool isUserEditingComment = false;
|
|
||||||
final controller = TextEditingController();
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
controller.text = widget.commentText;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final s = S.of(context);
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
||||||
child: isUserEditingComment
|
|
||||||
? Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Row(children: [
|
|
||||||
Expanded(
|
|
||||||
child: CustomTextFormField(
|
|
||||||
autofocus: true,
|
|
||||||
controller: controller,
|
|
||||||
onChanged: (value) => setState(() {}),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
widget.edit(controller.text);
|
|
||||||
GoRouter.of(context).pop();
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: controller.text.length > 2 ? appTheme.blueA400 : appTheme.gray50,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
height: 32,
|
|
||||||
width: 32,
|
|
||||||
child: Icon(
|
|
||||||
Icons.send_rounded,
|
|
||||||
size: 18,
|
|
||||||
color: controller.text.length > 2 ? appTheme.whiteA700 : appTheme.black,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
])
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const SizedBox(
|
|
||||||
height: 7,
|
|
||||||
),
|
|
||||||
const BottomSheetHandlerBar(),
|
|
||||||
if (widget.isMyComment)
|
|
||||||
_ActionWidget(
|
|
||||||
text: s.edit,
|
|
||||||
iconPath: 'assets/icons/edit_paint.svg',
|
|
||||||
needToPop: false,
|
|
||||||
onTap: () => setState(() {
|
|
||||||
isUserEditingComment = true;
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
else if (widget.reply != null)
|
|
||||||
_ActionWidget(
|
|
||||||
text: s.reply,
|
|
||||||
iconPath: 'assets/icons/reply.svg',
|
|
||||||
onTap: widget.reply!,
|
|
||||||
),
|
|
||||||
if (widget.reply != null) const CustomDivider(),
|
|
||||||
_ActionWidget(
|
|
||||||
text: s.copy,
|
|
||||||
iconPath: 'assets/icons/copy2.svg',
|
|
||||||
onTap: widget.copy,
|
|
||||||
),
|
|
||||||
if (widget.isMyComment) ...[
|
|
||||||
if (widget.reply != null) ...[
|
|
||||||
const CustomDivider(),
|
|
||||||
_ActionWidget(
|
|
||||||
text: s.reply,
|
|
||||||
iconPath: 'assets/icons/reply.svg',
|
|
||||||
onTap: widget.reply!,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
const CustomDivider(),
|
|
||||||
_ActionWidget(
|
|
||||||
text: s.deleteComment,
|
|
||||||
iconPath: 'assets/icons/red_trash.svg',
|
|
||||||
onTap: widget.delete,
|
|
||||||
),
|
|
||||||
] else ...[
|
|
||||||
const CustomDivider(),
|
|
||||||
_ActionWidget(
|
|
||||||
text: s.complain,
|
|
||||||
iconPath: 'assets/icons/complain.svg',
|
|
||||||
onTap: widget.complain,
|
|
||||||
),
|
|
||||||
]
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ActionWidget extends StatelessWidget {
|
|
||||||
const _ActionWidget({required this.text, required this.iconPath, required this.onTap, this.needToPop = true});
|
|
||||||
final String text;
|
|
||||||
final String iconPath;
|
|
||||||
final void Function() onTap;
|
|
||||||
final bool needToPop;
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
if (needToPop) Navigator.pop(context);
|
|
||||||
onTap();
|
|
||||||
},
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
text,
|
|
||||||
style: TextStyles.textLMedium.copyWith(color: appTheme.black),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SvgPicture.asset(iconPath)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import 'package:eatvio_app/core/comments/models/comment_update_model.dart';
|
|
||||||
import 'package:eatvio_app/core/comments/services/comments_service.dart';
|
|
||||||
|
|
||||||
class CommentsRepository {
|
|
||||||
final CommentsService service;
|
|
||||||
|
|
||||||
CommentsRepository({required this.service});
|
|
||||||
|
|
||||||
Future<void> deleteComment({required int id}) async {
|
|
||||||
try {
|
|
||||||
return service.deleteComment(id);
|
|
||||||
} catch (e) {
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> updateComment({required int id, required String body}) async {
|
|
||||||
try {
|
|
||||||
return service.updateComment(id, CommentUpdateModel(body: body));
|
|
||||||
} catch (e) {
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:eatvio_app/core/comments/models/comment_update_model.dart';
|
|
||||||
import 'package:retrofit/retrofit.dart';
|
|
||||||
|
|
||||||
part 'comments_service.g.dart';
|
|
||||||
|
|
||||||
@RestApi()
|
|
||||||
abstract class CommentsService {
|
|
||||||
factory CommentsService(Dio dio, {String baseUrl}) = _CommentsService;
|
|
||||||
|
|
||||||
@DELETE('/api/comments/{id}')
|
|
||||||
Future<void> deleteComment(@Path('id') int id);
|
|
||||||
|
|
||||||
@PATCH('/api/comments/{id}')
|
|
||||||
Future<void> updateComment(@Path('id') int id, @Body() CommentUpdateModel body);
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
import 'package:eatvio_app/core/env_config.dart';
|
String baseUrl = 'https://eatvio.ru';
|
||||||
|
// 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];
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,7 @@
|
|||||||
enum ComplainedContent { diet, plan, recipe, profile, posts, comment }
|
enum ComplainedContent {
|
||||||
|
diet,
|
||||||
|
plan,
|
||||||
|
recipe,
|
||||||
|
profile,
|
||||||
|
posts
|
||||||
|
}
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
|
||||||
|
|
||||||
class EnvConfig {
|
|
||||||
static String get baseUrl => dotenv.env['BASE_URL'] ?? '';
|
|
||||||
static String get devBaseUrl => dotenv.env['DEV_BASE_URL'] ?? '';
|
|
||||||
|
|
||||||
// Sentry Configuration
|
|
||||||
static String get sentryDsn => dotenv.env['SENTRY_DSN'] ?? '';
|
|
||||||
|
|
||||||
// Firebase Configuration
|
|
||||||
static String get firebaseProjectId => dotenv.env['FIREBASE_PROJECT_ID'] ?? '';
|
|
||||||
static String get firebaseDatabaseUrl => dotenv.env['FIREBASE_DATABASE_URL'] ?? '';
|
|
||||||
static String get firebaseStorageBucket => dotenv.env['FIREBASE_STORAGE_BUCKET'] ?? '';
|
|
||||||
static String get firebaseMessagingSenderId => dotenv.env['FIREBASE_MESSAGING_SENDER_ID'] ?? '';
|
|
||||||
|
|
||||||
// Firebase Android Configuration
|
|
||||||
static String get firebaseAndroidApiKey => dotenv.env['FIREBASE_ANDROID_API_KEY'] ?? '';
|
|
||||||
static String get firebaseAndroidAppId => dotenv.env['FIREBASE_ANDROID_APP_ID'] ?? '';
|
|
||||||
|
|
||||||
// Firebase iOS Configuration
|
|
||||||
static String get firebaseIosApiKey => dotenv.env['FIREBASE_IOS_API_KEY'] ?? '';
|
|
||||||
static String get firebaseIosAppId => dotenv.env['FIREBASE_IOS_APP_ID'] ?? '';
|
|
||||||
static String get firebaseIosBundleId => dotenv.env['FIREBASE_IOS_BUNDLE_ID'] ?? '';
|
|
||||||
|
|
||||||
// App Store URLs
|
|
||||||
static String get appStoreUrl => dotenv.env['APP_STORE_URL'] ?? '';
|
|
||||||
static String get rustoreUrl => dotenv.env['RUSTORE_URL'] ?? '';
|
|
||||||
|
|
||||||
// Telegram Support
|
|
||||||
static String get telegramSupportUrl => dotenv.env['TELEGRAM_SUPPORT_URL'] ?? '';
|
|
||||||
static String get supportEmail => dotenv.env['SUPPORT_EMAIL'] ?? '';
|
|
||||||
|
|
||||||
// Test Credentials (for development only)
|
|
||||||
static String get testEmail => dotenv.env['TEST_EMAIL'] ?? '';
|
|
||||||
static String get testPassword => dotenv.env['TEST_PASSWORD'] ?? '';
|
|
||||||
|
|
||||||
// Email Validation API
|
|
||||||
static String get emailValidationApiUrl => dotenv.env['EMAIL_VALIDATION_API_URL'] ?? '';
|
|
||||||
|
|
||||||
// Connection Check URLs
|
|
||||||
static String get googleCheckUrl => dotenv.env['GOOGLE_CHECK_URL'] ?? '';
|
|
||||||
static String get yandexCheckUrl => dotenv.env['YANDEX_CHECK_URL'] ?? '';
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,3 @@
|
|||||||
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 {
|
||||||
@@ -14,22 +7,4 @@ 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:eatvio_app/core/comments/repositories/comments_repository.dart';
|
|
||||||
import 'package:eatvio_app/core/comments/services/comments_service.dart';
|
|
||||||
import 'package:eatvio_app/core/const.dart';
|
import 'package:eatvio_app/core/const.dart';
|
||||||
import 'package:eatvio_app/core/dio/phone_verify_error_interceptor.dart';
|
import 'package:eatvio_app/core/dio/phone_verify_error_interceptor.dart';
|
||||||
import 'package:eatvio_app/core/firebase_api.dart';
|
import 'package:eatvio_app/core/firebase_api.dart';
|
||||||
@@ -58,7 +56,7 @@ import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
|
|||||||
import 'package:eatvio_app/features/diary/repositories/diary_repository.dart';
|
import 'package:eatvio_app/features/diary/repositories/diary_repository.dart';
|
||||||
import 'package:eatvio_app/features/diary/services/diary_service.dart';
|
import 'package:eatvio_app/features/diary/services/diary_service.dart';
|
||||||
import 'package:eatvio_app/features/diets/presentation/cubit/about_diet_cubit.dart';
|
import 'package:eatvio_app/features/diets/presentation/cubit/about_diet_cubit.dart';
|
||||||
import 'package:eatvio_app/core/comments/comments_cubit/comments_cubit.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart';
|
||||||
import 'package:eatvio_app/features/feed/presentation/bloc/consultants_cubit/consultants_cubit.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/consultants_cubit/consultants_cubit.dart';
|
||||||
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart';
|
||||||
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
|
||||||
@@ -130,7 +128,7 @@ Future<void> init() async {
|
|||||||
sl.registerFactory<ProfileCubit>(() => ProfileCubit(profileRepository: sl(), chatRepository: sl(), consultantRepository: sl()));
|
sl.registerFactory<ProfileCubit>(() => ProfileCubit(profileRepository: sl(), chatRepository: sl(), consultantRepository: sl()));
|
||||||
sl.registerFactory<profile_plans.PlansCubit>(() => profile_plans.PlansCubit(plansRepository: sl()));
|
sl.registerFactory<profile_plans.PlansCubit>(() => profile_plans.PlansCubit(plansRepository: sl()));
|
||||||
sl.registerFactory<DietsCubit>(() => DietsCubit(feedRepository: sl(), dietsRepository: sl()));
|
sl.registerFactory<DietsCubit>(() => DietsCubit(feedRepository: sl(), dietsRepository: sl()));
|
||||||
sl.registerFactory<CommentsCubit>(() => CommentsCubit(feedRepository: sl(), commentsRepository: sl()));
|
sl.registerFactory<CommentsCubit>(() => CommentsCubit(feedRepository: sl()));
|
||||||
sl.registerFactory<PlansCubit>(() => PlansCubit(feedRepository: sl(), planRepository: sl()));
|
sl.registerFactory<PlansCubit>(() => PlansCubit(feedRepository: sl(), planRepository: sl()));
|
||||||
sl.registerFactory<SettingsCubit>(() => SettingsCubit(profileRepository: sl(), settingsRepository: sl(), consultantRepository: sl()));
|
sl.registerFactory<SettingsCubit>(() => SettingsCubit(profileRepository: sl(), settingsRepository: sl(), consultantRepository: sl()));
|
||||||
sl.registerFactory<RegisterConsultantCubit>(() => RegisterConsultantCubit(consultantRepository: sl()));
|
sl.registerFactory<RegisterConsultantCubit>(() => RegisterConsultantCubit(consultantRepository: sl()));
|
||||||
@@ -200,7 +198,6 @@ Future<void> init() async {
|
|||||||
sl.registerFactory<MyRecipesRepository>(() => MyRecipesRepository(myRecipesService: sl()));
|
sl.registerFactory<MyRecipesRepository>(() => MyRecipesRepository(myRecipesService: sl()));
|
||||||
sl.registerFactory<ResetPasswordRepository>(() => ResetPasswordRepository(resetPasswordService: sl()));
|
sl.registerFactory<ResetPasswordRepository>(() => ResetPasswordRepository(resetPasswordService: sl()));
|
||||||
sl.registerLazySingleton<BlockedUsersRepository>(() => BlockedUsersRepository(blockedUserService: sl()));
|
sl.registerLazySingleton<BlockedUsersRepository>(() => BlockedUsersRepository(blockedUserService: sl()));
|
||||||
sl.registerLazySingleton<CommentsRepository>(() => CommentsRepository(service: sl()));
|
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
sl.registerLazySingleton<ChatService>(() => ChatService(sl()));
|
sl.registerLazySingleton<ChatService>(() => ChatService(sl()));
|
||||||
@@ -233,7 +230,6 @@ Future<void> init() async {
|
|||||||
sl.registerLazySingleton<FirebaseApi>(() => FirebaseApi());
|
sl.registerLazySingleton<FirebaseApi>(() => FirebaseApi());
|
||||||
sl.registerLazySingleton<ResetPasswordService>(() => ResetPasswordService(sl()));
|
sl.registerLazySingleton<ResetPasswordService>(() => ResetPasswordService(sl()));
|
||||||
sl.registerLazySingleton<BlockedUserService>(() => BlockedUserService(sl()));
|
sl.registerLazySingleton<BlockedUserService>(() => BlockedUserService(sl()));
|
||||||
sl.registerLazySingleton<CommentsService>(() => CommentsService(sl()));
|
|
||||||
|
|
||||||
// External
|
// External
|
||||||
sl.registerLazySingleton(
|
sl.registerLazySingleton(
|
||||||
@@ -244,6 +240,7 @@ 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,5 +1,3 @@
|
|||||||
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';
|
||||||
@@ -13,10 +11,6 @@ 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,25 +21,3 @@ 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,4 +1,3 @@
|
|||||||
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';
|
||||||
@@ -60,24 +59,6 @@ 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;
|
||||||
@@ -96,8 +77,9 @@ 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) {
|
||||||
|
|||||||
@@ -26,13 +26,9 @@ class SuccessPublishedRecipeBottomSheet extends StatelessWidget {
|
|||||||
s.recipe_publishing,
|
s.recipe_publishing,
|
||||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||||
),
|
),
|
||||||
Padding(
|
Text(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
'"$title" ${s.successfully_published}',
|
||||||
child: Text(
|
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||||
'"$title" ${s.successfully_published}',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
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';
|
||||||
@@ -67,20 +66,19 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AppRouter.navigatorKey.currentContext?.go('/feed-screen', extra: {'needToInitializeWebSocket': false});
|
AppRouter.navigatorKey.currentContext?.go(
|
||||||
|
'/feed-screen',
|
||||||
|
extra: {'needToInitializeWebSocket': false},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@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>(
|
||||||
@@ -99,7 +97,11 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
GoRouter.of(context).pushNamed(DevToolsScreen.name);
|
GoRouter.of(context).pushNamed(DevToolsScreen.name);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: SvgPicture.asset(ImageConstant.imgLogoEatvio, height: isFocused ? 64 : MediaQuery.of(context).size.width - 64, fit: BoxFit.contain),
|
child: SvgPicture.asset(
|
||||||
|
ImageConstant.imgLogoEatvio,
|
||||||
|
height: isFocused ? 64 : MediaQuery.of(context).size.width - 64,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
@@ -130,16 +132,23 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
hidePassword = !hidePassword;
|
hidePassword = !hidePassword;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
icon: Icon(hidePassword ? Icons.visibility_outlined : Icons.visibility_off_outlined, color: Colors.grey),
|
icon: Icon(
|
||||||
|
hidePassword ? Icons.visibility_outlined : Icons.visibility_off_outlined,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Center(
|
Center(
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () => GoRouter.of(context).goNamed('forgetPasswordScreen', extra: false),
|
onTap: () => GoRouter.of(context).goNamed('forgetPasswordScreen'),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Забыли пароль?',
|
'Забыли пароль?',
|
||||||
style: TextStyle(color: appTheme.blueA400, decorationColor: appTheme.blueA400, decoration: TextDecoration.underline),
|
style: TextStyle(
|
||||||
|
color: appTheme.blueA400,
|
||||||
|
decorationColor: appTheme.blueA400,
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -152,7 +161,11 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
onTap: () => GoRouter.of(context).goNamed('registrationScreen'),
|
onTap: () => GoRouter.of(context).goNamed('registrationScreen'),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Зарегистрироваться',
|
'Зарегистрироваться',
|
||||||
style: TextStyle(color: appTheme.blueA400, decorationColor: appTheme.blueA400, decoration: TextDecoration.underline),
|
style: TextStyle(
|
||||||
|
color: appTheme.blueA400,
|
||||||
|
decorationColor: appTheme.blueA400,
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -163,22 +176,23 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
text: 'Нажимая "Войти" или "Зарегистрироваться", Вы соглашаетесь \n',
|
text: 'Нажимая "Войти" или "Зарегистрироваться", Вы соглашаетесь \n',
|
||||||
style: const TextStyle(fontSize: 12, color: Colors.black),
|
style: const TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
children: [
|
children: [
|
||||||
const TextSpan(text: 'c '),
|
const TextSpan(text: 'c '),
|
||||||
if (!hidePlans) ...[
|
TextSpan(
|
||||||
TextSpan(
|
text: ' Публичной офертой',
|
||||||
text: ' Публичной офертой',
|
style: TextStyle(
|
||||||
style: TextStyle(
|
fontSize: 12,
|
||||||
fontSize: 12,
|
color: appTheme.blueA400,
|
||||||
color: appTheme.blueA400,
|
decorationColor: appTheme.blueA400,
|
||||||
decorationColor: appTheme.blueA400,
|
decoration: TextDecoration.underline,
|
||||||
decoration: TextDecoration.underline,
|
|
||||||
),
|
|
||||||
recognizer: TapGestureRecognizer()..onTap = () async => await launchUrl(Uri.parse('$baseUrl/oferta_bs.pdf')),
|
|
||||||
),
|
),
|
||||||
const TextSpan(text: ' и '),
|
recognizer: TapGestureRecognizer()..onTap = () async => await launchUrl(Uri.parse('$baseUrl/oferta_bs.pdf')),
|
||||||
],
|
),
|
||||||
|
const TextSpan(text: ' и '),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: 'Политикой конфиденциальности\nи защитой персональных данных',
|
text: 'Политикой конфиденциальности\nи защитой персональных данных',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -204,12 +218,17 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
highlightElevation: 0,
|
highlightElevation: 0,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
focusElevation: 0,
|
focusElevation: 0,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
backgroundColor: state is AuthStateLoading ? Colors.black.withAlpha(50) : appTheme.blueA400,
|
backgroundColor: state is AuthStateLoading ? Colors.black.withAlpha(50) : appTheme.blueA400,
|
||||||
onPressed: state is AuthStateLoading
|
onPressed: state is AuthStateLoading
|
||||||
? null
|
? null
|
||||||
: () async {
|
: () async {
|
||||||
final isAuthenticated = await authCubit.getTokenByCredentials(emailController.text, passwordController.text);
|
final isAuthenticated = await authCubit.getTokenByCredentials(
|
||||||
|
emailController.text,
|
||||||
|
passwordController.text,
|
||||||
|
);
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
_reconnectWebSocket(context);
|
_reconnectWebSocket(context);
|
||||||
@@ -218,7 +237,10 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
},
|
},
|
||||||
label: state is AuthStateLoading
|
label: state is AuthStateLoading
|
||||||
? const CupertinoActivityIndicator()
|
? const CupertinoActivityIndicator()
|
||||||
: Text('Войти', style: theme.textTheme.titleSmall!.copyWith(color: Colors.white)),
|
: Text(
|
||||||
|
'Войти',
|
||||||
|
style: theme.textTheme.titleSmall!.copyWith(color: Colors.white),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
@@ -230,7 +252,9 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
highlightElevation: 0,
|
highlightElevation: 0,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
focusElevation: 0,
|
focusElevation: 0,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
backgroundColor: state is AuthStateLoading ? Colors.black.withAlpha(50) : appTheme.blueA400,
|
backgroundColor: state is AuthStateLoading ? Colors.black.withAlpha(50) : appTheme.blueA400,
|
||||||
onPressed: state is AuthStateLoading
|
onPressed: state is AuthStateLoading
|
||||||
? null
|
? null
|
||||||
@@ -245,10 +269,14 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const Text('Где взять QR-код?\n', style: TextStyle(fontSize: 18)),
|
const Text(
|
||||||
Text(
|
'Где взять QR-код?\n',
|
||||||
'1. Войдите на сайте $baseUrl в свой аккаунт\n\n2. Перейдите в настройки профиля\n\n3. В разделе "Доп информация" перейдите на "QR Login"',
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
Text(
|
||||||
|
'1. Войдите на сайте $baseUrl в свой аккаунт\n\n2. Перейдите в настройки профиля\n\n3. В разделе "Доп информация" перейдите на "QR Login"'),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
@@ -260,10 +288,15 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
},
|
},
|
||||||
child: Ink(
|
child: Ink(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)),
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
child: const Text(
|
child: const Text(
|
||||||
'Отмена',
|
'Отмена',
|
||||||
style: TextStyle(color: Colors.black, fontWeight: FontWeight.w400),
|
style: TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -276,10 +309,15 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
},
|
},
|
||||||
child: Ink(
|
child: Ink(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)),
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Перейти',
|
'Перейти',
|
||||||
style: TextStyle(color: appTheme.blueA400, fontWeight: FontWeight.w600),
|
style: TextStyle(
|
||||||
|
color: appTheme.blueA400,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -294,7 +332,10 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
},
|
},
|
||||||
label: state is AuthStateLoading
|
label: state is AuthStateLoading
|
||||||
? const CupertinoActivityIndicator()
|
? const CupertinoActivityIndicator()
|
||||||
: const Icon(Icons.qr_code_scanner_rounded, color: Colors.white),
|
: const Icon(
|
||||||
|
Icons.qr_code_scanner_rounded,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
class ForgetPasswordScreen extends StatefulWidget {
|
class ForgetPasswordScreen extends StatefulWidget {
|
||||||
final bool isAuth;
|
const ForgetPasswordScreen({super.key});
|
||||||
const ForgetPasswordScreen({super.key, required this.isAuth});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ForgetPasswordScreen> createState() => _ForgetPasswordScreenState();
|
State<ForgetPasswordScreen> createState() => _ForgetPasswordScreenState();
|
||||||
@@ -30,7 +29,7 @@ class _ForgetPasswordScreenState extends State<ForgetPasswordScreen> {
|
|||||||
builder: (BuildContext context, ResetPasswordState state) {
|
builder: (BuildContext context, ResetPasswordState state) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: widget.isAuth ? const Text('Смена пароля') : const Text('Восстановление пароля'),
|
title: const Text('Смена пароля'),
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
),
|
),
|
||||||
body: state is ResetPasswordStateLoadingResetPassword || state is ResetPasswordStateLoadingSendEmailCode
|
body: state is ResetPasswordStateLoadingResetPassword || state is ResetPasswordStateLoadingSendEmailCode
|
||||||
@@ -43,9 +42,9 @@ class _ForgetPasswordScreenState extends State<ForgetPasswordScreen> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (state is ResetPasswordStateCodeSendError || state is ResetPasswordStateEmpty) ...[
|
if (state is ResetPasswordStateCodeSendError || state is ResetPasswordStateEmpty) ...[
|
||||||
Text(
|
const Text(
|
||||||
'${widget.isAuth ? '' : 'Забыли пароль? '}Укажите свой email,\nи мы пришлём код для сброса.',
|
'Забыли пароль? Укажите свой email,\nи мы пришлём код для сброса.',
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
@@ -157,7 +156,7 @@ class _ForgetPasswordScreenState extends State<ForgetPasswordScreen> {
|
|||||||
}
|
}
|
||||||
if (state is ResetPasswordStateResetPassowrdSuccess) {
|
if (state is ResetPasswordStateResetPassowrdSuccess) {
|
||||||
GoRouter.of(context).pop();
|
GoRouter.of(context).pop();
|
||||||
showSuccessToast('Пароль успешно сменён. ${widget.isAuth ? '' : 'Войдите в аккаунт'}');
|
showSuccessToast('Пароль успешно сменён. Войдите в аккаунт');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import 'package:eatvio_app/core/widgets/custom_popup.dart';
|
|||||||
import 'package:eatvio_app/core/widgets/loader.dart';
|
import 'package:eatvio_app/core/widgets/loader.dart';
|
||||||
import 'package:eatvio_app/features/blog_post/presentation/cubit/blog_post_cubit.dart';
|
import 'package:eatvio_app/features/blog_post/presentation/cubit/blog_post_cubit.dart';
|
||||||
import 'package:eatvio_app/features/blog_post/presentation/cubit/blog_post_state.dart';
|
import 'package:eatvio_app/features/blog_post/presentation/cubit/blog_post_state.dart';
|
||||||
import 'package:eatvio_app/core/comments/comments_cubit/comments_cubit.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart';
|
||||||
import 'package:eatvio_app/core/comments/comments_cubit/comments_state.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_state.dart';
|
||||||
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
|
||||||
import 'package:eatvio_app/features/feed/presentation/widgets/comments_widget.dart';
|
import 'package:eatvio_app/features/feed/presentation/widgets/comments_widget.dart';
|
||||||
import 'package:eatvio_app/core/locator_service.dart';
|
import 'package:eatvio_app/core/locator_service.dart';
|
||||||
|
|||||||
@@ -22,9 +22,13 @@ 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,4 +1,3 @@
|
|||||||
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';
|
||||||
@@ -9,18 +8,29 @@ 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: "data") required List<ChatRoom> data,
|
@JsonKey(name: "chatRooms") required ChatRooms chatRooms,
|
||||||
@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,8 +6,11 @@ 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({
|
||||||
@JsonKey(name: 'room_name') required String name,
|
required int id,
|
||||||
|
@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 uuid) async {
|
Future<void> loadNextPage(String chatId) 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(uuid, currentPage + 1);
|
final chatMessages = await chatRepository.getChatMessages(chatId, currentPage + 1);
|
||||||
|
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
emit(
|
emit(
|
||||||
@@ -89,19 +89,17 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initChat(
|
Future<void> initChat(String chatId, {bool sendInitialMessage = false}) async {
|
||||||
String uuid, {
|
|
||||||
bool sendInitialMessage = false,
|
|
||||||
}) async {
|
|
||||||
try {
|
try {
|
||||||
final chatRoom = await chatRepository.getChatRoom(uuid);
|
final chatRoom = await chatRepository.getChatRoom(chatId);
|
||||||
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(uuid, currentPage);
|
final chatMessages = await chatRepository.getChatMessages(chatId, 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),
|
||||||
@@ -160,14 +158,14 @@ class ChatRoomCubit extends Cubit<ChatRoomState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _subscribeToChatChannel({required String uuid, required String chatId}) async {
|
Future<void> _subscribeToChatChannel({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));
|
final message = ChatMessageModel.fromJson(jsonDecode(event.data)['message']);
|
||||||
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));
|
||||||
@@ -181,15 +179,7 @@ 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',
|
'client-typing', const Duration(seconds: 3), () => emit((state as ChatRoomStateLoaded).copyWith(isTyping: false, isLoading: isLoading)));
|
||||||
const Duration(seconds: 3),
|
|
||||||
() => emit(
|
|
||||||
(state as ChatRoomStateLoaded).copyWith(
|
|
||||||
isTyping: false,
|
|
||||||
isLoading: isLoading,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,31 +189,12 @@ 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: compressedFiles,
|
files: files != null ? await ImageCompressUtility.compressFilesList(List<File>.from(files)) : null,
|
||||||
slug: userSlug,
|
slug: userSlug,
|
||||||
);
|
);
|
||||||
sl<ChatsCubit>().getChatRooms();
|
AudioPlayer().play((AssetSource('audio/send_message.wav')));
|
||||||
return true;
|
return true;
|
||||||
} catch (e, st) {
|
} catch (e, st) {
|
||||||
log(e.toString(), stackTrace: st);
|
log(e.toString(), stackTrace: st);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class ChatsCubit extends Cubit<ChatsState> {
|
|||||||
required this.chatRepository,
|
required this.chatRepository,
|
||||||
required this.wsManager,
|
required this.wsManager,
|
||||||
required this.profileRepository,
|
required this.profileRepository,
|
||||||
}) : super(ChatsState()) {
|
}) : super(ChatsState()) {
|
||||||
newLastMessageSubscription = chatRepository.newUserMessagesStream.listen((message) {
|
newLastMessageSubscription = chatRepository.newUserMessagesStream.listen((message) {
|
||||||
_setNewLastMessage(message);
|
_setNewLastMessage(message);
|
||||||
});
|
});
|
||||||
@@ -29,7 +29,6 @@ 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) {
|
||||||
@@ -44,10 +43,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.meta.lastPage + 1;
|
lastPage = chatRoomsPaginationModel.chatRooms.lastPage + 1;
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
chatRooms: chatRoomsPaginationModel.data,
|
chatRooms: chatRoomsPaginationModel.chatRooms.data,
|
||||||
chatsWithUnreadMessagesCount: unreadChats,
|
chatsWithUnreadMessagesCount: unreadChats,
|
||||||
state: BaseState.success,
|
state: BaseState.success,
|
||||||
),
|
),
|
||||||
@@ -66,10 +65,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.meta.lastPage + 1;
|
lastPage = chatRoomsPaginationModel.chatRooms.lastPage + 1;
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
chatRooms: chatRoomsPaginationModel.data,
|
chatRooms: chatRoomsPaginationModel.chatRooms.data,
|
||||||
chatsWithUnreadMessagesCount: unreadChats,
|
chatsWithUnreadMessagesCount: unreadChats,
|
||||||
state: BaseState.success,
|
state: BaseState.success,
|
||||||
),
|
),
|
||||||
@@ -91,7 +90,7 @@ class ChatsCubit extends Cubit<ChatsState> {
|
|||||||
final unreadChats = chatRoomsPaginationModel.unreadChatsCount;
|
final unreadChats = chatRoomsPaginationModel.unreadChatsCount;
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
chatRooms: [...state.chatRooms, ...chatRoomsPaginationModel.data],
|
chatRooms: [...state.chatRooms, ...chatRoomsPaginationModel.chatRooms.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 uuid;
|
final String chatId;
|
||||||
final bool? sendInitialMessage;
|
final bool? sendInitialMessage;
|
||||||
|
|
||||||
const ChatScreen({required this.uuid, super.key, required this.sendInitialMessage});
|
const ChatScreen({required this.chatId, 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.uuid, sendInitialMessage: widget.sendInitialMessage ?? false);
|
chatRoomCubit.initChat(widget.chatId, 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.uuid);
|
chatRoomCubit.loadNextPage(widget.chatId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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.uuid)));
|
return Scaffold(appBar: AppBar(), body: DataErrorWidget(onReload: () => chatRoomCubit.initChat(widget.chatId)));
|
||||||
} 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: {
|
||||||
'uuid': chatIdOrError,
|
'chatId': chatIdOrError,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -186,7 +186,7 @@ class _EmptyChatScreenState extends State<EmptyChatScreen> {
|
|||||||
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
||||||
'chatScreen',
|
'chatScreen',
|
||||||
extra: {
|
extra: {
|
||||||
'uuid': chatIdOrError,
|
'chatId': chatIdOrError,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -214,7 +214,7 @@ class _EmptyChatScreenState extends State<EmptyChatScreen> {
|
|||||||
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
||||||
'chatScreen',
|
'chatScreen',
|
||||||
extra: {
|
extra: {
|
||||||
'uuid': chatIdOrError,
|
'chatId': chatIdOrError,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -264,7 +264,7 @@ class _EmptyChatScreenState extends State<EmptyChatScreen> {
|
|||||||
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
||||||
'chatScreen',
|
'chatScreen',
|
||||||
extra: {
|
extra: {
|
||||||
'uuid': chatIdOrError,
|
'chatId': chatIdOrError,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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/const.dart';
|
import 'package:eatvio_app/core/const.dart';
|
||||||
@@ -9,7 +11,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<dynamic> imageUrls;
|
final List<String> imageUrls;
|
||||||
final int index;
|
final int index;
|
||||||
final bool isLocal;
|
final bool isLocal;
|
||||||
|
|
||||||
@@ -48,12 +50,14 @@ 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: imageProvider,
|
imageProvider: widget.isLocal
|
||||||
|
? 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,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/theme/custom_text_style.dart';
|
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||||
@@ -61,9 +59,8 @@ class ChatBubbleWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: mediaData.isNotEmpty
|
padding:
|
||||||
? const EdgeInsets.all(8)
|
mediaData.isNotEmpty ? const EdgeInsets.all(8) : EdgeInsets.only(top: 8, bottom: 8, left: isCurrentUser ? 16 : 8, right: isCurrentUser ? 8 : 16),
|
||||||
: 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: [
|
||||||
@@ -89,17 +86,17 @@ class ChatBubbleWidget extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
leading: const Icon(
|
leading: const Icon(
|
||||||
Icons.file_copy_outlined,
|
Icons.file_copy_outlined,
|
||||||
color: Colors.black,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
trailing: const Icon(
|
trailing: const Icon(
|
||||||
Icons.download_outlined,
|
Icons.download_outlined,
|
||||||
color: Colors.black,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
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.black,
|
color: Colors.white,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@@ -110,9 +107,8 @@ class ChatBubbleWidget extends StatelessWidget {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
context.pushTransparentRoute(
|
context.pushTransparentRoute(
|
||||||
PhotoViewScreen(
|
PhotoViewScreen(
|
||||||
imageUrls: mediaData,
|
imageUrls: mediaData.map((e) => '$e').toList(),
|
||||||
index: index,
|
index: index,
|
||||||
isLocal: mediaData[index] is File ? true : false,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -121,11 +117,9 @@ class ChatBubbleWidget extends StatelessWidget {
|
|||||||
transitionOnUserGestures: true,
|
transitionOnUserGestures: true,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
child: mediaData[index] is File
|
child: CachedNetworkImage(
|
||||||
? Image.file(mediaData[index])
|
imageUrl: '${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: {
|
||||||
'uuid': widget.chatRoom.id.toString(),
|
'chatId': widget.chatRoom.id.toString(),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ 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';
|
||||||
@@ -48,11 +47,10 @@ class ChatRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await chatService.sendMessage(
|
await chatService.sendMessageByUserSlug(
|
||||||
slug: slug,
|
userSlug: 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);
|
||||||
@@ -60,11 +58,10 @@ class ChatRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ChatRoomModel> getChatRoom(String uuid) async {
|
Future<ChatRoomModel> getChatRoom(String chatId) async {
|
||||||
try {
|
try {
|
||||||
return (await chatService.getChatRoom(uuid: uuid));
|
return (await chatService.getChatRoom(id: chatId))['chatRoom']!;
|
||||||
} catch (e, st) {
|
} catch (e) {
|
||||||
log(e.toString(), stackTrace: st);
|
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,9 +74,9 @@ class ChatRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ChatMessagesModel> getChatMessages(String id, int page) async {
|
Future<ChatMessagesModel> getChatMessages(String chatId, int page) async {
|
||||||
try {
|
try {
|
||||||
return (await chatService.getChatMessages(id: id, page: page));
|
return (await chatService.getChatMessages(id: chatId, page: page));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
@@ -93,9 +90,9 @@ class ChatRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> markMessagesAsRead(String id, List<int> messageIds) async {
|
Future<void> markMessagesAsRead(String chatId, List<int> messageIds) async {
|
||||||
try {
|
try {
|
||||||
await chatService.markMessagesAsRead(id: id, body: {'messages_ids': messageIds});
|
await chatService.markMessagesAsRead(id: chatId, body: {'messages_ids': messageIds});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,33 +13,32 @@ 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/v2/chat/index')
|
@GET('/api/chat/index')
|
||||||
Future<ChatRoomPaginationModel> getChatRooms({
|
Future<ChatRoomPaginationModel> getChatRooms({
|
||||||
@Query('page') required int page,
|
@Query('page') required int page,
|
||||||
@Query('filters[data][\$conversation_name]') String? query,
|
@Query('find') String? query,
|
||||||
});
|
});
|
||||||
|
|
||||||
@GET('/api/v2/chat/show/{uuid}')
|
@GET('/api/chat/show/{id}')
|
||||||
Future<ChatRoomModel> getChatRoom({
|
Future<Map<String, ChatRoomModel>> getChatRoom({
|
||||||
@Path() required String uuid,
|
@Path() required String id,
|
||||||
});
|
});
|
||||||
|
|
||||||
@POST('/api/v2/chat/message/send/{slug}')
|
@POST('/api/v2/chat/message/send/{id}')
|
||||||
@MultiPart()
|
@MultiPart()
|
||||||
Future<void> sendMessage({
|
Future<void> sendMessage({
|
||||||
@Header('X-Socket-ID') required String socketId,
|
@Path() required String id,
|
||||||
@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/v2/chat/show/{id}/messages?page={page}')
|
@GET('/api/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/v2/chat/get_by_user/{user}')
|
@GET('/api/chat/get_by_user/{user}')
|
||||||
Future<ChatRoomModel> getChatRoomIdByUserSlug({
|
Future<ChatRoomModel> getChatRoomIdByUserSlug({
|
||||||
@Path() required String user,
|
@Path() required String user,
|
||||||
});
|
});
|
||||||
@@ -82,8 +81,16 @@ abstract class ChatService {
|
|||||||
@Body() required PlanComplainModel body,
|
@Body() required PlanComplainModel body,
|
||||||
});
|
});
|
||||||
|
|
||||||
@GET('/api/chat/get_by_user/{slug}')
|
@GET('/api/v2/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,7 +9,6 @@ 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';
|
||||||
@@ -195,74 +194,63 @@ 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 BlocBuilder<ComplainingCubit, ComplainingState>(
|
return Column(
|
||||||
builder: (context, state) {
|
mainAxisSize: MainAxisSize.min,
|
||||||
return Column(
|
children: [
|
||||||
mainAxisSize: MainAxisSize.min,
|
const BottomSheetHandlerBar(),
|
||||||
children: [
|
const SizedBox(height: 20),
|
||||||
const BottomSheetHandlerBar(),
|
Text(
|
||||||
const SizedBox(height: 20),
|
s.lawViolation,
|
||||||
Text(
|
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
||||||
s.lawViolation,
|
),
|
||||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
const SizedBox(height: 8),
|
||||||
),
|
SizedBox(
|
||||||
const SizedBox(height: 8),
|
width: MediaQuery.sizeOf(context).width * 0.7,
|
||||||
SizedBox(
|
child: Text(
|
||||||
width: MediaQuery.sizeOf(context).width * 0.7,
|
s.what_do_you_want_to_complaint_about,
|
||||||
child: Text(
|
textAlign: TextAlign.center,
|
||||||
s.what_do_you_want_to_complaint_about,
|
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
|
||||||
textAlign: TextAlign.center,
|
),
|
||||||
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
|
),
|
||||||
),
|
const SizedBox(height: 24),
|
||||||
),
|
...tags.mapIndexed((index, element) {
|
||||||
const SizedBox(height: 24),
|
return Row(
|
||||||
...tags.mapIndexed((index, element) {
|
children: [
|
||||||
return Row(
|
Checkbox(
|
||||||
children: [
|
activeColor: appTheme.blueA400,
|
||||||
Checkbox(
|
side: BorderSide(width: 2, color: tags[index].$1 ? appTheme.blueA400 : appTheme.blueGray),
|
||||||
activeColor: appTheme.blueA400,
|
value: element.$1,
|
||||||
side: BorderSide(width: 2, color: tags[index].$1 ? appTheme.blueA400 : appTheme.blueGray),
|
onChanged: (isChecked) {
|
||||||
value: element.$1,
|
setState(() {
|
||||||
onChanged: (isChecked) {
|
if (isChecked == null) return;
|
||||||
setState(() {
|
if (index == tags.length - 1 && !isChecked) {
|
||||||
if (isChecked == null) return;
|
context.read<ComplainingCubit>().toggleDescription();
|
||||||
if (index == tags.length - 1 && !isChecked) {
|
}
|
||||||
context.read<ComplainingCubit>().toggleDescription();
|
tags[index] = (isChecked, element.$2);
|
||||||
}
|
final activeTags = tags.where((elem) => elem.$1).map((e) => e.$2).toList();
|
||||||
tags[index] = (isChecked, element.$2);
|
context.read<ComplainingCubit>().setTags(activeTags);
|
||||||
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);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if ((state.complainInfo.description?.length ?? 10) < 10) ...[
|
Text(
|
||||||
const SizedBox(height: 4),
|
element.$2,
|
||||||
const ValidationErrorWidget(error: 'Минимум 10 символов'),
|
style: TextStyles.textLSemibold.copyWith(color: appTheme.black),
|
||||||
]
|
),
|
||||||
],
|
],
|
||||||
const SizedBox(height: 20),
|
);
|
||||||
_ComplainButton(
|
}),
|
||||||
isDisabled: (state.complainInfo.description?.length ?? 0) < 10,
|
if (tags.last.$1) ...[
|
||||||
),
|
const SizedBox(height: 12),
|
||||||
],
|
CustomTextFormField(
|
||||||
);
|
hintText: s.type_here_your_complaint,
|
||||||
},
|
onChanged: (value) {
|
||||||
|
context.read<ComplainingCubit>().setDescription(value);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
const _ComplainButton(),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -527,8 +515,8 @@ class _ComplaintFailedWidget extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ComplainButton extends StatelessWidget {
|
class _ComplainButton extends StatelessWidget {
|
||||||
const _ComplainButton({this.isDisabled = false});
|
const _ComplainButton();
|
||||||
final bool isDisabled;
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = S.of(context);
|
final s = S.of(context);
|
||||||
@@ -546,10 +534,6 @@ 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();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -72,12 +72,6 @@ class ComplainingCubit extends Cubit<ComplainingState> {
|
|||||||
emit(state.copyWith(wasSentSuccessfully: true));
|
emit(state.copyWith(wasSentSuccessfully: true));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case ComplainedContent.comment:
|
|
||||||
if (id != null) {
|
|
||||||
await repository.sendCommentComplain(id: id, category: category.getValue(), body: state.complainInfo);
|
|
||||||
emit(state.copyWith(wasSentSuccessfully: true));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e, st) {
|
} catch (e, st) {
|
||||||
|
|||||||
@@ -64,16 +64,4 @@ class ComplainingRepository {
|
|||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> sendCommentComplain({
|
|
||||||
required int id,
|
|
||||||
required String category,
|
|
||||||
required ComplainModel body,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
await service.sendCommentComplain(id: id, category: category, body: body);
|
|
||||||
} catch (e) {
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,12 +42,5 @@ abstract class ComplainingService {
|
|||||||
@Path() required String category,
|
@Path() required String category,
|
||||||
@Body() required ComplainModel body,
|
@Body() required ComplainModel body,
|
||||||
});
|
});
|
||||||
|
|
||||||
@POST('/api/comments/{id}/complain/{category}')
|
|
||||||
Future<void> sendCommentComplain({
|
|
||||||
@Path() required int id,
|
|
||||||
@Path() required String category,
|
|
||||||
@Body() required ComplainModel body,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,16 +25,24 @@ class _LastStepScreenState extends State<LastStepScreen> {
|
|||||||
children: [
|
children: [
|
||||||
const Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: Text('Последний шаг', style: TextStyle(fontSize: 24, fontWeight: FontWeight.w700, height: 1.1)),
|
child: Text(
|
||||||
|
'Последний шаг',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
height: 1.1,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
CheckboxListTile(
|
CheckboxListTile(
|
||||||
value: checkboxState,
|
value: checkboxState,
|
||||||
title: Text(
|
title: const Text(
|
||||||
hidePlans
|
'Я ознакомлен и принимаю договор публичной офёрты',
|
||||||
? 'Я ознакомлен и принимаю политику конфиденциальности и защиту персональных данных'
|
style: TextStyle(
|
||||||
: 'Я ознакомлен и принимаю договор публичной офёрты',
|
fontSize: 18,
|
||||||
style: const TextStyle(fontSize: 18, height: 1),
|
height: 1,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
onChanged: (_) {
|
onChanged: (_) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -45,14 +53,25 @@ class _LastStepScreenState extends State<LastStepScreen> {
|
|||||||
activeColor: appTheme.blueA400,
|
activeColor: appTheme.blueA400,
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
),
|
),
|
||||||
Padding(padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), child: _divider(context)),
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 16,
|
||||||
|
),
|
||||||
|
child: _divider(context),
|
||||||
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||||
child: TextButton(
|
child: TextButton(
|
||||||
onPressed: () async => await launchUrl(Uri.parse(hidePlans ? '$baseUrl/docs/privacy.pdf' : '$baseUrl/oferta_bs.pdf')),
|
onPressed: () async => await launchUrl(Uri.parse('$baseUrl/oferta_bs.pdf')),
|
||||||
child: Text(
|
child: const Text(
|
||||||
hidePlans ? 'Политика конфиденциальности и пользовательское соглашение' : 'Договор офёрты',
|
'Договор офёрты',
|
||||||
style: const TextStyle(height: 1, fontSize: 16, color: Color(0xFF323F4B), decoration: TextDecoration.underline),
|
style: TextStyle(
|
||||||
|
height: 1,
|
||||||
|
fontSize: 16,
|
||||||
|
color: Color(0xFF323F4B),
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -65,7 +84,10 @@ class _LastStepScreenState extends State<LastStepScreen> {
|
|||||||
return Container(
|
return Container(
|
||||||
height: 0.4,
|
height: 0.4,
|
||||||
width: MediaQuery.of(context).size.width - 32,
|
width: MediaQuery.of(context).size.width - 32,
|
||||||
decoration: BoxDecoration(color: Colors.black26, borderRadius: BorderRadius.circular(16)),
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.black26,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ import 'package:go_router/go_router.dart';
|
|||||||
class ConsultationCardWidget extends StatefulWidget {
|
class ConsultationCardWidget extends StatefulWidget {
|
||||||
final ConsultationModel consultationsModel;
|
final ConsultationModel consultationsModel;
|
||||||
|
|
||||||
const ConsultationCardWidget({super.key, required this.consultationsModel});
|
const ConsultationCardWidget({
|
||||||
|
super.key,
|
||||||
|
required this.consultationsModel,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ConsultationCardWidget> createState() => _ConsultationCardWidgetState();
|
State<ConsultationCardWidget> createState() => _ConsultationCardWidgetState();
|
||||||
@@ -29,7 +32,13 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
|
|||||||
'Дневник заполнен, прокомментирован консультантом и после этого клиент просмотрел комментарии.',
|
'Дневник заполнен, прокомментирован консультантом и после этого клиент просмотрел комментарии.',
|
||||||
];
|
];
|
||||||
|
|
||||||
final List<Color> legendColors = const [Colors.white, Color(0xFFF0C497), Color(0xFFFF9500), Color(0xFF2F80ED), Color(0xFF4CD964)];
|
final List<Color> legendColors = const [
|
||||||
|
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 = [
|
||||||
@@ -37,7 +46,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() {
|
||||||
@@ -63,19 +72,32 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
|
|||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
CircleAvatar(radius: 30, backgroundImage: NetworkImage(baseUrl + widget.consultationsModel.userProfilePhotoUrl)),
|
CircleAvatar(
|
||||||
|
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(height: 1, color: Color(0xFF323F4B), fontSize: 16, fontWeight: FontWeight.w600),
|
style: const TextStyle(
|
||||||
|
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(height: 1, color: Color(0xFF88949F), fontSize: 14, fontWeight: FontWeight.w500),
|
style: const TextStyle(
|
||||||
|
height: 1,
|
||||||
|
color: Color(0xFF88949F),
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -105,7 +127,12 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'День ${widget.consultationsModel.currentDiaryDay} из ${widget.consultationsModel.duration}',
|
'День ${widget.consultationsModel.currentDiaryDay} из ${widget.consultationsModel.duration}',
|
||||||
style: const TextStyle(height: 1, color: Color(0xFF323F4B), fontSize: 16, fontWeight: FontWeight.w500),
|
style: const TextStyle(
|
||||||
|
height: 1,
|
||||||
|
color: Color(0xFF323F4B),
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@@ -122,7 +149,12 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
|
|||||||
children: [
|
children: [
|
||||||
const Text(
|
const Text(
|
||||||
'Легенда',
|
'Легенда',
|
||||||
style: TextStyle(height: 1, fontSize: 20, fontWeight: FontWeight.w700, color: Color(0xFF323F4B)),
|
style: TextStyle(
|
||||||
|
height: 1,
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
color: Color(0xFF323F4B),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
ListView.separated(
|
ListView.separated(
|
||||||
@@ -138,7 +170,11 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: const Icon(Icons.help_outline_rounded, color: Color(0xFF323F4B), size: 20),
|
child: const Icon(
|
||||||
|
Icons.help_outline_rounded,
|
||||||
|
color: Color(0xFF323F4B),
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -149,8 +185,7 @@ 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 =
|
final canConsultantOpenThisDay = day != null &&
|
||||||
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),
|
||||||
@@ -160,74 +195,81 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
|
|||||||
InkWell(
|
InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (!hasActivity || !canConsultantOpenThisDay) {
|
if (!hasActivity || !canConsultantOpenThisDay) {
|
||||||
showErrorDialog(
|
showErrorDialog(context,
|
||||||
context,
|
title: s.you_can_not_open_this_day, description: '${widget.consultationsModel.userName} ${s.has_not_filled_it_yet}');
|
||||||
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: {'diaryId': day.$2.id, 'currentDiaryDay': index + 1});
|
GoRouter.of(context).push(UserDiaryScreen.path, extra: {
|
||||||
|
'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(color: hasActivity ? appTheme.gray : appTheme.gray.withValues(alpha: 0.4)),
|
style: TextStyles.textMSemibold.copyWith(
|
||||||
|
color: hasActivity ? appTheme.gray : appTheme.gray.withValues(alpha: 0.4),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (hasActivity) ...[
|
if (hasActivity) ...[
|
||||||
const SizedBox(height: 4),
|
const SizedBox(
|
||||||
|
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(height: 12),
|
const SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
] else
|
] else
|
||||||
const SizedBox(height: 24),
|
const SizedBox(
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -238,7 +280,11 @@ 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(color: Color(0xFF019A3E), fontSize: 20, fontWeight: FontWeight.w600),
|
style: const TextStyle(
|
||||||
|
color: Color(0xFF019A3E),
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Row(
|
Row(
|
||||||
@@ -247,26 +293,36 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
|
|||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
final lastActiveDay = givenDaysWithDayNumber
|
final lastActiveDay = givenDaysWithDayNumber
|
||||||
.where((e) => e.$2.state == DiaryStateEnum.finished || e.$2.state == DiaryStateEnum.lastDayFinished)
|
.where(
|
||||||
|
(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: {'diaryId': lastActiveDay.$2.id, 'currentDiaryDay': lastActiveDay.$1});
|
GoRouter.of(context).push(UserDiaryScreen.path, extra: {
|
||||||
|
'diaryId': lastActiveDay.$2.id,
|
||||||
|
'currentDiaryDay': lastActiveDay.$1,
|
||||||
|
});
|
||||||
|
GoRouter.of(context).push(UserDiaryScreen.path, extra: lastActiveDay.$2.id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
showErrorDialog(
|
showErrorDialog(context,
|
||||||
context,
|
title: s.you_can_not_open_this_day, description: '${widget.consultationsModel.userName} ${s.has_not_filled_it_yet}');
|
||||||
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(borderRadius: BorderRadius.circular(20), color: appTheme.blueA400),
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
color: appTheme.blueA400,
|
||||||
|
),
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
'Комментировать отчёт',
|
'Комментировать отчёт',
|
||||||
style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w600),
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -278,8 +334,16 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
|
|||||||
child: Ink(
|
child: Ink(
|
||||||
height: 52,
|
height: 52,
|
||||||
width: 52,
|
width: 52,
|
||||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(20), color: const Color(0xFFF6F6F6)),
|
decoration: BoxDecoration(
|
||||||
child: const Center(child: Icon(Icons.chat_outlined, size: 24)),
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
color: const Color(0xFFF6F6F6),
|
||||||
|
),
|
||||||
|
child: const Center(
|
||||||
|
child: Icon(
|
||||||
|
Icons.chat_outlined,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -297,13 +361,21 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
|
|||||||
Container(
|
Container(
|
||||||
height: 20,
|
height: 20,
|
||||||
width: 20,
|
width: 20,
|
||||||
decoration: BoxDecoration(shape: BoxShape.circle, color: color),
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: color,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
text,
|
text,
|
||||||
style: const TextStyle(height: 1, fontSize: 16, fontWeight: FontWeight.w500, color: Color(0xFF323F4B)),
|
style: const TextStyle(
|
||||||
|
height: 1,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Color(0xFF323F4B),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ class ConsultationService {
|
|||||||
else if (state != null && stateComparison != null)
|
else if (state != null && stateComparison != null)
|
||||||
'filters[last_state][${stateComparison.value}]': state,
|
'filters[last_state][${stateComparison.value}]': state,
|
||||||
if (dateComparison != null && date != null) 'filters[start_date][${dateComparison.value}]': date,
|
if (dateComparison != null && date != null) 'filters[start_date][${dateComparison.value}]': date,
|
||||||
'sort[]': 'created_at:desc',
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return ConsultationPaginationModel.fromJson(response.data);
|
return ConsultationPaginationModel.fromJson(response.data);
|
||||||
@@ -78,8 +77,7 @@ class ConsultationService {
|
|||||||
final response = await dio.get(
|
final response = await dio.get(
|
||||||
'/api/finances/my_finances',
|
'/api/finances/my_finances',
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
if (date != null && dateComparison != null) 'filters[start_date][${dateComparison.value}]': date,
|
if (dateComparison != null && date != null) 'filters[end_date]': date,
|
||||||
'sort[]': 'start_date:desc',
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
final data = response.data as Map<String, dynamic>;
|
final data = response.data as Map<String, dynamic>;
|
||||||
@@ -98,8 +96,7 @@ class ConsultationService {
|
|||||||
final response = await dio.get(
|
final response = await dio.get(
|
||||||
'/api/finances/my_purchases',
|
'/api/finances/my_purchases',
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
if (date != null && dateComparison != null) 'filters[start_date][${dateComparison.value}]': date,
|
if (dateComparison != null && date != null) 'filters[start_date]': date,
|
||||||
'sort[]': 'start_date:desc',
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
final data = response.data as Map<String, dynamic>;
|
final data = response.data as Map<String, dynamic>;
|
||||||
|
|||||||
@@ -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,7 +9,6 @@ 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';
|
||||||
|
|
||||||
@@ -98,15 +97,10 @@ 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(padding: const EdgeInsets.all(8.0), child: Row(children: [Text('BaseUrl: $baseUrlForTest')])),
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Row(children: [Text('BaseUrl: $baseUrlForTest')]),
|
|
||||||
),
|
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(child: CustomTextFormField(controller: controller, hintText: 'type new base url')),
|
||||||
child: CustomTextFormField(controller: controller, hintText: 'type new base url'),
|
|
||||||
),
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -122,13 +116,13 @@ class _DevToolsScreenContentState extends State<DevToolsScreenContent> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
CustomOutlinedButton(
|
CustomOutlinedButton(
|
||||||
text: baseUrl == EnvConfig.devBaseUrl ? 'Set production base url' : 'Set dev2 base url',
|
text: baseUrl == 'https://dev2.eatvio.com' ? 'Set production base url' : 'Set dev2 base url',
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
if (baseUrl == EnvConfig.baseUrl) {
|
if (baseUrl == 'https://eatvio.ru') {
|
||||||
baseUrlForTest = EnvConfig.devBaseUrl;
|
baseUrlForTest = 'https://dev2.eatvio.com';
|
||||||
} else {
|
} else {
|
||||||
baseUrlForTest = EnvConfig.baseUrl;
|
baseUrlForTest = 'https://eatvio.ru';
|
||||||
}
|
}
|
||||||
baseUrl = baseUrlForTest.toLowerCase();
|
baseUrl = baseUrlForTest.toLowerCase();
|
||||||
sl<Dio>().options.baseUrl = baseUrl;
|
sl<Dio>().options.baseUrl = baseUrl;
|
||||||
@@ -138,7 +132,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() != EnvConfig.baseUrl.toLowerCase())
|
if (baseUrl.toLowerCase() != 'https://eatvio.ru')
|
||||||
ListView.builder(
|
ListView.builder(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: testCases.length,
|
itemCount: testCases.length,
|
||||||
@@ -181,7 +175,12 @@ 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: [TextButton(onPressed: () => Navigator.of(context).pop(), child: const Text('OK'))],
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: const Text('OK'),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -198,7 +197,12 @@ 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: [TextButton(onPressed: () => Navigator.of(context).pop(), child: const Text('OK'))],
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: const Text('OK'),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -212,10 +216,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,7 +35,6 @@ 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;
|
||||||
@@ -478,9 +477,7 @@ 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(
|
await authService.getTokenByCredentials(credentials: const CredentialsModel(email: 'reshetnick.mike+user1@gmail.com', password: '1234567890'));
|
||||||
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');
|
||||||
|
|||||||
784
lib/features/diary/models/consultant_review_model.freezed.dart
Normal file
784
lib/features/diary/models/consultant_review_model.freezed.dart
Normal file
@@ -0,0 +1,784 @@
|
|||||||
|
// 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
|
||||||
67
lib/features/diary/models/consultant_review_model.g.dart
Normal file
67
lib/features/diary/models/consultant_review_model.g.dart
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// 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,
|
||||||
|
};
|
||||||
@@ -136,7 +136,15 @@ class DiaryCubit extends Cubit<DiaryState> {
|
|||||||
final fifthMeal = MealModel(id: 0, time: '0', typeNumber: 5, satiety: 0, mood: 0, portions: fifthMealPortions ?? [], photos: []);
|
final fifthMeal = MealModel(id: 0, time: '0', typeNumber: 5, satiety: 0, mood: 0, portions: fifthMealPortions ?? [], photos: []);
|
||||||
emit(
|
emit(
|
||||||
DiaryState.loadedTomorrowWithDiet(
|
DiaryState.loadedTomorrowWithDiet(
|
||||||
diary: diaryDay.copyWith(meals: [firstMeal, secondMeal, thirdMeal, fourthMeal, fifthMeal]),
|
diary: diaryDay.copyWith(
|
||||||
|
meals: [
|
||||||
|
firstMeal,
|
||||||
|
secondMeal,
|
||||||
|
thirdMeal,
|
||||||
|
fourthMeal,
|
||||||
|
fifthMeal,
|
||||||
|
],
|
||||||
|
),
|
||||||
selectedDay: selectedDay,
|
selectedDay: selectedDay,
|
||||||
selectedMeal: selectedMeal,
|
selectedMeal: selectedMeal,
|
||||||
),
|
),
|
||||||
@@ -148,7 +156,7 @@ class DiaryCubit extends Cubit<DiaryState> {
|
|||||||
|
|
||||||
Future<void> fetchDiary([bool needLoading = false, int? diaryId, int? currentDay]) async {
|
Future<void> fetchDiary([bool needLoading = false, int? diaryId, int? currentDay]) async {
|
||||||
this.diaryId ??= diaryId;
|
this.diaryId ??= diaryId;
|
||||||
final isConsultantView = this.diaryId != null;
|
|
||||||
if (needLoading) {
|
if (needLoading) {
|
||||||
emit(const DiaryState.loading());
|
emit(const DiaryState.loading());
|
||||||
selectedMeal = 1;
|
selectedMeal = 1;
|
||||||
@@ -171,22 +179,17 @@ class DiaryCubit extends Cubit<DiaryState> {
|
|||||||
int? currentPlanDay;
|
int? currentPlanDay;
|
||||||
String? planName;
|
String? planName;
|
||||||
if (diaryDay.chosenPlanId != null) {
|
if (diaryDay.chosenPlanId != null) {
|
||||||
final chosenPlanModel = await chatRepository.getChosenPlan(id: diaryDay.chosenPlanId!);
|
|
||||||
if (!isConsultantView &&
|
|
||||||
(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);
|
consultantReview = await diaryRepository.getDiaryComments(planId: diaryDay.chosenPlanId!, diaryId: diaryDay.id);
|
||||||
|
final chosenPlanModel = await chatRepository.getChosenPlan(id: diaryDay.chosenPlanId!);
|
||||||
planDuration = chosenPlanModel.plan.duration;
|
planDuration = chosenPlanModel.plan.duration;
|
||||||
planName = chosenPlanModel.plan.name;
|
planName = chosenPlanModel.plan.name;
|
||||||
currentPlanDay = consultantReview.currentPlanDay;
|
currentPlanDay = currentDay ?? DateTime.now().difference(chosenPlanModel.startDate).inDays + 1;
|
||||||
}
|
}
|
||||||
final nutrition = await getNutrition(diaryDay.id);
|
final nutrition = await getNutrition(diaryDay.id);
|
||||||
|
|
||||||
final isDiaryFinished = diaryDay.typeOfDiary.diaryState == DiaryStateEnum.finished || diaryDay.typeOfDiary.diaryState == DiaryStateEnum.lastDayFinished;
|
final isDiaryFinished = diaryDay.typeOfDiary.diaryState == DiaryStateEnum.finished || diaryDay.typeOfDiary.diaryState == DiaryStateEnum.lastDayFinished;
|
||||||
|
|
||||||
final isDiaryCommented =
|
final isDiaryCommented = diaryDay.typeOfDiary.diaryState == DiaryStateEnum.commented ||
|
||||||
diaryDay.typeOfDiary.diaryState == DiaryStateEnum.commented ||
|
|
||||||
diaryDay.typeOfDiary.diaryState == DiaryStateEnum.lastDayCommented ||
|
diaryDay.typeOfDiary.diaryState == DiaryStateEnum.lastDayCommented ||
|
||||||
diaryDay.typeOfDiary.diaryState == DiaryStateEnum.reviewedByUser;
|
diaryDay.typeOfDiary.diaryState == DiaryStateEnum.reviewedByUser;
|
||||||
emit(
|
emit(
|
||||||
@@ -195,7 +198,7 @@ class DiaryCubit extends Cubit<DiaryState> {
|
|||||||
nutrition: nutrition,
|
nutrition: nutrition,
|
||||||
selectedDay: selectedDay,
|
selectedDay: selectedDay,
|
||||||
selectedMeal: selectedMeal,
|
selectedMeal: selectedMeal,
|
||||||
isConsultantView: isConsultantView,
|
isConsultantView: this.diaryId != null,
|
||||||
consultantReview: consultantReview,
|
consultantReview: consultantReview,
|
||||||
isDiaryFinished: isDiaryFinished,
|
isDiaryFinished: isDiaryFinished,
|
||||||
isDiaryCommented: isDiaryCommented,
|
isDiaryCommented: isDiaryCommented,
|
||||||
@@ -203,7 +206,7 @@ class DiaryCubit extends Cubit<DiaryState> {
|
|||||||
chosenPlanDuration: planDuration,
|
chosenPlanDuration: planDuration,
|
||||||
chosenPlanName: planName,
|
chosenPlanName: planName,
|
||||||
isMoodEnabled: isMoodEnabled,
|
isMoodEnabled: isMoodEnabled,
|
||||||
isWakeUpAndRetireEnabled: isWakeUpAndRetireTimeEnabled,
|
isWakeUpAndRetireEnabled: isWakeUpAndRetireTimeEnabled
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} catch (e, st) {
|
} catch (e, st) {
|
||||||
@@ -212,8 +215,17 @@ class DiaryCubit extends Cubit<DiaryState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> finishDiaryDay({required int diaryId, required String wakeUp, required String retire}) async {
|
Future<void> finishDiaryDay({
|
||||||
await diaryRepository.finishDiaryDay(diaryId: diaryId, wakeUp: wakeUp, retire: retire, comment: comment.isEmpty ? null : comment);
|
required int diaryId,
|
||||||
|
required String wakeUp,
|
||||||
|
required String retire,
|
||||||
|
}) async {
|
||||||
|
await diaryRepository.finishDiaryDay(
|
||||||
|
diaryId: diaryId,
|
||||||
|
wakeUp: wakeUp,
|
||||||
|
retire: retire,
|
||||||
|
comment: comment.isEmpty ? null : comment,
|
||||||
|
);
|
||||||
await fetchDiary();
|
await fetchDiary();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,7 +241,6 @@ 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,7 +29,6 @@ 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({
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class DiaryScreen extends StatelessWidget {
|
class DiaryScreen extends StatelessWidget {
|
||||||
const DiaryScreen({super.key});
|
const DiaryScreen({super.key, this.date});
|
||||||
|
final DateTime? date;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return const DiaryScreenContent();
|
return const DiaryScreenContent();
|
||||||
@@ -52,9 +52,10 @@ class UserDiaryScreen extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class DiaryScreenContent extends StatelessWidget {
|
class DiaryScreenContent extends StatelessWidget {
|
||||||
const DiaryScreenContent({super.key, this.diaryId, this.currentPlanDay});
|
const DiaryScreenContent({super.key, this.diaryId, this.currentPlanDay, this.date});
|
||||||
final int? diaryId;
|
final int? diaryId;
|
||||||
final int? currentPlanDay;
|
final int? currentPlanDay;
|
||||||
|
final DateTime? date;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = S.of(context);
|
final s = S.of(context);
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
import 'package:table_calendar/table_calendar.dart';
|
import 'package:table_calendar/table_calendar.dart';
|
||||||
|
|
||||||
class CalendarWidget extends StatefulWidget {
|
class CalendarWidget extends StatefulWidget {
|
||||||
const CalendarWidget({super.key});
|
const CalendarWidget({super.key,});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<CalendarWidget> createState() => _CalendarWidgetState();
|
State<CalendarWidget> createState() => _CalendarWidgetState();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
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,6 +1,5 @@
|
|||||||
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';
|
||||||
@@ -26,11 +25,6 @@ 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,12 +297,4 @@ class DiaryRepository {
|
|||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> viewCommentOfDiaryDayByConsultant({required int diaryId}) async {
|
|
||||||
try {
|
|
||||||
await diaryService.viewCommentOfDiaryDayByConsultant(diaryId: diaryId);
|
|
||||||
} catch (e) {
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,9 +150,4 @@ 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,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart'
|
|||||||
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
|
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
|
||||||
import 'package:eatvio_app/features/diets/presentation/widgets/start_diet_bottom_sheet.dart';
|
import 'package:eatvio_app/features/diets/presentation/widgets/start_diet_bottom_sheet.dart';
|
||||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
||||||
import 'package:eatvio_app/core/comments/comments_cubit/comments_cubit.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart';
|
||||||
import 'package:eatvio_app/core/comments/comments_cubit/comments_state.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_state.dart';
|
||||||
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
|
||||||
import 'package:eatvio_app/features/feed/presentation/widgets/comments_widget.dart';
|
import 'package:eatvio_app/features/feed/presentation/widgets/comments_widget.dart';
|
||||||
import 'package:eatvio_app/generated/l10n.dart';
|
import 'package:eatvio_app/generated/l10n.dart';
|
||||||
|
|||||||
@@ -10,11 +10,7 @@ class ExpandableText extends StatefulWidget {
|
|||||||
final String text;
|
final String text;
|
||||||
final int maxLines;
|
final int maxLines;
|
||||||
|
|
||||||
const ExpandableText({
|
const ExpandableText({super.key, required this.text, this.maxLines = 3});
|
||||||
super.key,
|
|
||||||
required this.text,
|
|
||||||
this.maxLines = 5, // по умолчанию 5 строк
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ExpandableText> createState() => _ExpandableTextState();
|
State<ExpandableText> createState() => _ExpandableTextState();
|
||||||
@@ -22,29 +18,6 @@ class ExpandableText extends StatefulWidget {
|
|||||||
|
|
||||||
class _ExpandableTextState extends State<ExpandableText> {
|
class _ExpandableTextState extends State<ExpandableText> {
|
||||||
bool isExpanded = false;
|
bool isExpanded = false;
|
||||||
bool showExpandButton = false;
|
|
||||||
|
|
||||||
final GlobalKey _textKey = GlobalKey();
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didChangeDependencies() {
|
|
||||||
super.didChangeDependencies();
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) => _checkTextOverflow());
|
|
||||||
}
|
|
||||||
|
|
||||||
void _checkTextOverflow() {
|
|
||||||
final textRenderObject = _textKey.currentContext?.findRenderObject();
|
|
||||||
if (textRenderObject is RenderBox) {
|
|
||||||
final maxLinesHeight = widget.maxLines * TextStyles.textMMediumAuto.fontSize! * 1.2;
|
|
||||||
final actualHeight = textRenderObject.size.height;
|
|
||||||
|
|
||||||
if (actualHeight > maxLinesHeight && !showExpandButton) {
|
|
||||||
setState(() {
|
|
||||||
showExpandButton = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void toggleExpansion() {
|
void toggleExpansion() {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -60,63 +33,52 @@ class _ExpandableTextState extends State<ExpandableText> {
|
|||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: AnimatedSize(
|
child: Stack(
|
||||||
duration: const Duration(milliseconds: 200),
|
children: [
|
||||||
curve: Curves.easeInOut,
|
Text(
|
||||||
child: Stack(
|
widget.text,
|
||||||
children: [
|
style: TextStyles.textMMediumAuto.copyWith(color: appTheme.black),
|
||||||
Text(
|
// style: {
|
||||||
widget.text,
|
// "*": Style(
|
||||||
key: _textKey,
|
// margin: Margins.all(0),
|
||||||
style: TextStyles.textMMediumAuto.copyWith(color: appTheme.black),
|
// color: Colors.black87,
|
||||||
maxLines: isExpanded ? null : widget.maxLines,
|
// maxLines: isExpanded ? null : widget.maxLines,
|
||||||
overflow: TextOverflow.clip,
|
// textOverflow: TextOverflow.clip,
|
||||||
),
|
// ),
|
||||||
if (!isExpanded && showExpandButton)
|
// },
|
||||||
Positioned.fill(
|
),
|
||||||
child: Align(
|
if (!isExpanded)
|
||||||
alignment: Alignment.bottomCenter,
|
Positioned.fill(
|
||||||
child: Container(
|
child: Align(
|
||||||
height: 35,
|
alignment: Alignment.bottomCenter,
|
||||||
decoration: BoxDecoration(
|
child: Container(
|
||||||
gradient: LinearGradient(
|
height: 35,
|
||||||
begin: Alignment.topCenter,
|
decoration: BoxDecoration(
|
||||||
end: Alignment.bottomCenter,
|
gradient: LinearGradient(
|
||||||
colors: [
|
begin: Alignment.topCenter,
|
||||||
Colors.white.withAlpha(0),
|
end: Alignment.bottomCenter,
|
||||||
Colors.white.withAlpha((255 * 0.6).toInt()),
|
colors: [Colors.white.withValues(alpha: 0.0), Colors.white.withValues(alpha: 0.6), Colors.white],
|
||||||
Colors.white,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: toggleExpansion,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(isExpanded ? s.hide : s.expand, style: TextStyles.textMMedium.copyWith(color: appTheme.blueA400)),
|
||||||
|
Transform.rotate(angle: isExpanded ? math.pi : 0, child: SvgPicture.asset('assets/icons/arrow_down.svg', width: 12, height: 12)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (showExpandButton)
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: toggleExpansion,
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
isExpanded ? s.hide : s.expand,
|
|
||||||
style: TextStyles.textMMedium.copyWith(color: appTheme.blueA400),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
Transform.rotate(
|
|
||||||
angle: isExpanded ? math.pi : 0,
|
|
||||||
child: SvgPicture.asset('assets/icons/arrow_down.svg', width: 12, height: 12),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,20 @@
|
|||||||
import 'dart:developer';
|
import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_state.dart';
|
||||||
|
|
||||||
import 'package:eatvio_app/core/comments/comments_cubit/comments_state.dart';
|
|
||||||
import 'package:eatvio_app/core/comments/repositories/comments_repository.dart';
|
|
||||||
import 'package:eatvio_app/features/feed/presentation/widgets/comments_widget.dart';
|
import 'package:eatvio_app/features/feed/presentation/widgets/comments_widget.dart';
|
||||||
import 'package:eatvio_app/features/feed/repositories/feed_repository.dart';
|
import 'package:eatvio_app/features/feed/repositories/feed_repository.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class CommentsCubit extends Cubit<CommentsState> {
|
class CommentsCubit extends Cubit<CommentsState> {
|
||||||
final FeedRepository feedRepository;
|
final FeedRepository feedRepository;
|
||||||
final CommentsRepository commentsRepository;
|
|
||||||
|
|
||||||
CommentsCubit({
|
CommentsCubit({
|
||||||
required this.feedRepository,
|
required this.feedRepository,
|
||||||
required this.commentsRepository,
|
|
||||||
}) : super(const CommentsState());
|
}) : super(const CommentsState());
|
||||||
|
|
||||||
void setReplyId(int? replyId) {
|
void setReplyId(int? replyId) {
|
||||||
emit((state as CommentsStateLoaded).copyWith(replyId: replyId));
|
emit((state as CommentsStateLoaded).copyWith(replyId: replyId));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> getComments(String id, CommentSourceType commentSourceType, [bool isFirstFetch = false]) async {
|
Future<void> getComments(String id, CommentSourceType commentSourceType, [bool isFirstFetch = false]) async {
|
||||||
if (isFirstFetch) emit(const CommentsState.loading());
|
if (isFirstFetch) emit(const CommentsState.loading());
|
||||||
try {
|
try {
|
||||||
switch (commentSourceType) {
|
switch (commentSourceType) {
|
||||||
@@ -32,7 +27,7 @@ class CommentsCubit extends Cubit<CommentsState> {
|
|||||||
case CommentSourceType.recipe:
|
case CommentSourceType.recipe:
|
||||||
emit(CommentsState.loaded(comment: await feedRepository.getRecipesComments(id: id)));
|
emit(CommentsState.loaded(comment: await feedRepository.getRecipesComments(id: id)));
|
||||||
break;
|
break;
|
||||||
case CommentSourceType.plan:
|
case CommentSourceType.plan:
|
||||||
emit(CommentsState.loaded(comment: await feedRepository.getPlansComments(slug: id)));
|
emit(CommentsState.loaded(comment: await feedRepository.getPlansComments(slug: id)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -83,34 +78,4 @@ class CommentsCubit extends Cubit<CommentsState> {
|
|||||||
emit(CommentsState.error(errorMessage: e.toString()));
|
emit(CommentsState.error(errorMessage: e.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> deleteComment({required int id}) async {
|
|
||||||
if (state is! CommentsStateLoaded) return false;
|
|
||||||
final commentModel = (state as CommentsStateLoaded).comment;
|
|
||||||
final updatedCommentModel = commentModel.copyWith(data: commentModel.data.where((comment) => comment.id != id).toList());
|
|
||||||
try {
|
|
||||||
await commentsRepository.deleteComment(id: id);
|
|
||||||
emit(CommentsState.loaded(comment: updatedCommentModel));
|
|
||||||
return true;
|
|
||||||
} catch (e, st) {
|
|
||||||
emit(CommentsState.loaded(comment: commentModel));
|
|
||||||
log(e.toString(), stackTrace: st);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> updateComment({required int id, required String body}) async {
|
|
||||||
final commentModel = (state as CommentsStateLoaded).comment;
|
|
||||||
final updatedCommentModel =
|
|
||||||
commentModel.copyWith(data: commentModel.data.map((comment) => comment.id == id ? comment.copyWith(body: body) : comment).toList());
|
|
||||||
try {
|
|
||||||
await commentsRepository.updateComment(id: id, body: body);
|
|
||||||
emit(CommentsState.loaded(comment: updatedCommentModel));
|
|
||||||
return true;
|
|
||||||
} catch (e, st) {
|
|
||||||
emit(CommentsState.loaded(comment: commentModel));
|
|
||||||
log(e.toString(), stackTrace: st);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,6 @@ abstract class CommentsState with _$CommentsState {
|
|||||||
const factory CommentsState.loaded({
|
const factory CommentsState.loaded({
|
||||||
required CommentModel comment,
|
required CommentModel comment,
|
||||||
int? replyId,
|
int? replyId,
|
||||||
int? editedCommentId,
|
|
||||||
}) = CommentsStateLoaded;
|
}) = CommentsStateLoaded;
|
||||||
|
|
||||||
const factory CommentsState.error({
|
const factory CommentsState.error({
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
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';
|
||||||
@@ -33,12 +32,24 @@ class FeedScreen extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MultiBlocProvider(
|
return MultiBlocProvider(
|
||||||
providers: [
|
providers: [
|
||||||
BlocProvider<RecipesCubit>(create: (context) => sl<RecipesCubit>()..loadRecipesList()),
|
BlocProvider<RecipesCubit>(
|
||||||
BlocProvider<FeedProfileCubit>(create: (context) => sl<FeedProfileCubit>()..getProfile()),
|
create: (context) => sl<RecipesCubit>()..loadRecipesList(),
|
||||||
BlocProvider<FeedCubit>(create: (context) => sl<FeedCubit>()..loadFeedBlogPostList()),
|
),
|
||||||
BlocProvider<DietsCubit>(create: (context) => sl<DietsCubit>()..loadDiets()),
|
BlocProvider<FeedProfileCubit>(
|
||||||
if (!hidePlans) BlocProvider<PlansCubit>(create: (context) => sl<PlansCubit>()..loadPlans()),
|
create: (context) => sl<FeedProfileCubit>()..getProfile(),
|
||||||
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(),
|
||||||
);
|
);
|
||||||
@@ -58,8 +69,7 @@ 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();
|
||||||
@@ -68,13 +78,12 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
|
|||||||
final consultantsScrollController = ScrollController();
|
final consultantsScrollController = ScrollController();
|
||||||
|
|
||||||
@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) {}
|
||||||
@@ -99,36 +108,45 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
|
|||||||
heroTag: 'recipesTagsFilter',
|
heroTag: 'recipesTagsFilter',
|
||||||
backgroundColor: appTheme.blueA400,
|
backgroundColor: appTheme.blueA400,
|
||||||
shape: const CircleBorder(),
|
shape: const CircleBorder(),
|
||||||
child: const Icon(Icons.filter_list_rounded, color: Colors.white),
|
child: const Icon(
|
||||||
|
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(recipesCubit: recipesCubit),
|
builder: (context) => TagsModalWidget(
|
||||||
|
recipesCubit: recipesCubit,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
recipesCubit.loadRecipesList();
|
recipesCubit.loadRecipesList();
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
appBar: AppBar(backgroundColor: Colors.white, toolbarHeight: 0),
|
appBar: AppBar(
|
||||||
|
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 when !hidePlans => plansScrollController,
|
3 => plansScrollController,
|
||||||
3 when hidePlans => consultantsScrollController,
|
4 => consultantsScrollController,
|
||||||
4 when !hidePlans => consultantsScrollController,
|
_ => blogPostsScrollController
|
||||||
_ => 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(borderRadius: BorderRadius.vertical(bottom: Radius.circular(12))),
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.vertical(bottom: Radius.circular(12)),
|
||||||
|
),
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
@@ -154,71 +172,78 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
|
|||||||
searchController: switch (_selectedChipIndex) {
|
searchController: switch (_selectedChipIndex) {
|
||||||
1 => recipesController,
|
1 => recipesController,
|
||||||
2 => dietsController,
|
2 => dietsController,
|
||||||
3 when !hidePlans => plansController,
|
3 => plansController,
|
||||||
3 when hidePlans => consultantsController,
|
4 => consultantsController,
|
||||||
4 when !hidePlans => consultantsController,
|
|
||||||
_ => postsController,
|
_ => postsController,
|
||||||
},
|
},
|
||||||
onChanged: (query) {
|
onChanged: (query) {
|
||||||
if (query.length > 2) {
|
if (query.length > 2) {
|
||||||
EasyDebounce.debounce('searchFeed', const Duration(milliseconds: 500), () {
|
EasyDebounce.debounce(
|
||||||
switch (_selectedChipIndex) {
|
'searchFeed',
|
||||||
case 0:
|
const Duration(milliseconds: 500),
|
||||||
context.read<FeedCubit>().loadFeedBlogPostList(query: query);
|
() {
|
||||||
break;
|
switch (_selectedChipIndex) {
|
||||||
case 1:
|
case 0:
|
||||||
context.read<RecipesCubit>().loadRecipesList(query: query);
|
context.read<FeedCubit>().loadFeedBlogPostList(query: query);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 1:
|
||||||
context.read<DietsCubit>().loadDiets(query: query);
|
context.read<RecipesCubit>().loadRecipesList(query: query);
|
||||||
break;
|
break;
|
||||||
case 3 when !hidePlans:
|
case 2:
|
||||||
context.read<PlansCubit>().loadPlans(query: query);
|
context.read<DietsCubit>().loadDiets(query: query);
|
||||||
break;
|
break;
|
||||||
case 3 when hidePlans:
|
case 3:
|
||||||
case 4 when !hidePlans:
|
context.read<PlansCubit>().loadPlans(query: query);
|
||||||
context.read<ConsultantsCubit>().getConsultants();
|
break;
|
||||||
break;
|
case 4:
|
||||||
}
|
context.read<ConsultantsCubit>().getConsultants();
|
||||||
});
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
EasyDebounce.debounce('searchFeed', const Duration(milliseconds: 500), () {
|
EasyDebounce.debounce(
|
||||||
switch (_selectedChipIndex) {
|
'searchFeed',
|
||||||
case 0:
|
const Duration(milliseconds: 500),
|
||||||
context.read<FeedCubit>().loadFeedBlogPostList();
|
() {
|
||||||
break;
|
switch (_selectedChipIndex) {
|
||||||
case 1:
|
case 0:
|
||||||
context.read<RecipesCubit>().loadRecipesList();
|
context.read<FeedCubit>().loadFeedBlogPostList();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 1:
|
||||||
context.read<DietsCubit>().loadDiets();
|
context.read<RecipesCubit>().loadRecipesList();
|
||||||
break;
|
break;
|
||||||
case 3 when !hidePlans:
|
case 2:
|
||||||
context.read<PlansCubit>().loadPlans();
|
context.read<DietsCubit>().loadDiets();
|
||||||
break;
|
break;
|
||||||
case 3 when hidePlans:
|
case 3:
|
||||||
case 4 when !hidePlans:
|
context.read<PlansCubit>().loadPlans();
|
||||||
context.read<ConsultantsCubit>().getConsultants();
|
break;
|
||||||
break;
|
case 4:
|
||||||
}
|
context.read<ConsultantsCubit>().getConsultants();
|
||||||
});
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
switch (_selectedChipIndex) {
|
switch (_selectedChipIndex) {
|
||||||
1 => AddItemButton(onTap: () => GoRouter.of(context).pushNamed(RecipeCreateScreen.name)),
|
1 => AddItemButton(
|
||||||
|
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 when !hidePlans => AddItemButton(
|
3 => 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()
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -231,9 +256,8 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
|
|||||||
0 => const BlogPosts(),
|
0 => const BlogPosts(),
|
||||||
1 => const Recipes(),
|
1 => const Recipes(),
|
||||||
2 => const Diets(),
|
2 => const Diets(),
|
||||||
3 when !hidePlans => const Plans(),
|
3 => const Plans(),
|
||||||
3 when hidePlans => const Consultants(),
|
4 => const Consultants(),
|
||||||
4 when !hidePlans => const Consultants(),
|
|
||||||
_ => Center(child: Text(chipsLabelList[_selectedChipIndex])),
|
_ => Center(child: Text(chipsLabelList[_selectedChipIndex])),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,7 +2,12 @@ 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({super.key, required this.selectedChipIndex, required this.chipsLabelList, required this.setSelectedChipIndex});
|
const ChipGroup({
|
||||||
|
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;
|
||||||
@@ -17,7 +22,7 @@ class ChipGroup extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
...List.generate(
|
...List.generate(
|
||||||
chipsLabelList.length,
|
5,
|
||||||
(index) => Padding(
|
(index) => Padding(
|
||||||
padding: const EdgeInsets.only(right: 8.0),
|
padding: const EdgeInsets.only(right: 8.0),
|
||||||
child: ChoiceChipWidget(
|
child: ChoiceChipWidget(
|
||||||
|
|||||||
@@ -1,19 +1,14 @@
|
|||||||
import 'package:eatvio_app/core/comments/presentation/comments_actions_bottom_sheet.dart';
|
|
||||||
import 'package:eatvio_app/core/const.dart';
|
import 'package:eatvio_app/core/const.dart';
|
||||||
import 'package:eatvio_app/core/enums/complained_content.dart';
|
|
||||||
import 'package:eatvio_app/core/locator_service.dart';
|
import 'package:eatvio_app/core/locator_service.dart';
|
||||||
import 'package:eatvio_app/core/utils/date_time_utils.dart';
|
import 'package:eatvio_app/core/utils/date_time_utils.dart';
|
||||||
import 'package:eatvio_app/core/utils/toasts.dart';
|
|
||||||
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart';
|
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart';
|
||||||
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_state.dart';
|
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_state.dart';
|
||||||
import 'package:eatvio_app/features/complaining/presentation/complaining_bottom_sheet.dart';
|
|
||||||
import 'package:eatvio_app/features/feed/models/comment_model.dart';
|
import 'package:eatvio_app/features/feed/models/comment_model.dart';
|
||||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
||||||
import 'package:eatvio_app/core/comments/comments_cubit/comments_cubit.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart';
|
||||||
import 'package:eatvio_app/generated/l10n.dart';
|
import 'package:eatvio_app/generated/l10n.dart';
|
||||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
@@ -58,133 +53,98 @@ class _CommentsWidgetState extends State<CommentsWidget> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _comment(S s, Comment comment, [double radius = 18, bool isChild = false]) {
|
Container _comment(S s, Comment comment, [double radius = 18, bool isChild = false]) {
|
||||||
final isMe = (sl<ProfileAvatarCubit>().state as ProfileAvatarStateLoaded).profile.slug == comment.authorSlug;
|
return Container(
|
||||||
return GestureDetector(
|
width: MediaQuery.of(context).size.width,
|
||||||
onTap: () {
|
decoration: BoxDecoration(
|
||||||
showCommentsActionsBottomSheet(
|
color: widget.replyId == comment.id ? appTheme.blueA400.withValues(alpha: 0.1) : Colors.white,
|
||||||
context,
|
borderRadius: BorderRadius.circular(16),
|
||||||
commentText: comment.body,
|
),
|
||||||
reply: !isChild
|
child: Padding(
|
||||||
? () {
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
context.read<CommentsCubit>().setReplyId(comment.id);
|
child: Row(
|
||||||
}
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
: null,
|
children: [
|
||||||
copy: () {
|
GestureDetector(
|
||||||
Clipboard.setData(ClipboardData(text: comment.body));
|
onTap: () {
|
||||||
showInfoToast(s.link_was_copied);
|
GoRouter.of(context).pushNamed(
|
||||||
},
|
'profileScreen',
|
||||||
edit: (String text) {
|
extra: Author(
|
||||||
context.read<CommentsCubit>().updateComment(id: comment.id, body: text);
|
authorSlug: comment.authorSlug,
|
||||||
},
|
authorName: comment.authorName,
|
||||||
delete: () {
|
authorProfileIcon: comment.authorProfileIcon.contains('https') ? comment.authorProfileIcon : baseUrl + comment.authorProfileIcon,
|
||||||
context.read<CommentsCubit>().deleteComment(id: comment.id);
|
authorIsMe: (sl<ProfileAvatarCubit>().state as ProfileAvatarStateLoaded).profile.slug == comment.authorSlug ? true : false,
|
||||||
},
|
|
||||||
complain: () {
|
|
||||||
showModalBottomSheet(
|
|
||||||
context: context,
|
|
||||||
isScrollControlled: true,
|
|
||||||
builder: (context) => ComplainingBottomSheet(
|
|
||||||
complainedContentType: ComplainedContent.comment,
|
|
||||||
id: comment.id,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
isMyComment: isMe,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
width: MediaQuery.of(context).size.width,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: widget.replyId == comment.id ? appTheme.blueA400.withValues(alpha: 0.1) : Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
GoRouter.of(context).pushNamed(
|
|
||||||
'profileScreen',
|
|
||||||
extra: Author(
|
|
||||||
authorSlug: comment.authorSlug,
|
|
||||||
authorName: comment.authorName,
|
|
||||||
authorProfileIcon: comment.authorProfileIcon.contains('https') ? comment.authorProfileIcon : baseUrl + comment.authorProfileIcon,
|
|
||||||
authorIsMe: isMe,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: CircleAvatar(
|
|
||||||
foregroundImage: NetworkImage(
|
|
||||||
comment.authorProfileIcon.contains('https') ? comment.authorProfileIcon : baseUrl + comment.authorProfileIcon,
|
|
||||||
),
|
),
|
||||||
backgroundImage: const AssetImage('assets/images/no_avatar.png'),
|
);
|
||||||
radius: radius,
|
},
|
||||||
|
child: CircleAvatar(
|
||||||
|
foregroundImage: NetworkImage(
|
||||||
|
comment.authorProfileIcon.contains('https') ? comment.authorProfileIcon : baseUrl + comment.authorProfileIcon,
|
||||||
),
|
),
|
||||||
|
backgroundImage: const AssetImage('assets/images/no_avatar.png'),
|
||||||
|
radius: radius,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
),
|
||||||
Column(
|
const SizedBox(width: 8),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
Text(
|
children: [
|
||||||
comment.authorName,
|
Text(
|
||||||
|
comment.authorName,
|
||||||
|
style: TextStyle(
|
||||||
|
height: 1,
|
||||||
|
color: appTheme.gray,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
SizedBox(
|
||||||
|
width: isChild ? MediaQuery.of(context).size.width * 0.72 : MediaQuery.of(context).size.width * 0.8,
|
||||||
|
child: Text(
|
||||||
|
comment.body,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
height: 1,
|
height: 1,
|
||||||
color: appTheme.gray,
|
color: appTheme.black,
|
||||||
fontSize: 14,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
),
|
||||||
SizedBox(
|
const SizedBox(height: 4),
|
||||||
width: isChild ? MediaQuery.of(context).size.width * 0.72 : MediaQuery.of(context).size.width * 0.8,
|
Row(
|
||||||
child: Text(
|
children: [
|
||||||
comment.body,
|
Text(
|
||||||
|
comment.createdAt.format('dd MMMM', 'ru'),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
height: 1,
|
height: 1,
|
||||||
color: appTheme.black,
|
color: appTheme.gray,
|
||||||
fontSize: 16,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(width: 16),
|
||||||
const SizedBox(height: 4),
|
if (!isChild)
|
||||||
Row(
|
GestureDetector(
|
||||||
children: [
|
onTap: () {
|
||||||
Text(
|
context.read<CommentsCubit>().setReplyId(comment.id);
|
||||||
comment.createdAt.format('dd MMMM', 'ru'),
|
},
|
||||||
style: TextStyle(
|
child: Text(
|
||||||
height: 1,
|
s.answer,
|
||||||
color: appTheme.gray,
|
style: TextStyle(
|
||||||
fontSize: 14,
|
height: 1,
|
||||||
fontWeight: FontWeight.w500,
|
color: appTheme.gray,
|
||||||
),
|
fontSize: 14,
|
||||||
),
|
fontWeight: FontWeight.w600,
|
||||||
const SizedBox(width: 16),
|
|
||||||
if (!isChild)
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
context.read<CommentsCubit>().setReplyId(comment.id);
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
s.answer,
|
|
||||||
style: TextStyle(
|
|
||||||
height: 1,
|
|
||||||
color: appTheme.gray,
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -31,7 +31,9 @@ class ConsultationsArchiveCubit extends Cubit<ConsultationsArchiveState> {
|
|||||||
dateComparison: state.periodFilterStatus == PeriodFilterStatus.all ? null : FiltersComparisonEnum.greaterThanOrEqual,
|
dateComparison: state.periodFilterStatus == PeriodFilterStatus.all ? null : FiltersComparisonEnum.greaterThanOrEqual,
|
||||||
stateComparison: state.consultationFilterStatus == ConsultationFilterStatus.all
|
stateComparison: state.consultationFilterStatus == ConsultationFilterStatus.all
|
||||||
? null
|
? null
|
||||||
: FiltersComparisonEnum.equal,
|
: state.consultationFilterStatus == ConsultationFilterStatus.inProgress
|
||||||
|
? FiltersComparisonEnum.notInArray
|
||||||
|
: FiltersComparisonEnum.equal,
|
||||||
);
|
);
|
||||||
emit(state.copyWith(consultations: consultations, isLoading: false));
|
emit(state.copyWith(consultations: consultations, isLoading: false));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -65,17 +67,20 @@ class ConsultationsArchiveCubit extends Cubit<ConsultationsArchiveState> {
|
|||||||
enum ConsultationFilterStatus {
|
enum ConsultationFilterStatus {
|
||||||
all,
|
all,
|
||||||
canceled,
|
canceled,
|
||||||
finished;
|
finished,
|
||||||
|
inProgress;
|
||||||
|
|
||||||
String get name => switch (this) {
|
String get name => switch (this) {
|
||||||
ConsultationFilterStatus.all => 'Все консультации',
|
ConsultationFilterStatus.all => 'Все консультации',
|
||||||
ConsultationFilterStatus.canceled => 'Отменён',
|
ConsultationFilterStatus.canceled => 'Отменён',
|
||||||
ConsultationFilterStatus.finished => 'Завершён',
|
ConsultationFilterStatus.finished => 'Завершён',
|
||||||
|
ConsultationFilterStatus.inProgress => 'В процессе',
|
||||||
};
|
};
|
||||||
|
|
||||||
String? get value => switch (this) {
|
String? get value => switch (this) {
|
||||||
ConsultationFilterStatus.all => null,
|
ConsultationFilterStatus.all => null,
|
||||||
ConsultationFilterStatus.canceled => 'canceled_by_the_initiator',
|
ConsultationFilterStatus.canceled => 'canceled_by_the_initiator',
|
||||||
ConsultationFilterStatus.finished => 'charge_was_collected',
|
ConsultationFilterStatus.finished => 'charge_was_collected',
|
||||||
|
ConsultationFilterStatus.inProgress => 'active',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ abstract class ConsultationsArchiveState with _$ConsultationsArchiveState {
|
|||||||
@Default([]) List<ConsultationModel> consultations,
|
@Default([]) List<ConsultationModel> consultations,
|
||||||
@Default(false) isLoading,
|
@Default(false) isLoading,
|
||||||
@Default(null) String? error,
|
@Default(null) String? error,
|
||||||
@Default(PeriodFilterStatus.all) PeriodFilterStatus? periodFilterStatus,
|
@Default(null) PeriodFilterStatus? periodFilterStatus,
|
||||||
@Default(null) ConsultationFilterStatus? consultationFilterStatus,
|
@Default(null) ConsultationFilterStatus? consultationFilterStatus,
|
||||||
required StatisticModel statisticModel,
|
required StatisticModel statisticModel,
|
||||||
}) = _ConsultationsArchiveState;
|
}) = _ConsultationsArchiveState;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ abstract class ConsultantFinancesState with _$ConsultantFinancesState {
|
|||||||
@Default([]) List<ConsultantFinanceModel> financesModels,
|
@Default([]) List<ConsultantFinanceModel> financesModels,
|
||||||
@Default(false) isLoading,
|
@Default(false) isLoading,
|
||||||
@Default(null) String? error,
|
@Default(null) String? error,
|
||||||
@Default(PeriodFilterStatus.all) PeriodFilterStatus? periodFilterStatus,
|
@Default(null) PeriodFilterStatus? periodFilterStatus,
|
||||||
}) = _ConsultantFinancesState;
|
}) = _ConsultantFinancesState;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ abstract class UserPurchasesState with _$UserPurchasesState {
|
|||||||
@Default([]) List<UserPurchasesModel> purchasesModels,
|
@Default([]) List<UserPurchasesModel> purchasesModels,
|
||||||
@Default(false) isLoading,
|
@Default(false) isLoading,
|
||||||
@Default(null) String? error,
|
@Default(null) String? error,
|
||||||
@Default(PeriodFilterStatus.all) PeriodFilterStatus? periodFilterStatus,
|
@Default(null) PeriodFilterStatus? periodFilterStatus,
|
||||||
}) = _UserPurchasesState;
|
}) = _UserPurchasesState;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ class _ConsultantFinancesScreenState extends State<ConsultantFinancesScreen> {
|
|||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
context.read<ConsultantFinancesCubit>().setFinancesPeriod(value);
|
context.read<ConsultantFinancesCubit>().setFinancesPeriod(value);
|
||||||
},
|
},
|
||||||
value: state.periodFilterStatus ?? PeriodFilterStatus.all,
|
value: state.periodFilterStatus ?? PeriodFilterStatus.values.first,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
if (state.financesModels.isNotEmpty)
|
if (state.financesModels.isNotEmpty)
|
||||||
@@ -129,7 +129,7 @@ class _ConsultantFinancesScreenState extends State<ConsultantFinancesScreen> {
|
|||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
Text(
|
Text(
|
||||||
state.financesModels[index].startDate.format('dd.MM.yy'),
|
state.financesModels[index].startDate.format('yyyy.MM.dd'),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Color(0xFF6D6F71),
|
color: Color(0xFF6D6F71),
|
||||||
height: 1,
|
height: 1,
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ class _ConsultationsArchiveScreenState extends State<ConsultationsArchiveScreen>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
state.consultations[index].startDate.format('dd.MM.yy'),
|
state.consultations[index].startDate.format('yyyy.MM.dd'),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Color(0xFF6D6F71),
|
color: Color(0xFF6D6F71),
|
||||||
height: 1,
|
height: 1,
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ 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});
|
||||||
@@ -67,7 +66,12 @@ class _InternetConnectionCheckerScreenState extends State<InternetConnectionChec
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final uris = [Uri.parse(EnvConfig.googleCheckUrl), Uri.parse(EnvConfig.yandexCheckUrl), Uri.parse(EnvConfig.devBaseUrl), Uri.parse(EnvConfig.baseUrl)];
|
final uris = [
|
||||||
|
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));
|
||||||
@@ -94,9 +98,13 @@ class _InternetConnectionCheckerScreenState extends State<InternetConnectionChec
|
|||||||
if (!_hasInternet)
|
if (!_hasInternet)
|
||||||
NoInternetScreen(
|
NoInternetScreen(
|
||||||
onTryAgain: () async {
|
onTryAgain: () async {
|
||||||
EasyDebounce.debounce('internet connection check', const Duration(seconds: 2), () async {
|
EasyDebounce.debounce(
|
||||||
await _checkConnection();
|
'internet connection check',
|
||||||
});
|
const Duration(seconds: 2),
|
||||||
|
() async {
|
||||||
|
await _checkConnection();
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -121,7 +129,9 @@ class NoInternetScreen extends StatelessWidget {
|
|||||||
child: Text(
|
child: Text(
|
||||||
'Отсутствует подключение \nк интернету',
|
'Отсутствует подключение \nк интернету',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
|
style: TextStyles.headingXLSemibold.copyWith(
|
||||||
|
color: appTheme.black,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
@@ -130,7 +140,9 @@ class NoInternetScreen extends StatelessWidget {
|
|||||||
child: Text(
|
child: Text(
|
||||||
'Проверьте соединение с сетью\nи попробуйте ещё раз',
|
'Проверьте соединение с сетью\nи попробуйте ещё раз',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyles.textMMedium.copyWith(color: appTheme.gray),
|
style: TextStyles.textMMedium.copyWith(
|
||||||
|
color: appTheme.gray,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
@@ -141,7 +153,10 @@ 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(backgroundColor: WidgetStatePropertyAll(appTheme.blueA400), side: const WidgetStatePropertyAll(BorderSide.none)),
|
buttonStyle: ButtonStyle(
|
||||||
|
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
||||||
|
side: const WidgetStatePropertyAll(BorderSide.none),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
|
|||||||
@@ -37,20 +37,18 @@ 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)),
|
||||||
if (!hidePlans) ...[
|
_divider(context),
|
||||||
_divider(context),
|
_tile(
|
||||||
_tile(
|
leading: 'assets/icons/finance.svg',
|
||||||
leading: 'assets/icons/finance.svg',
|
title: 'Мои финансы',
|
||||||
title: 'Мои финансы',
|
onTap: () {
|
||||||
onTap: () {
|
if (profileModel.roles.contains('consultant')) {
|
||||||
if (profileModel.roles.contains('consultant')) {
|
GoRouter.of(context).push(ConsultantFinancesScreen.path);
|
||||||
GoRouter.of(context).push(ConsultantFinancesScreen.path);
|
return;
|
||||||
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),
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
|
import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart';
|
||||||
|
|
||||||
sealed class BaseNotificationModel {
|
sealed class BaseNotificationModel {
|
||||||
abstract final NotificationType type;
|
abstract final NotificationType type;
|
||||||
abstract final bool isRead;
|
abstract final bool isRead;
|
||||||
abstract final DateTime createdAt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class PlanSubscriptionStartedToAuthorNotification implements BaseNotificationModel {
|
class PlanSubscriptionStartedToAuthorNotification implements BaseNotificationModel {
|
||||||
@@ -12,8 +13,6 @@ class PlanSubscriptionStartedToAuthorNotification implements BaseNotificationMod
|
|||||||
final String planName;
|
final String planName;
|
||||||
final String customerName;
|
final String customerName;
|
||||||
final String chatId;
|
final String chatId;
|
||||||
@override
|
|
||||||
final DateTime createdAt;
|
|
||||||
|
|
||||||
PlanSubscriptionStartedToAuthorNotification({
|
PlanSubscriptionStartedToAuthorNotification({
|
||||||
required this.type,
|
required this.type,
|
||||||
@@ -21,17 +20,15 @@ class PlanSubscriptionStartedToAuthorNotification implements BaseNotificationMod
|
|||||||
required this.customerName,
|
required this.customerName,
|
||||||
required this.chatId,
|
required this.chatId,
|
||||||
required this.isRead,
|
required this.isRead,
|
||||||
required this.createdAt,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
factory PlanSubscriptionStartedToAuthorNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
factory PlanSubscriptionStartedToAuthorNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead}) {
|
||||||
return PlanSubscriptionStartedToAuthorNotification(
|
return PlanSubscriptionStartedToAuthorNotification(
|
||||||
type: type,
|
type: type,
|
||||||
planName: json['plan_name'],
|
planName: json['plan_name'],
|
||||||
customerName: json['user_name'],
|
customerName: json['user_name'],
|
||||||
chatId: json['chat_id'],
|
chatId: json['chat_id'],
|
||||||
isRead: isRead ?? false,
|
isRead: isRead ?? false,
|
||||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,24 +40,20 @@ class PlanSubscriptionCanceledToAuthorNotification implements BaseNotificationMo
|
|||||||
final bool isRead;
|
final bool isRead;
|
||||||
final String planName;
|
final String planName;
|
||||||
final String customerName;
|
final String customerName;
|
||||||
@override
|
|
||||||
final DateTime createdAt;
|
|
||||||
|
|
||||||
PlanSubscriptionCanceledToAuthorNotification({
|
PlanSubscriptionCanceledToAuthorNotification({
|
||||||
required this.type,
|
required this.type,
|
||||||
required this.planName,
|
required this.planName,
|
||||||
required this.customerName,
|
required this.customerName,
|
||||||
required this.isRead,
|
required this.isRead,
|
||||||
required this.createdAt,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
factory PlanSubscriptionCanceledToAuthorNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
factory PlanSubscriptionCanceledToAuthorNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead}) {
|
||||||
return PlanSubscriptionCanceledToAuthorNotification(
|
return PlanSubscriptionCanceledToAuthorNotification(
|
||||||
type: type,
|
type: type,
|
||||||
planName: json['plan_name'],
|
planName: json['plan_name'],
|
||||||
customerName: json['user_name'],
|
customerName: json['user_name'],
|
||||||
isRead: isRead ?? false,
|
isRead: isRead ?? false,
|
||||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,18 +65,20 @@ class ChosenPlanStartedToClientNotification implements BaseNotificationModel {
|
|||||||
final bool isRead;
|
final bool isRead;
|
||||||
final String planName;
|
final String planName;
|
||||||
final int planId;
|
final int planId;
|
||||||
@override
|
|
||||||
final DateTime createdAt;
|
|
||||||
|
|
||||||
ChosenPlanStartedToClientNotification({required this.type, required this.planName, required this.planId, required this.isRead, required this.createdAt});
|
ChosenPlanStartedToClientNotification({
|
||||||
|
required this.type,
|
||||||
|
required this.planName,
|
||||||
|
required this.planId,
|
||||||
|
required this.isRead,
|
||||||
|
});
|
||||||
|
|
||||||
factory ChosenPlanStartedToClientNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
factory ChosenPlanStartedToClientNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead}) {
|
||||||
return ChosenPlanStartedToClientNotification(
|
return ChosenPlanStartedToClientNotification(
|
||||||
type: type,
|
type: type,
|
||||||
planName: json['plan_name'],
|
planName: json['plan_name'],
|
||||||
planId: json['plan_id'],
|
planId: json['plan_id'],
|
||||||
isRead: isRead ?? false,
|
isRead: isRead ?? false,
|
||||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,18 +89,15 @@ class ChosenPlanTomorrowReminderToClientNotification implements BaseNotification
|
|||||||
@override
|
@override
|
||||||
final bool isRead;
|
final bool isRead;
|
||||||
final String planName;
|
final String planName;
|
||||||
@override
|
|
||||||
final DateTime createdAt;
|
|
||||||
|
|
||||||
ChosenPlanTomorrowReminderToClientNotification({required this.type, required this.planName, required this.isRead, required this.createdAt});
|
ChosenPlanTomorrowReminderToClientNotification({
|
||||||
|
required this.type,
|
||||||
|
required this.planName,
|
||||||
|
required this.isRead,
|
||||||
|
});
|
||||||
|
|
||||||
factory ChosenPlanTomorrowReminderToClientNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
factory ChosenPlanTomorrowReminderToClientNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead}) {
|
||||||
return ChosenPlanTomorrowReminderToClientNotification(
|
return ChosenPlanTomorrowReminderToClientNotification(type: type, planName: json['plan_name'], isRead: isRead ?? false);
|
||||||
type: type,
|
|
||||||
planName: json['plan_name'],
|
|
||||||
isRead: isRead ?? false,
|
|
||||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,17 +106,16 @@ class DiaryReminderNotification implements BaseNotificationModel {
|
|||||||
final NotificationType type;
|
final NotificationType type;
|
||||||
@override
|
@override
|
||||||
final bool isRead;
|
final bool isRead;
|
||||||
@override
|
final DateTime date;
|
||||||
final DateTime createdAt;
|
|
||||||
|
|
||||||
DiaryReminderNotification({required this.type, required this.isRead, required this.createdAt});
|
DiaryReminderNotification({
|
||||||
|
required this.type,
|
||||||
|
required this.isRead,
|
||||||
|
required this.date,
|
||||||
|
});
|
||||||
|
|
||||||
factory DiaryReminderNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
factory DiaryReminderNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead}) {
|
||||||
return DiaryReminderNotification(
|
return DiaryReminderNotification(type: type, isRead: isRead ?? false, date: parseDate(json['date']));
|
||||||
type: type,
|
|
||||||
isRead: isRead ?? false,
|
|
||||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,17 +125,18 @@ class ConsultantLeftCommentNotification implements BaseNotificationModel {
|
|||||||
@override
|
@override
|
||||||
final bool isRead;
|
final bool isRead;
|
||||||
final DateTime date;
|
final DateTime date;
|
||||||
@override
|
|
||||||
final DateTime createdAt;
|
|
||||||
|
|
||||||
ConsultantLeftCommentNotification({required this.type, required this.date, required this.isRead, required this.createdAt});
|
ConsultantLeftCommentNotification({
|
||||||
|
required this.type,
|
||||||
|
required this.date,
|
||||||
|
required this.isRead,
|
||||||
|
});
|
||||||
|
|
||||||
factory ConsultantLeftCommentNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
factory ConsultantLeftCommentNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead}) {
|
||||||
return ConsultantLeftCommentNotification(
|
return ConsultantLeftCommentNotification(
|
||||||
type: type,
|
type: type,
|
||||||
date: parseDate(json['diary_date'] ?? DateTime.now().toIso8601String()),
|
date: parseDate(json['diary_date']),
|
||||||
isRead: isRead ?? false,
|
isRead: isRead ?? false,
|
||||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,16 +146,16 @@ class PersonalizedDietIsDefinedNotification implements BaseNotificationModel {
|
|||||||
final NotificationType type;
|
final NotificationType type;
|
||||||
@override
|
@override
|
||||||
final bool isRead;
|
final bool isRead;
|
||||||
@override
|
|
||||||
final DateTime createdAt;
|
|
||||||
|
|
||||||
PersonalizedDietIsDefinedNotification({required this.type, required this.isRead, required this.createdAt});
|
PersonalizedDietIsDefinedNotification({
|
||||||
|
required this.type,
|
||||||
|
required this.isRead,
|
||||||
|
});
|
||||||
|
|
||||||
factory PersonalizedDietIsDefinedNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
factory PersonalizedDietIsDefinedNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead}) {
|
||||||
return PersonalizedDietIsDefinedNotification(
|
return PersonalizedDietIsDefinedNotification(
|
||||||
type: type,
|
type: type,
|
||||||
isRead: isRead ?? false,
|
isRead: isRead ?? false,
|
||||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,16 +166,17 @@ class ChosenPlanFinishedToClientNotification implements BaseNotificationModel {
|
|||||||
@override
|
@override
|
||||||
final bool isRead;
|
final bool isRead;
|
||||||
final String planName;
|
final String planName;
|
||||||
@override
|
ChosenPlanFinishedToClientNotification({
|
||||||
final DateTime createdAt;
|
required this.type,
|
||||||
ChosenPlanFinishedToClientNotification({required this.type, required this.planName, required this.isRead, required this.createdAt});
|
required this.planName,
|
||||||
|
required this.isRead,
|
||||||
|
});
|
||||||
|
|
||||||
factory ChosenPlanFinishedToClientNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
factory ChosenPlanFinishedToClientNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead}) {
|
||||||
return ChosenPlanFinishedToClientNotification(
|
return ChosenPlanFinishedToClientNotification(
|
||||||
type: type,
|
type: type,
|
||||||
planName: json['plan_name'],
|
planName: json['plan_name'],
|
||||||
isRead: isRead ?? false,
|
isRead: isRead ?? false,
|
||||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,16 +187,17 @@ class PlanSubscriptionCanceledToCustomerNotification implements BaseNotification
|
|||||||
@override
|
@override
|
||||||
final bool isRead;
|
final bool isRead;
|
||||||
final String planName;
|
final String planName;
|
||||||
@override
|
PlanSubscriptionCanceledToCustomerNotification({
|
||||||
final DateTime createdAt;
|
required this.type,
|
||||||
PlanSubscriptionCanceledToCustomerNotification({required this.type, required this.planName, required this.isRead, required this.createdAt});
|
required this.planName,
|
||||||
|
required this.isRead,
|
||||||
|
});
|
||||||
|
|
||||||
factory PlanSubscriptionCanceledToCustomerNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
factory PlanSubscriptionCanceledToCustomerNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead}) {
|
||||||
return PlanSubscriptionCanceledToCustomerNotification(
|
return PlanSubscriptionCanceledToCustomerNotification(
|
||||||
type: type,
|
type: type,
|
||||||
planName: json['plan_name'],
|
planName: json['plan_name'],
|
||||||
isRead: isRead ?? false,
|
isRead: isRead ?? false,
|
||||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -215,23 +209,19 @@ class ChosenPlanStartedToConsultantNotification implements BaseNotificationModel
|
|||||||
final bool isRead;
|
final bool isRead;
|
||||||
final String planName;
|
final String planName;
|
||||||
final String customerName;
|
final String customerName;
|
||||||
@override
|
|
||||||
final DateTime createdAt;
|
|
||||||
ChosenPlanStartedToConsultantNotification({
|
ChosenPlanStartedToConsultantNotification({
|
||||||
required this.type,
|
required this.type,
|
||||||
required this.isRead,
|
required this.isRead,
|
||||||
required this.planName,
|
required this.planName,
|
||||||
required this.customerName,
|
required this.customerName,
|
||||||
required this.createdAt,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
factory ChosenPlanStartedToConsultantNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
factory ChosenPlanStartedToConsultantNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead}) {
|
||||||
return ChosenPlanStartedToConsultantNotification(
|
return ChosenPlanStartedToConsultantNotification(
|
||||||
type: type,
|
type: type,
|
||||||
isRead: isRead ?? false,
|
isRead: isRead ?? false,
|
||||||
planName: json['plan_name'],
|
planName: json['plan_name'],
|
||||||
customerName: json['client_name'],
|
customerName: json['client_name'],
|
||||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -243,23 +233,19 @@ class ChosenPlanTomorrowReminderToConsultantNotification implements BaseNotifica
|
|||||||
final bool isRead;
|
final bool isRead;
|
||||||
final String planName;
|
final String planName;
|
||||||
final String customerName;
|
final String customerName;
|
||||||
@override
|
|
||||||
final DateTime createdAt;
|
|
||||||
ChosenPlanTomorrowReminderToConsultantNotification({
|
ChosenPlanTomorrowReminderToConsultantNotification({
|
||||||
required this.type,
|
required this.type,
|
||||||
required this.planName,
|
required this.planName,
|
||||||
required this.customerName,
|
required this.customerName,
|
||||||
required this.isRead,
|
required this.isRead,
|
||||||
required this.createdAt,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
factory ChosenPlanTomorrowReminderToConsultantNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
factory ChosenPlanTomorrowReminderToConsultantNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead}) {
|
||||||
return ChosenPlanTomorrowReminderToConsultantNotification(
|
return ChosenPlanTomorrowReminderToConsultantNotification(
|
||||||
type: type,
|
type: type,
|
||||||
planName: json['plan_name'],
|
planName: json['plan_name'],
|
||||||
customerName: json['client_name'],
|
customerName: json['client_name'],
|
||||||
isRead: isRead ?? false,
|
isRead: isRead ?? false,
|
||||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -271,23 +257,19 @@ class PlanSubscriptionPaidToAuthorNotification implements BaseNotificationModel
|
|||||||
final bool isRead;
|
final bool isRead;
|
||||||
final String planName;
|
final String planName;
|
||||||
final String customerName;
|
final String customerName;
|
||||||
@override
|
|
||||||
final DateTime createdAt;
|
|
||||||
PlanSubscriptionPaidToAuthorNotification({
|
PlanSubscriptionPaidToAuthorNotification({
|
||||||
required this.type,
|
required this.type,
|
||||||
required this.isRead,
|
required this.isRead,
|
||||||
required this.planName,
|
required this.planName,
|
||||||
required this.customerName,
|
required this.customerName,
|
||||||
required this.createdAt,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
factory PlanSubscriptionPaidToAuthorNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
factory PlanSubscriptionPaidToAuthorNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead}) {
|
||||||
return PlanSubscriptionPaidToAuthorNotification(
|
return PlanSubscriptionPaidToAuthorNotification(
|
||||||
type: type,
|
type: type,
|
||||||
isRead: isRead ?? false,
|
isRead: isRead ?? false,
|
||||||
planName: json['plan_name'],
|
planName: json['plan_name'],
|
||||||
customerName: json['user_name'],
|
customerName: json['user_name'],
|
||||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -300,240 +282,21 @@ class ChosenPlanFinishedToConsultantNotification implements BaseNotificationMode
|
|||||||
final String planName;
|
final String planName;
|
||||||
final String customerName;
|
final String customerName;
|
||||||
final int planId;
|
final int planId;
|
||||||
@override
|
|
||||||
final DateTime createdAt;
|
|
||||||
ChosenPlanFinishedToConsultantNotification({
|
ChosenPlanFinishedToConsultantNotification({
|
||||||
required this.type,
|
required this.type,
|
||||||
required this.planName,
|
required this.planName,
|
||||||
required this.customerName,
|
required this.customerName,
|
||||||
required this.planId,
|
required this.planId,
|
||||||
required this.isRead,
|
required this.isRead,
|
||||||
required this.createdAt,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
factory ChosenPlanFinishedToConsultantNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
factory ChosenPlanFinishedToConsultantNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead}) {
|
||||||
return ChosenPlanFinishedToConsultantNotification(
|
return ChosenPlanFinishedToConsultantNotification(
|
||||||
type: type,
|
type: type,
|
||||||
planName: json['plan_name'],
|
planName: json['plan_name'],
|
||||||
customerName: json['client_name'],
|
customerName: json['client_name'],
|
||||||
planId: json['plan_id'],
|
planId: json['plan_id'],
|
||||||
isRead: isRead ?? false,
|
isRead: isRead ?? false,
|
||||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ChosenPlanLastDayFinishedForUser implements BaseNotificationModel {
|
|
||||||
@override
|
|
||||||
final NotificationType type;
|
|
||||||
@override
|
|
||||||
final bool isRead;
|
|
||||||
@override
|
|
||||||
final DateTime createdAt;
|
|
||||||
|
|
||||||
ChosenPlanLastDayFinishedForUser({required this.type, required this.isRead, required this.createdAt});
|
|
||||||
|
|
||||||
factory ChosenPlanLastDayFinishedForUser.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
|
||||||
return ChosenPlanLastDayFinishedForUser(
|
|
||||||
type: type,
|
|
||||||
isRead: isRead ?? false,
|
|
||||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ChosenPlanConsultantLastDayStarted implements BaseNotificationModel {
|
|
||||||
@override
|
|
||||||
final NotificationType type;
|
|
||||||
@override
|
|
||||||
final bool isRead;
|
|
||||||
final String planName;
|
|
||||||
final int planId;
|
|
||||||
final String chatId;
|
|
||||||
final String chatUrl;
|
|
||||||
@override
|
|
||||||
final DateTime createdAt;
|
|
||||||
|
|
||||||
ChosenPlanConsultantLastDayStarted({
|
|
||||||
required this.type,
|
|
||||||
required this.planName,
|
|
||||||
required this.planId,
|
|
||||||
required this.chatId,
|
|
||||||
required this.chatUrl,
|
|
||||||
required this.isRead,
|
|
||||||
required this.createdAt,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory ChosenPlanConsultantLastDayStarted.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
|
||||||
return ChosenPlanConsultantLastDayStarted(
|
|
||||||
type: type,
|
|
||||||
planName: json['plan_name'],
|
|
||||||
planId: json['plan_id'],
|
|
||||||
chatId: json['chat_id'],
|
|
||||||
chatUrl: json['chat_url'],
|
|
||||||
isRead: isRead ?? false,
|
|
||||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ComplaintToModerationForUser implements BaseNotificationModel {
|
|
||||||
@override
|
|
||||||
final NotificationType type;
|
|
||||||
@override
|
|
||||||
final bool isRead;
|
|
||||||
final String message;
|
|
||||||
final int entityId;
|
|
||||||
final String entityType;
|
|
||||||
@override
|
|
||||||
final DateTime createdAt;
|
|
||||||
|
|
||||||
ComplaintToModerationForUser({
|
|
||||||
required this.type,
|
|
||||||
required this.message,
|
|
||||||
required this.entityId,
|
|
||||||
required this.entityType,
|
|
||||||
required this.isRead,
|
|
||||||
required this.createdAt,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory ComplaintToModerationForUser.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
|
||||||
return ComplaintToModerationForUser(
|
|
||||||
type: type,
|
|
||||||
message: json['message'],
|
|
||||||
entityId: json['entity_id'],
|
|
||||||
entityType: json['entity_type'],
|
|
||||||
isRead: isRead ?? false,
|
|
||||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CommentReplied implements BaseNotificationModel {
|
|
||||||
@override
|
|
||||||
final NotificationType type;
|
|
||||||
@override
|
|
||||||
final bool isRead;
|
|
||||||
final String entityName;
|
|
||||||
final int commentedEntityId;
|
|
||||||
final String commentedEntityType;
|
|
||||||
final String author;
|
|
||||||
final int authorId;
|
|
||||||
final int parentCommentId;
|
|
||||||
@override
|
|
||||||
final DateTime createdAt;
|
|
||||||
|
|
||||||
CommentReplied({
|
|
||||||
required this.type,
|
|
||||||
required this.entityName,
|
|
||||||
required this.commentedEntityId,
|
|
||||||
required this.commentedEntityType,
|
|
||||||
required this.author,
|
|
||||||
required this.authorId,
|
|
||||||
required this.parentCommentId,
|
|
||||||
required this.isRead,
|
|
||||||
required this.createdAt,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory CommentReplied.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
|
||||||
return CommentReplied(
|
|
||||||
type: type,
|
|
||||||
entityName: json['entity_name'],
|
|
||||||
commentedEntityId: json['commented_entity_id'],
|
|
||||||
commentedEntityType: json['commented_entity_type'],
|
|
||||||
author: json['author'],
|
|
||||||
authorId: json['author_id'],
|
|
||||||
parentCommentId: json['parrent_comment_id'],
|
|
||||||
isRead: isRead ?? false,
|
|
||||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ScoreAdded implements BaseNotificationModel {
|
|
||||||
@override
|
|
||||||
final NotificationType type;
|
|
||||||
@override
|
|
||||||
final bool isRead;
|
|
||||||
final String scoreKey;
|
|
||||||
final int scorableId;
|
|
||||||
final String scorableType;
|
|
||||||
final String scorableName;
|
|
||||||
final String scorableSlug;
|
|
||||||
final int scoreValue;
|
|
||||||
@override
|
|
||||||
final DateTime createdAt;
|
|
||||||
|
|
||||||
ScoreAdded({
|
|
||||||
required this.type,
|
|
||||||
required this.scoreKey,
|
|
||||||
required this.scorableId,
|
|
||||||
required this.scorableType,
|
|
||||||
required this.scorableName,
|
|
||||||
required this.scorableSlug,
|
|
||||||
required this.scoreValue,
|
|
||||||
required this.isRead,
|
|
||||||
required this.createdAt,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory ScoreAdded.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
|
||||||
return ScoreAdded(
|
|
||||||
type: type,
|
|
||||||
scoreKey: json['score_key'],
|
|
||||||
scorableId: json['scorable_id'],
|
|
||||||
scorableType: json['scorable_type'],
|
|
||||||
scorableName: json['scorable_name'] ?? '',
|
|
||||||
scorableSlug: json['scorable_slug'] ?? '',
|
|
||||||
scoreValue: json['score_value'],
|
|
||||||
isRead: isRead ?? false,
|
|
||||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class EntityCommented implements BaseNotificationModel {
|
|
||||||
@override
|
|
||||||
final NotificationType type;
|
|
||||||
@override
|
|
||||||
final bool isRead;
|
|
||||||
final String entityName;
|
|
||||||
final int entityId;
|
|
||||||
final String entityType;
|
|
||||||
final String entityTranslatedType;
|
|
||||||
final String entityUrl;
|
|
||||||
final String authorName;
|
|
||||||
final int authorId;
|
|
||||||
final String authorSlug;
|
|
||||||
@override
|
|
||||||
final DateTime createdAt;
|
|
||||||
|
|
||||||
EntityCommented({
|
|
||||||
required this.type,
|
|
||||||
required this.entityName,
|
|
||||||
required this.entityId,
|
|
||||||
required this.entityType,
|
|
||||||
required this.entityTranslatedType,
|
|
||||||
required this.entityUrl,
|
|
||||||
required this.authorName,
|
|
||||||
required this.authorId,
|
|
||||||
required this.authorSlug,
|
|
||||||
required this.isRead,
|
|
||||||
required this.createdAt,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory EntityCommented.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
|
||||||
return EntityCommented(
|
|
||||||
type: type,
|
|
||||||
entityName: json['entity_name'],
|
|
||||||
entityId: json['entity_id'],
|
|
||||||
entityType: json['entity_type'],
|
|
||||||
entityTranslatedType: json['entity_traslated_type'],
|
|
||||||
entityUrl: json['entity_url'],
|
|
||||||
authorName: json['author_name'],
|
|
||||||
authorId: json['author_id'],
|
|
||||||
authorSlug: json['author_slug'],
|
|
||||||
isRead: isRead ?? false,
|
|
||||||
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -543,10 +306,11 @@ class UnknownNotification implements BaseNotificationModel {
|
|||||||
final NotificationType type;
|
final NotificationType type;
|
||||||
@override
|
@override
|
||||||
final bool isRead;
|
final bool isRead;
|
||||||
@override
|
|
||||||
final DateTime createdAt;
|
|
||||||
|
|
||||||
UnknownNotification({required this.type, this.isRead = true, DateTime? createdAt}) : createdAt = createdAt ?? DateTime.fromMillisecondsSinceEpoch(0);
|
UnknownNotification({
|
||||||
|
required this.type,
|
||||||
|
this.isRead = true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
enum NotificationType {
|
enum NotificationType {
|
||||||
@@ -563,38 +327,22 @@ enum NotificationType {
|
|||||||
chosenPlanTomorrowReminderToConsultantNotification,
|
chosenPlanTomorrowReminderToConsultantNotification,
|
||||||
planSubscriptionPaidToAuthor,
|
planSubscriptionPaidToAuthor,
|
||||||
chosenPlanFinishedToConsultant,
|
chosenPlanFinishedToConsultant,
|
||||||
chosenPlanLastDayFinishedForUser,
|
|
||||||
// Новые типы
|
|
||||||
chosenPlanConsultantLastDayStarted,
|
|
||||||
complaintToModerationForUser,
|
|
||||||
commentReplied,
|
|
||||||
scoreAdded,
|
|
||||||
entityCommented,
|
|
||||||
unknown;
|
unknown;
|
||||||
|
|
||||||
static NotificationType getTypeFromString(String type) => switch (type) {
|
static NotificationType getTypeFromString(String type) => switch (type) {
|
||||||
'App\\Notifications\\Plan\\PlanSubscriptionStartedToAuthor' => NotificationType.planSubscriptionStartedToAuthor,
|
'App\\Notifications\\Plan\\PlanSubscriptionStartedToAuthor' => NotificationType.planSubscriptionStartedToAuthor,
|
||||||
'App\\Notifications\\Plan\\PlanSubscriptionCanceledToAuthor' => NotificationType.planSubscriptionCanceledToAuthor,
|
'App\\Notifications\\Plan\\PlanSubscriptionCanceledToAuthor' => NotificationType.planSubscriptionCanceledToAuthor,
|
||||||
'App\\Notifications\\Plan\\ChosenPlanStartedToClientNotification' => NotificationType.chosenPlanStartedToClientNotification,
|
'App\\Notifications\\Plan\\ChosenPlanStartedToClientNotification' => NotificationType.chosenPlanStartedToClientNotification,
|
||||||
'App\\Notifications\\Plan\\ChosenPlanTomorrowReminderToClientNotification' => NotificationType.chosenPlanStartedToClientNotification,
|
'App\\Notifications\\Plan\\ChosenPlanTomorrowReminderToClientNotification' => NotificationType.chosenPlanStartedToClientNotification,
|
||||||
'App\\Notifications\\DiaryReminderNotification' => NotificationType.diaryReminderNotification,
|
'App\\Notifications\\DiaryReminderNotification' => NotificationType.diaryReminderNotification,
|
||||||
'App\\Notifications\\Plan\\ConsultantLeftComment' => NotificationType.consultantLeftComment,
|
'App\\Notifications\\Plan\\ConsultantLeftComment' => NotificationType.consultantLeftComment,
|
||||||
'App\\Notifications\\Plan\\PersonalizedDietIsDefined' => NotificationType.personalizedDietIsDefined,
|
'App\\Notifications\\Plan\\PersonalizedDietIsDefined' => NotificationType.personalizedDietIsDefined,
|
||||||
'App\\Notifications\\Plan\\ChosenPlanFinishedToClient' => NotificationType.chosenPlanFinishedToClient,
|
'App\\Notifications\\Plan\\ChosenPlanFinishedToClient' => NotificationType.chosenPlanFinishedToClient,
|
||||||
'App\\Notifications\\Plan\\PlanSubscriptionCanceledToCustomer' => NotificationType.planSubscriptionCanceledToCustomer,
|
'App\\Notifications\\Plan\\PlanSubscriptionCanceledToCustomer' => NotificationType.planSubscriptionCanceledToCustomer,
|
||||||
'App\\Notifications\\Plan\\ChosenPlanStartedToConsultantNotification' => NotificationType.chosenPlanStartedToConsultantNotification,
|
'App\\Notifications\\Plan\\ChosenPlanStartedToConsultantNotification' => NotificationType.chosenPlanStartedToConsultantNotification,
|
||||||
'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,
|
||||||
// Новые маппинги
|
};
|
||||||
'App\\Notifications\\ChosenPlan\\LastDayStartedForConsultant' => NotificationType.chosenPlanConsultantLastDayStarted,
|
|
||||||
'App\\Notifications\\ComplaintToModerationNotify' => NotificationType.complaintToModerationForUser,
|
|
||||||
'App\\Notifications\\Comment\\Replied' => NotificationType.commentReplied,
|
|
||||||
'App\\Notifications\\Score\\ScoreAdded' => NotificationType.scoreAdded,
|
|
||||||
'App\\Notifications\\Comment\\EntityCommented' => NotificationType.entityCommented,
|
|
||||||
_ => NotificationType.unknown,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DateTime parseDate(String date) => DateTime.parse(date).toLocal();
|
|
||||||
|
|||||||
@@ -13,106 +13,36 @@ class NotificationsCubit extends Cubit<NotificationsState> {
|
|||||||
final WsManager wsManager;
|
final WsManager wsManager;
|
||||||
final NotificationsRepository notificationsRepository;
|
final NotificationsRepository notificationsRepository;
|
||||||
StreamSubscription? _newNotificationsSubscription;
|
StreamSubscription? _newNotificationsSubscription;
|
||||||
NotificationsCubit({required this.wsManager, required this.notificationsRepository}) : super(const NotificationsState.empty());
|
NotificationsCubit({
|
||||||
|
required this.wsManager,
|
||||||
|
required this.notificationsRepository,
|
||||||
|
}) : super(const NotificationsState.empty());
|
||||||
|
|
||||||
Future<void> connectToWebSocket() async {
|
Future<void> connectToWebSocket() async {
|
||||||
await _newNotificationsSubscription?.cancel();
|
await _newNotificationsSubscription?.cancel();
|
||||||
_newNotificationsSubscription = wsManager.newNotificationsStream?.listen((event) {
|
_newNotificationsSubscription = wsManager.newNotificationsStream?.listen((event) {
|
||||||
_handleNotification(event.data);
|
|
||||||
|
_handleNotification(event.data, event.rootObject['created_at']);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseNotificationModel _parseNotification(Map<String, dynamic> notificationJson, NotificationType type) {
|
BaseNotificationModel _parseNotification(Map<String, dynamic> notificationJson, NotificationType type) {
|
||||||
try {
|
try {
|
||||||
final createdAt = notificationJson['created_at'];
|
|
||||||
final isRead = notificationJson['read_at'] != null;
|
|
||||||
|
|
||||||
return switch (type) {
|
return switch (type) {
|
||||||
NotificationType.planSubscriptionStartedToAuthor => PlanSubscriptionStartedToAuthorNotification.fromJson(
|
NotificationType.planSubscriptionStartedToAuthor => PlanSubscriptionStartedToAuthorNotification.fromJson(notificationJson, type),
|
||||||
notificationJson,
|
NotificationType.planSubscriptionCanceledToAuthor => PlanSubscriptionCanceledToAuthorNotification.fromJson(notificationJson, type),
|
||||||
type,
|
NotificationType.chosenPlanStartedToClientNotification => ChosenPlanStartedToClientNotification.fromJson(notificationJson, type),
|
||||||
isRead: isRead,
|
NotificationType.chosenPlanTomorrowReminderToClientNotification => ChosenPlanTomorrowReminderToClientNotification.fromJson(notificationJson, type),
|
||||||
createdAt: createdAt,
|
NotificationType.diaryReminderNotification => DiaryReminderNotification.fromJson(notificationJson, type),
|
||||||
),
|
NotificationType.consultantLeftComment => ConsultantLeftCommentNotification.fromJson(notificationJson, type),
|
||||||
NotificationType.planSubscriptionCanceledToAuthor => PlanSubscriptionCanceledToAuthorNotification.fromJson(
|
NotificationType.personalizedDietIsDefined => PersonalizedDietIsDefinedNotification.fromJson(notificationJson, type),
|
||||||
notificationJson,
|
NotificationType.chosenPlanFinishedToClient => ChosenPlanFinishedToClientNotification.fromJson(notificationJson, type),
|
||||||
type,
|
NotificationType.planSubscriptionCanceledToCustomer => PlanSubscriptionCanceledToAuthorNotification.fromJson(notificationJson, type),
|
||||||
isRead: isRead,
|
NotificationType.chosenPlanStartedToConsultantNotification => ChosenPlanStartedToConsultantNotification.fromJson(notificationJson, type),
|
||||||
createdAt: createdAt,
|
NotificationType.chosenPlanTomorrowReminderToConsultantNotification =>
|
||||||
),
|
ChosenPlanTomorrowReminderToConsultantNotification.fromJson(notificationJson, type),
|
||||||
NotificationType.chosenPlanStartedToClientNotification => ChosenPlanStartedToClientNotification.fromJson(
|
NotificationType.planSubscriptionPaidToAuthor => PlanSubscriptionPaidToAuthorNotification.fromJson(notificationJson, type),
|
||||||
notificationJson,
|
NotificationType.chosenPlanFinishedToConsultant => ChosenPlanFinishedToConsultantNotification.fromJson(notificationJson, type),
|
||||||
type,
|
|
||||||
isRead: isRead,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
NotificationType.chosenPlanTomorrowReminderToClientNotification => ChosenPlanTomorrowReminderToClientNotification.fromJson(
|
|
||||||
notificationJson,
|
|
||||||
type,
|
|
||||||
isRead: isRead,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
NotificationType.chosenPlanConsultantLastDayStarted => ChosenPlanConsultantLastDayStarted.fromJson(
|
|
||||||
notificationJson,
|
|
||||||
type,
|
|
||||||
isRead: isRead,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
NotificationType.diaryReminderNotification => DiaryReminderNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
|
|
||||||
NotificationType.consultantLeftComment => ConsultantLeftCommentNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
|
|
||||||
NotificationType.personalizedDietIsDefined => PersonalizedDietIsDefinedNotification.fromJson(
|
|
||||||
notificationJson,
|
|
||||||
type,
|
|
||||||
isRead: isRead,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
NotificationType.chosenPlanFinishedToClient => ChosenPlanFinishedToClientNotification.fromJson(
|
|
||||||
notificationJson,
|
|
||||||
type,
|
|
||||||
isRead: isRead,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
NotificationType.planSubscriptionCanceledToCustomer => PlanSubscriptionCanceledToAuthorNotification.fromJson(
|
|
||||||
notificationJson,
|
|
||||||
type,
|
|
||||||
isRead: isRead,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
NotificationType.chosenPlanStartedToConsultantNotification => ChosenPlanStartedToConsultantNotification.fromJson(
|
|
||||||
notificationJson,
|
|
||||||
type,
|
|
||||||
isRead: isRead,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
NotificationType.chosenPlanTomorrowReminderToConsultantNotification => ChosenPlanTomorrowReminderToConsultantNotification.fromJson(
|
|
||||||
notificationJson,
|
|
||||||
type,
|
|
||||||
isRead: isRead,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
NotificationType.planSubscriptionPaidToAuthor => PlanSubscriptionPaidToAuthorNotification.fromJson(
|
|
||||||
notificationJson,
|
|
||||||
type,
|
|
||||||
isRead: isRead,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
NotificationType.chosenPlanFinishedToConsultant => ChosenPlanFinishedToConsultantNotification.fromJson(
|
|
||||||
notificationJson,
|
|
||||||
type,
|
|
||||||
isRead: isRead,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
NotificationType.chosenPlanLastDayFinishedForUser => ChosenPlanLastDayFinishedForUser.fromJson(
|
|
||||||
notificationJson,
|
|
||||||
type,
|
|
||||||
isRead: isRead,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
// New notification types
|
|
||||||
NotificationType.complaintToModerationForUser => ComplaintToModerationForUser.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
|
|
||||||
NotificationType.commentReplied => CommentReplied.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
|
|
||||||
NotificationType.scoreAdded => ScoreAdded.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
|
|
||||||
NotificationType.entityCommented => EntityCommented.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
|
|
||||||
NotificationType.unknown => UnknownNotification(type: type),
|
NotificationType.unknown => UnknownNotification(type: type),
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -120,86 +50,39 @@ class NotificationsCubit extends Cubit<NotificationsState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleNotification(String notificationData) {
|
void _handleNotification(String notificationData, String createdAt) {
|
||||||
final json = jsonDecode(notificationData) as Map<String, dynamic>;
|
final json = jsonDecode(notificationData) as Map<String, dynamic>;
|
||||||
|
json.addAll({'date': createdAt});
|
||||||
final stringifiedType = json['notification_class_name'];
|
final stringifiedType = json['notification_class_name'];
|
||||||
final type = NotificationType.getTypeFromString(stringifiedType);
|
final type = NotificationType.getTypeFromString(stringifiedType);
|
||||||
final notification = _parseNotification(json, type);
|
final notification = _parseNotification(json, type);
|
||||||
_notifications.insert(0, notification);
|
_notifications.insert(0, notification);
|
||||||
|
emit(
|
||||||
final currentState = state;
|
NotificationsState.loaded(
|
||||||
if (currentState is NotificationsStateLoaded) {
|
notifications: _notifications,
|
||||||
emit(currentState.copyWith(notifications: _notifications, notificationsCount: _notifications.length, hasUnreadNotifications: true));
|
notificationsCount: _notifications.length,
|
||||||
}
|
hasUnreadNotifications: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> getNotifications() async {
|
Future<void> getNotifications() async {
|
||||||
try {
|
try {
|
||||||
final result = await notificationsRepository.getNotifications(page: 1);
|
final notifications = await notificationsRepository.getNotifications(page: 2);
|
||||||
if (result != null) {
|
_notifications.clear();
|
||||||
final notifications = result['notifications'] as List<BaseNotificationModel>;
|
_notifications.addAll(notifications ?? []);
|
||||||
final pagination = result['pagination'] as Map<String, dynamic>;
|
emit(
|
||||||
|
NotificationsState.loaded(
|
||||||
_notifications.clear();
|
notifications: _notifications,
|
||||||
_notifications.addAll(notifications);
|
notificationsCount: _notifications.length,
|
||||||
|
hasUnreadNotifications: (notifications ?? []).where((elem) => elem.isRead == false).isNotEmpty,
|
||||||
final currentPage = pagination['current_page'] as int;
|
),
|
||||||
final lastPage = pagination['last_page'] as int;
|
);
|
||||||
final hasNextPage = currentPage < lastPage;
|
|
||||||
|
|
||||||
emit(
|
|
||||||
NotificationsState.loaded(
|
|
||||||
notifications: _notifications,
|
|
||||||
notificationsCount: _notifications.length,
|
|
||||||
hasUnreadNotifications: notifications.where((elem) => elem.isRead == false).isNotEmpty,
|
|
||||||
currentPage: currentPage,
|
|
||||||
hasNextPage: hasNextPage,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (e, st) {
|
} catch (e, st) {
|
||||||
log(e.toString(), stackTrace: st);
|
log(e.toString(), stackTrace: st);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> loadMoreNotifications() async {
|
|
||||||
final currentState = state;
|
|
||||||
if (currentState is NotificationsStateLoaded && currentState.hasNextPage && !currentState.isLoadingMore) {
|
|
||||||
try {
|
|
||||||
emit(currentState.copyWith(isLoadingMore: true));
|
|
||||||
|
|
||||||
final nextPage = currentState.currentPage + 1;
|
|
||||||
final result = await notificationsRepository.getNotifications(page: nextPage);
|
|
||||||
|
|
||||||
if (result != null) {
|
|
||||||
final newNotifications = result['notifications'] as List<BaseNotificationModel>;
|
|
||||||
final pagination = result['pagination'] as Map<String, dynamic>;
|
|
||||||
|
|
||||||
_notifications.addAll(newNotifications);
|
|
||||||
|
|
||||||
final currentPage = pagination['current_page'] as int;
|
|
||||||
final lastPage = pagination['last_page'] as int;
|
|
||||||
final hasNextPage = currentPage < lastPage;
|
|
||||||
|
|
||||||
emit(
|
|
||||||
currentState.copyWith(
|
|
||||||
notifications: _notifications,
|
|
||||||
notificationsCount: _notifications.length,
|
|
||||||
currentPage: currentPage,
|
|
||||||
hasNextPage: hasNextPage,
|
|
||||||
isLoadingMore: false,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
emit(currentState.copyWith(isLoadingMore: false));
|
|
||||||
}
|
|
||||||
} catch (e, st) {
|
|
||||||
log(e.toString(), stackTrace: st);
|
|
||||||
emit(currentState.copyWith(isLoadingMore: false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> markNotificationsAsRead() async {
|
Future<void> markNotificationsAsRead() async {
|
||||||
final currentState = state;
|
final currentState = state;
|
||||||
if (currentState is NotificationsStateLoaded) {
|
if (currentState is NotificationsStateLoaded) {
|
||||||
|
|||||||
@@ -14,9 +14,6 @@ abstract class NotificationsState with _$NotificationsState {
|
|||||||
required int notificationsCount,
|
required int notificationsCount,
|
||||||
@Default(false) bool isLoading,
|
@Default(false) bool isLoading,
|
||||||
@Default(false) bool hasUnreadNotifications,
|
@Default(false) bool hasUnreadNotifications,
|
||||||
@Default(1) int currentPage,
|
|
||||||
@Default(true) bool hasNextPage,
|
|
||||||
@Default(false) bool isLoadingMore,
|
|
||||||
}) = NotificationsStateLoaded;
|
}) = NotificationsStateLoaded;
|
||||||
|
|
||||||
const factory NotificationsState.error({
|
const factory NotificationsState.error({
|
||||||
|
|||||||
@@ -10,38 +10,9 @@ 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';
|
||||||
|
|
||||||
class NotificationsScreen extends StatefulWidget {
|
class NotificationsScreen extends StatelessWidget {
|
||||||
const NotificationsScreen({super.key});
|
const NotificationsScreen({super.key});
|
||||||
|
|
||||||
@override
|
|
||||||
State<NotificationsScreen> createState() => _NotificationsScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _NotificationsScreenState extends State<NotificationsScreen> {
|
|
||||||
final ScrollController _scrollController = ScrollController();
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_scrollController.addListener(_onScroll);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_scrollController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onScroll() {
|
|
||||||
if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 200) {
|
|
||||||
final cubit = context.read<NotificationsCubit>();
|
|
||||||
final state = cubit.state;
|
|
||||||
if (state is NotificationsStateLoaded && state.hasNextPage && !state.isLoadingMore) {
|
|
||||||
cubit.loadMoreNotifications();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = S.of(context);
|
final s = S.of(context);
|
||||||
@@ -49,65 +20,62 @@ class _NotificationsScreenState extends State<NotificationsScreen> {
|
|||||||
builder: (BuildContext context, NotificationsState state) {
|
builder: (BuildContext context, NotificationsState state) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: appTheme.gray100,
|
backgroundColor: appTheme.gray100,
|
||||||
appBar: AppBar(title: const Text('Уведомления')),
|
appBar: AppBar(
|
||||||
|
title: const Text('Уведомления'),
|
||||||
|
),
|
||||||
body: state is NotificationsStateLoaded
|
body: state is NotificationsStateLoaded
|
||||||
? Stack(
|
? Stack(children: [
|
||||||
children: [
|
Padding(
|
||||||
Padding(
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
child: Container(
|
||||||
child: Container(
|
padding: const EdgeInsets.only(top: 16),
|
||||||
padding: const EdgeInsets.only(top: 16),
|
decoration: BoxDecoration(
|
||||||
decoration: BoxDecoration(
|
color: appTheme.whiteA700,
|
||||||
color: appTheme.whiteA700,
|
borderRadius: const BorderRadius.only(
|
||||||
borderRadius: const BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12)),
|
topLeft: Radius.circular(12),
|
||||||
|
topRight: Radius.circular(12),
|
||||||
),
|
),
|
||||||
child: state.isLoading
|
),
|
||||||
? const Center(child: Loader())
|
child: state.isLoading
|
||||||
: CustomScrollView(
|
? const Center(child: Loader())
|
||||||
controller: _scrollController,
|
: CustomScrollView(
|
||||||
slivers: [
|
slivers: [
|
||||||
SliverList.separated(
|
SliverList.separated(
|
||||||
separatorBuilder: (context, index) =>
|
separatorBuilder: (context, index) => const Padding(
|
||||||
const Padding(padding: EdgeInsets.symmetric(vertical: 12, horizontal: 16), child: CustomDivider()),
|
padding: EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||||
itemBuilder: (context, index) {
|
child: CustomDivider(),
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
||||||
child: NotificationWidget(notification: state.notifications[index]),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
itemCount: state.notifications.length,
|
|
||||||
),
|
),
|
||||||
if (state.isLoadingMore)
|
itemBuilder: (context, index) {
|
||||||
const SliverToBoxAdapter(
|
return Padding(
|
||||||
child: Padding(
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
padding: EdgeInsets.all(16.0),
|
child: NotificationWidget(
|
||||||
child: Center(child: Loader()),
|
notification: state.notifications[index],
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
],
|
},
|
||||||
),
|
itemCount: state.notifications.length,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16),
|
||||||
|
child: CustomOutlinedButton(
|
||||||
|
text: s.markAsRead,
|
||||||
|
height: 52,
|
||||||
|
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
||||||
|
buttonStyle:
|
||||||
|
ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.blueA400)),
|
||||||
|
onPressed: () async {
|
||||||
|
context.read<NotificationsCubit>().markNotificationsAsRead();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Align(
|
)
|
||||||
alignment: Alignment.bottomCenter,
|
])
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16),
|
|
||||||
child: CustomOutlinedButton(
|
|
||||||
text: s.markAsRead,
|
|
||||||
height: 52,
|
|
||||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
|
||||||
buttonStyle: ButtonStyle(
|
|
||||||
side: const WidgetStatePropertyAll(BorderSide.none),
|
|
||||||
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
|
|
||||||
),
|
|
||||||
onPressed: () async {
|
|
||||||
context.read<NotificationsCubit>().markNotificationsAsRead();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: const Placeholder(),
|
: const Placeholder(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,39 +15,45 @@ class NotificationWidget extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return switch (notification) {
|
return switch (notification) {
|
||||||
PlanSubscriptionStartedToAuthorNotification() => _PlanSubscriptionStartedToAuthorWidget(
|
PlanSubscriptionStartedToAuthorNotification() => _PlanSubscriptionStartedToAuthorWidget(
|
||||||
notification: notification as PlanSubscriptionStartedToAuthorNotification,
|
notification: notification as PlanSubscriptionStartedToAuthorNotification,
|
||||||
),
|
),
|
||||||
PlanSubscriptionCanceledToAuthorNotification() => _PlanSubscriptionCanceledToAuthorWidget(
|
PlanSubscriptionCanceledToAuthorNotification() => _PlanSubscriptionCanceledToAuthorWidget(
|
||||||
notification: notification as PlanSubscriptionCanceledToAuthorNotification,
|
notification: notification as PlanSubscriptionCanceledToAuthorNotification,
|
||||||
),
|
),
|
||||||
ChosenPlanStartedToClientNotification() => _ChosenPlanStartedToClientWidget(notification: notification as ChosenPlanStartedToClientNotification),
|
ChosenPlanStartedToClientNotification() => _ChosenPlanStartedToClientWidget(
|
||||||
|
notification: notification as ChosenPlanStartedToClientNotification,
|
||||||
|
),
|
||||||
ChosenPlanTomorrowReminderToClientNotification() => _ChosenPlanTomorrowReminderToClientWidget(
|
ChosenPlanTomorrowReminderToClientNotification() => _ChosenPlanTomorrowReminderToClientWidget(
|
||||||
notification: notification as ChosenPlanTomorrowReminderToClientNotification,
|
notification: notification as ChosenPlanTomorrowReminderToClientNotification,
|
||||||
),
|
),
|
||||||
DiaryReminderNotification() => _DiaryReminderWidget(notification: notification as DiaryReminderNotification),
|
DiaryReminderNotification() => _DiaryReminderWidget(
|
||||||
ConsultantLeftCommentNotification() => _ConsultantLeftCommentWidget(notification: notification as ConsultantLeftCommentNotification),
|
notification: notification as DiaryReminderNotification,
|
||||||
PersonalizedDietIsDefinedNotification() => _PersonalizedDietIsDefinedWidget(notification: notification as PersonalizedDietIsDefinedNotification),
|
),
|
||||||
ChosenPlanFinishedToClientNotification() => _ChosenPlanFinishedToClientWidget(notification: notification as ChosenPlanFinishedToClientNotification),
|
ConsultantLeftCommentNotification() => _ConsultantLeftCommentWidget(
|
||||||
|
notification: notification as ConsultantLeftCommentNotification,
|
||||||
|
),
|
||||||
|
PersonalizedDietIsDefinedNotification() => _PersonalizedDietIsDefinedWidget(
|
||||||
|
notification: notification as PersonalizedDietIsDefinedNotification,
|
||||||
|
),
|
||||||
|
ChosenPlanFinishedToClientNotification() => _ChosenPlanFinishedToClientWidget(
|
||||||
|
notification: notification as ChosenPlanFinishedToClientNotification,
|
||||||
|
),
|
||||||
PlanSubscriptionCanceledToCustomerNotification() => _PlanSubscriptionCanceledToCustomerWidget(
|
PlanSubscriptionCanceledToCustomerNotification() => _PlanSubscriptionCanceledToCustomerWidget(
|
||||||
notification: notification as PlanSubscriptionCanceledToCustomerNotification,
|
notification: notification as PlanSubscriptionCanceledToCustomerNotification,
|
||||||
),
|
),
|
||||||
ChosenPlanStartedToConsultantNotification() => _ChosenPlanStartedToConsultantWidget(
|
ChosenPlanStartedToConsultantNotification() => _ChosenPlanStartedToConsultantWidget(
|
||||||
notification: notification as ChosenPlanStartedToConsultantNotification,
|
notification: notification as ChosenPlanStartedToConsultantNotification,
|
||||||
),
|
),
|
||||||
ChosenPlanTomorrowReminderToConsultantNotification() => _ChosenPlanTomorrowReminderToConsultantWidget(
|
ChosenPlanTomorrowReminderToConsultantNotification() => _ChosenPlanTomorrowReminderToConsultantWidget(
|
||||||
notification: notification as ChosenPlanTomorrowReminderToConsultantNotification,
|
notification: notification as ChosenPlanTomorrowReminderToConsultantNotification,
|
||||||
),
|
),
|
||||||
PlanSubscriptionPaidToAuthorNotification() => _PlanSubscriptionPaidToAuthorWidget(notification: notification as PlanSubscriptionPaidToAuthorNotification),
|
PlanSubscriptionPaidToAuthorNotification() => _PlanSubscriptionPaidToAuthorWidget(
|
||||||
|
notification: notification as PlanSubscriptionPaidToAuthorNotification,
|
||||||
|
),
|
||||||
ChosenPlanFinishedToConsultantNotification() => _ChosenPlanFinishedToConsultantWidget(
|
ChosenPlanFinishedToConsultantNotification() => _ChosenPlanFinishedToConsultantWidget(
|
||||||
notification: notification as ChosenPlanFinishedToConsultantNotification,
|
notification: notification as ChosenPlanFinishedToConsultantNotification,
|
||||||
),
|
),
|
||||||
ChosenPlanLastDayFinishedForUser() => _ChosenPlanLastDayFinishedForUserWidget(notification: notification as ChosenPlanLastDayFinishedForUser),
|
UnknownNotification() => const SizedBox.shrink(),
|
||||||
ChosenPlanConsultantLastDayStarted() => _ChosenPlanConsultantLastDayStartedWidget(notification: notification as ChosenPlanConsultantLastDayStarted),
|
|
||||||
ComplaintToModerationForUser() => _ComplaintToModerationForUserWidget(notification: notification as ComplaintToModerationForUser),
|
|
||||||
CommentReplied() => _CommentRepliedWidget(notification: notification as CommentReplied),
|
|
||||||
ScoreAdded() => _ScoreAddedWidget(notification: notification as ScoreAdded),
|
|
||||||
EntityCommented() => _EntityCommentedWidget(notification: notification as EntityCommented),
|
|
||||||
UnknownNotification() => _UnknownNotificationWidget(notification: notification as UnknownNotification),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,8 +67,7 @@ class _PlanSubscriptionStartedToAuthorWidget extends StatelessWidget {
|
|||||||
|
|
||||||
return _BaseNotificationWidget(
|
return _BaseNotificationWidget(
|
||||||
isRead: notification.isRead,
|
isRead: notification.isRead,
|
||||||
notificationsText: s.userSubscribedToPlan(notification.customerName, notification.planName),
|
notificationsText: s.fillYasterday,
|
||||||
createdAt: notification.createdAt,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,8 +81,7 @@ class _PlanSubscriptionCanceledToAuthorWidget extends StatelessWidget {
|
|||||||
|
|
||||||
return _BaseNotificationWidget(
|
return _BaseNotificationWidget(
|
||||||
isRead: notification.isRead,
|
isRead: notification.isRead,
|
||||||
notificationsText: s.userCanceldThePlanSubscription(notification.customerName, notification.planName),
|
notificationsText: s.fillYasterday,
|
||||||
createdAt: notification.createdAt,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,7 +93,10 @@ class _ChosenPlanStartedToClientWidget extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = S.of(context);
|
final s = S.of(context);
|
||||||
|
|
||||||
return _BaseNotificationWidget(isRead: notification.isRead, notificationsText: s.planStarted(notification.planName), createdAt: notification.createdAt);
|
return _BaseNotificationWidget(
|
||||||
|
isRead: notification.isRead,
|
||||||
|
notificationsText: s.fillYasterday,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,8 +109,7 @@ class _ChosenPlanTomorrowReminderToClientWidget extends StatelessWidget {
|
|||||||
|
|
||||||
return _BaseNotificationWidget(
|
return _BaseNotificationWidget(
|
||||||
isRead: notification.isRead,
|
isRead: notification.isRead,
|
||||||
notificationsText: s.tomorrowYourPlanWillStart(notification.planName),
|
notificationsText: s.fillYasterday,
|
||||||
createdAt: notification.createdAt,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,7 +120,13 @@ class _DiaryReminderWidget extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = S.of(context);
|
final s = S.of(context);
|
||||||
return _BaseNotificationWidget(isRead: notification.isRead, notificationsText: s.fillYasterday, createdAt: notification.createdAt);
|
return _BaseNotificationWidget(
|
||||||
|
onTap: () {
|
||||||
|
GoRouter.of(context).go('/diary');
|
||||||
|
},
|
||||||
|
isRead: notification.isRead,
|
||||||
|
notificationsText: s.fillYasterday,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +140,6 @@ class _ConsultantLeftCommentWidget extends StatelessWidget {
|
|||||||
return _BaseNotificationWidget(
|
return _BaseNotificationWidget(
|
||||||
isRead: notification.isRead,
|
isRead: notification.isRead,
|
||||||
notificationsText: s.consultantLeftComment(notification.date.format('dd.MM.yyyy')),
|
notificationsText: s.consultantLeftComment(notification.date.format('dd.MM.yyyy')),
|
||||||
createdAt: notification.createdAt,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,7 +149,7 @@ class _PersonalizedDietIsDefinedWidget extends StatelessWidget {
|
|||||||
final PersonalizedDietIsDefinedNotification notification;
|
final PersonalizedDietIsDefinedNotification notification;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return _BaseNotificationWidget(isRead: notification.isRead, notificationsText: 'Персональная диета определена', createdAt: notification.createdAt);
|
return Container();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +163,6 @@ class _ChosenPlanFinishedToClientWidget extends StatelessWidget {
|
|||||||
return _BaseNotificationWidget(
|
return _BaseNotificationWidget(
|
||||||
isRead: notification.isRead,
|
isRead: notification.isRead,
|
||||||
notificationsText: '${s.congratsYouHaveFinishedPlan} «${notification.planName}»',
|
notificationsText: '${s.congratsYouHaveFinishedPlan} «${notification.planName}»',
|
||||||
createdAt: notification.createdAt,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,7 +177,6 @@ class _PlanSubscriptionCanceledToCustomerWidget extends StatelessWidget {
|
|||||||
return _BaseNotificationWidget(
|
return _BaseNotificationWidget(
|
||||||
isRead: notification.isRead,
|
isRead: notification.isRead,
|
||||||
notificationsText: s.youCanceledSubscriptionToPlan(notification.planName),
|
notificationsText: s.youCanceledSubscriptionToPlan(notification.planName),
|
||||||
createdAt: notification.createdAt,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -182,7 +191,6 @@ class _ChosenPlanStartedToConsultantWidget extends StatelessWidget {
|
|||||||
return _BaseNotificationWidget(
|
return _BaseNotificationWidget(
|
||||||
isRead: notification.isRead,
|
isRead: notification.isRead,
|
||||||
notificationsText: s.planWithUserStarted(notification.planName, notification.customerName),
|
notificationsText: s.planWithUserStarted(notification.planName, notification.customerName),
|
||||||
createdAt: notification.createdAt,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,7 +205,6 @@ class _ChosenPlanTomorrowReminderToConsultantWidget extends StatelessWidget {
|
|||||||
return _BaseNotificationWidget(
|
return _BaseNotificationWidget(
|
||||||
isRead: notification.isRead,
|
isRead: notification.isRead,
|
||||||
notificationsText: s.tommorowPlanWithUserWillStart(notification.planName, notification.customerName),
|
notificationsText: s.tommorowPlanWithUserWillStart(notification.planName, notification.customerName),
|
||||||
createdAt: notification.createdAt,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -212,7 +219,6 @@ class _PlanSubscriptionPaidToAuthorWidget extends StatelessWidget {
|
|||||||
return _BaseNotificationWidget(
|
return _BaseNotificationWidget(
|
||||||
isRead: notification.isRead,
|
isRead: notification.isRead,
|
||||||
notificationsText: s.userPaidThePlan(notification.customerName, notification.planName),
|
notificationsText: s.userPaidThePlan(notification.customerName, notification.planName),
|
||||||
createdAt: notification.createdAt,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,223 +229,62 @@ class _ChosenPlanFinishedToConsultantWidget extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = S.of(context);
|
final s = S.of(context);
|
||||||
return Column(
|
return Column(children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
Text(
|
||||||
children: [
|
'${s.congratsYouHaveFinishedPlan} «${notification.planName}»',
|
||||||
Row(
|
style: TextStyles.textMSemibold.copyWith(color: appTheme.black),
|
||||||
children: [
|
),
|
||||||
Expanded(
|
const SizedBox(height: 12),
|
||||||
child: Column(
|
Text(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
s.userConfirmedPlanCompletionWithoutSum,
|
||||||
children: [
|
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
||||||
Text('${s.congratsYouHaveFinishedPlan} «${notification.planName}»', style: TextStyles.textMSemibold.copyWith(color: appTheme.black)),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
Text(s.userConfirmedPlanCompletionWithoutSum, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
|
CustomOutlinedButton(
|
||||||
const SizedBox(height: 12),
|
text: s.collectCharge,
|
||||||
CustomOutlinedButton(
|
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
||||||
text: s.collectCharge,
|
buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.green)),
|
||||||
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
onPressed: () {
|
||||||
buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.green)),
|
GoRouter.of(context).pushNamed(CardSettingsScreen.name, extra: notification.planId);
|
||||||
onPressed: () {
|
},
|
||||||
GoRouter.of(context).pushNamed(CardSettingsScreen.name, extra: notification.planId);
|
),
|
||||||
},
|
const SizedBox(height: 12),
|
||||||
),
|
]);
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (!notification.isRead)
|
|
||||||
Container(
|
|
||||||
decoration: BoxDecoration(color: appTheme.blueA400, shape: BoxShape.circle),
|
|
||||||
width: 8,
|
|
||||||
height: 8,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
'${notification.createdAt.day} ${_getMonthName(notification.createdAt.month)} ${notification.createdAt.hour.toString().padLeft(2, '0')}:${notification.createdAt.minute.toString().padLeft(2, '0')}',
|
|
||||||
style: TextStyles.textSMedium.copyWith(color: appTheme.gray600),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String _getMonthName(int month) {
|
|
||||||
switch (month) {
|
|
||||||
case 1:
|
|
||||||
return 'янв';
|
|
||||||
case 2:
|
|
||||||
return 'фев';
|
|
||||||
case 3:
|
|
||||||
return 'мар';
|
|
||||||
case 4:
|
|
||||||
return 'апр';
|
|
||||||
case 5:
|
|
||||||
return 'май';
|
|
||||||
case 6:
|
|
||||||
return 'июн';
|
|
||||||
case 7:
|
|
||||||
return 'июл';
|
|
||||||
case 8:
|
|
||||||
return 'авг';
|
|
||||||
case 9:
|
|
||||||
return 'сен';
|
|
||||||
case 10:
|
|
||||||
return 'окт';
|
|
||||||
case 11:
|
|
||||||
return 'ноя';
|
|
||||||
case 12:
|
|
||||||
return 'дек';
|
|
||||||
default:
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CardSettingsScreen
|
// CardSettingsScreen
|
||||||
class _BaseNotificationWidget extends StatelessWidget {
|
class _BaseNotificationWidget extends StatelessWidget {
|
||||||
const _BaseNotificationWidget({required this.notificationsText, required this.isRead, required this.createdAt});
|
const _BaseNotificationWidget({required this.notificationsText, required this.isRead, this.onTap});
|
||||||
final String notificationsText;
|
final String notificationsText;
|
||||||
final bool isRead;
|
final bool isRead;
|
||||||
final DateTime createdAt;
|
final Function()? onTap;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return GestureDetector(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
onTap: onTap,
|
||||||
children: [
|
child: Row(
|
||||||
Row(
|
children: [
|
||||||
children: [
|
SizedBox(
|
||||||
Expanded(
|
width: MediaQuery.sizeOf(context).width * 0.6,
|
||||||
child: Text(notificationsText, style: TextStyles.textMMedium.copyWith(color: appTheme.black)),
|
child: Text(
|
||||||
|
notificationsText,
|
||||||
|
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
|
||||||
),
|
),
|
||||||
if (!isRead)
|
),
|
||||||
Container(
|
if (!isRead) ...[
|
||||||
decoration: BoxDecoration(color: appTheme.blueA400, shape: BoxShape.circle),
|
const Spacer(),
|
||||||
width: 8,
|
Container(
|
||||||
height: 8,
|
decoration: BoxDecoration(
|
||||||
|
color: appTheme.blueA400,
|
||||||
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
],
|
width: 8,
|
||||||
),
|
height: 8,
|
||||||
const SizedBox(height: 4),
|
)
|
||||||
Text(
|
]
|
||||||
'${createdAt.day} ${_getMonthName(createdAt.month)} ${createdAt.hour.toString().padLeft(2, '0')}:${createdAt.minute.toString().padLeft(2, '0')}',
|
],
|
||||||
style: TextStyles.textSMedium.copyWith(color: appTheme.gray600),
|
),
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String _getMonthName(int month) {
|
|
||||||
switch (month) {
|
|
||||||
case 1:
|
|
||||||
return 'янв';
|
|
||||||
case 2:
|
|
||||||
return 'фев';
|
|
||||||
case 3:
|
|
||||||
return 'мар';
|
|
||||||
case 4:
|
|
||||||
return 'апр';
|
|
||||||
case 5:
|
|
||||||
return 'май';
|
|
||||||
case 6:
|
|
||||||
return 'июн';
|
|
||||||
case 7:
|
|
||||||
return 'июл';
|
|
||||||
case 8:
|
|
||||||
return 'авг';
|
|
||||||
case 9:
|
|
||||||
return 'сен';
|
|
||||||
case 10:
|
|
||||||
return 'окт';
|
|
||||||
case 11:
|
|
||||||
return 'ноя';
|
|
||||||
case 12:
|
|
||||||
return 'дек';
|
|
||||||
default:
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ChosenPlanLastDayFinishedForUserWidget extends StatelessWidget {
|
|
||||||
const _ChosenPlanLastDayFinishedForUserWidget({required this.notification});
|
|
||||||
final ChosenPlanLastDayFinishedForUser notification;
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return _BaseNotificationWidget(isRead: notification.isRead, notificationsText: 'Последний день плана завершен', createdAt: notification.createdAt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ChosenPlanConsultantLastDayStartedWidget extends StatelessWidget {
|
|
||||||
const _ChosenPlanConsultantLastDayStartedWidget({required this.notification});
|
|
||||||
final ChosenPlanConsultantLastDayStarted notification;
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return _BaseNotificationWidget(
|
|
||||||
isRead: notification.isRead,
|
|
||||||
notificationsText: 'Последний день плана начался: ${notification.planName}',
|
|
||||||
createdAt: notification.createdAt,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ComplaintToModerationForUserWidget extends StatelessWidget {
|
|
||||||
const _ComplaintToModerationForUserWidget({required this.notification});
|
|
||||||
final ComplaintToModerationForUser notification;
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return _BaseNotificationWidget(isRead: notification.isRead, notificationsText: 'Жалоба отправлена на модерацию', createdAt: notification.createdAt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CommentRepliedWidget extends StatelessWidget {
|
|
||||||
const _CommentRepliedWidget({required this.notification});
|
|
||||||
final CommentReplied notification;
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return _BaseNotificationWidget(
|
|
||||||
isRead: notification.isRead,
|
|
||||||
notificationsText: 'Ответ на комментарий: ${notification.entityName}',
|
|
||||||
createdAt: notification.createdAt,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ScoreAddedWidget extends StatelessWidget {
|
|
||||||
const _ScoreAddedWidget({required this.notification});
|
|
||||||
final ScoreAdded notification;
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return _BaseNotificationWidget(
|
|
||||||
isRead: notification.isRead,
|
|
||||||
notificationsText: 'Добавлены баллы: ${notification.scoreValue}',
|
|
||||||
createdAt: notification.createdAt,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _EntityCommentedWidget extends StatelessWidget {
|
|
||||||
const _EntityCommentedWidget({required this.notification});
|
|
||||||
final EntityCommented notification;
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return _BaseNotificationWidget(
|
|
||||||
isRead: notification.isRead,
|
|
||||||
notificationsText: 'Комментарий к ${notification.entityTranslatedType}: ${notification.entityName}',
|
|
||||||
createdAt: notification.createdAt,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _UnknownNotificationWidget extends StatelessWidget {
|
|
||||||
const _UnknownNotificationWidget({required this.notification});
|
|
||||||
final UnknownNotification notification;
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return _BaseNotificationWidget(
|
|
||||||
isRead: notification.isRead,
|
|
||||||
notificationsText: 'Неизвестное уведомление: ${notification.type.toString()}',
|
|
||||||
createdAt: notification.createdAt,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
import 'dart:developer';
|
import 'package:eatvio_app/features/notifications/models/notification_model.dart';
|
||||||
import 'package:eatvio_app/features/notifications/service/notifications_service.dart';
|
import 'package:eatvio_app/features/notifications/service/notifications_service.dart';
|
||||||
|
|
||||||
class NotificationsRepository {
|
class NotificationsRepository {
|
||||||
final NotificationsService notificationsService;
|
final NotificationsService notificationsService;
|
||||||
NotificationsRepository({required this.notificationsService});
|
NotificationsRepository({required this.notificationsService});
|
||||||
|
|
||||||
Future<Map<String, dynamic>?> getNotifications({required int page}) {
|
Future<List<BaseNotificationModel>?> getNotifications({required int page}) {
|
||||||
try {
|
try {
|
||||||
return notificationsService.getNotifications(page: page);
|
return notificationsService.getNotifications(page: page);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e.toString());
|
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,137 +5,50 @@ class NotificationsService {
|
|||||||
final Dio dio;
|
final Dio dio;
|
||||||
NotificationsService(this.dio);
|
NotificationsService(this.dio);
|
||||||
|
|
||||||
Future<Map<String, dynamic>?> getNotifications({required int page}) async {
|
Future<List<BaseNotificationModel>?> getNotifications({required int page}) async {
|
||||||
try {
|
try {
|
||||||
final response = await dio.get('/api/profile/notifications?page=$page');
|
final response = await dio.get('/api/profile/notifications?page=$page');
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final data = response.data['data'] as List<dynamic>;
|
final data = response.data['data'] as List<dynamic>;
|
||||||
|
|
||||||
final notifications = data.map((notificationJson) {
|
final notifications = data.map((notificationJson) {
|
||||||
try {
|
try {
|
||||||
final stringifiedType = notificationJson['data']['notification_class_name'];
|
final stringifiedType = notificationJson['data']['notification_class_name'];
|
||||||
final readDate = notificationJson['read_at'];
|
final readDate = notificationJson['read_at'];
|
||||||
final createdAt = notificationJson['created_at'];
|
|
||||||
final type = NotificationType.getTypeFromString(stringifiedType);
|
final type = NotificationType.getTypeFromString(stringifiedType);
|
||||||
|
|
||||||
return switch (type) {
|
return switch (type) {
|
||||||
NotificationType.planSubscriptionStartedToAuthor => PlanSubscriptionStartedToAuthorNotification.fromJson(
|
NotificationType.planSubscriptionStartedToAuthor =>
|
||||||
notificationJson['data'],
|
PlanSubscriptionStartedToAuthorNotification.fromJson(notificationJson['data'], type, isRead: readDate != null),
|
||||||
type,
|
NotificationType.planSubscriptionCanceledToAuthor =>
|
||||||
isRead: readDate != null,
|
PlanSubscriptionCanceledToAuthorNotification.fromJson(notificationJson['data'], type, isRead: readDate != null),
|
||||||
createdAt: createdAt,
|
NotificationType.chosenPlanStartedToClientNotification =>
|
||||||
),
|
ChosenPlanStartedToClientNotification.fromJson(notificationJson['data'], type, isRead: readDate != null),
|
||||||
NotificationType.planSubscriptionCanceledToAuthor => PlanSubscriptionCanceledToAuthorNotification.fromJson(
|
NotificationType.chosenPlanTomorrowReminderToClientNotification =>
|
||||||
notificationJson['data'],
|
ChosenPlanTomorrowReminderToClientNotification.fromJson(notificationJson['data'], type, isRead: readDate != null),
|
||||||
type,
|
|
||||||
isRead: readDate != null,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
NotificationType.chosenPlanStartedToClientNotification => ChosenPlanStartedToClientNotification.fromJson(
|
|
||||||
notificationJson['data'],
|
|
||||||
type,
|
|
||||||
isRead: readDate != null,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
NotificationType.chosenPlanTomorrowReminderToClientNotification => ChosenPlanTomorrowReminderToClientNotification.fromJson(
|
|
||||||
notificationJson['data'],
|
|
||||||
type,
|
|
||||||
isRead: readDate != null,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
NotificationType.diaryReminderNotification => DiaryReminderNotification.fromJson(
|
NotificationType.diaryReminderNotification => DiaryReminderNotification.fromJson(
|
||||||
notificationJson['data'],
|
(notificationJson['data'] as Map<String, dynamic>).putIfAbsent('date', () => notificationJson['created_at']), type,
|
||||||
type,
|
isRead: readDate != null),
|
||||||
isRead: readDate != null,
|
NotificationType.consultantLeftComment => ConsultantLeftCommentNotification.fromJson(notificationJson['data'], type, isRead: readDate != null),
|
||||||
createdAt: createdAt,
|
NotificationType.personalizedDietIsDefined =>
|
||||||
),
|
PersonalizedDietIsDefinedNotification.fromJson(notificationJson['data'], type, isRead: readDate != null),
|
||||||
NotificationType.consultantLeftComment => ConsultantLeftCommentNotification.fromJson(
|
NotificationType.chosenPlanFinishedToClient =>
|
||||||
notificationJson['data'],
|
ChosenPlanFinishedToClientNotification.fromJson(notificationJson['data'], type, isRead: readDate != null),
|
||||||
type,
|
NotificationType.planSubscriptionCanceledToCustomer =>
|
||||||
isRead: readDate != null,
|
PlanSubscriptionCanceledToAuthorNotification.fromJson(notificationJson['data'], type, isRead: readDate != null),
|
||||||
createdAt: createdAt,
|
NotificationType.chosenPlanStartedToConsultantNotification =>
|
||||||
),
|
ChosenPlanStartedToConsultantNotification.fromJson(notificationJson['data'], type, isRead: readDate != null),
|
||||||
NotificationType.personalizedDietIsDefined => PersonalizedDietIsDefinedNotification.fromJson(
|
NotificationType.chosenPlanTomorrowReminderToConsultantNotification =>
|
||||||
notificationJson['data'],
|
ChosenPlanTomorrowReminderToConsultantNotification.fromJson(notificationJson['data'], type, isRead: readDate != null),
|
||||||
type,
|
NotificationType.planSubscriptionPaidToAuthor =>
|
||||||
isRead: readDate != null,
|
PlanSubscriptionPaidToAuthorNotification.fromJson(notificationJson['data'], type, isRead: readDate != null),
|
||||||
createdAt: createdAt,
|
NotificationType.chosenPlanFinishedToConsultant =>
|
||||||
),
|
ChosenPlanFinishedToConsultantNotification.fromJson(notificationJson['data'], type, isRead: readDate != null),
|
||||||
NotificationType.chosenPlanFinishedToClient => ChosenPlanFinishedToClientNotification.fromJson(
|
NotificationType.unknown => UnknownNotification(type: type),
|
||||||
notificationJson['data'],
|
|
||||||
type,
|
|
||||||
isRead: readDate != null,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
NotificationType.planSubscriptionCanceledToCustomer => PlanSubscriptionCanceledToAuthorNotification.fromJson(
|
|
||||||
notificationJson['data'],
|
|
||||||
type,
|
|
||||||
isRead: readDate != null,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
NotificationType.chosenPlanStartedToConsultantNotification => ChosenPlanStartedToConsultantNotification.fromJson(
|
|
||||||
notificationJson['data'],
|
|
||||||
type,
|
|
||||||
isRead: readDate != null,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
NotificationType.chosenPlanTomorrowReminderToConsultantNotification => ChosenPlanTomorrowReminderToConsultantNotification.fromJson(
|
|
||||||
notificationJson['data'],
|
|
||||||
type,
|
|
||||||
isRead: readDate != null,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
NotificationType.planSubscriptionPaidToAuthor => PlanSubscriptionPaidToAuthorNotification.fromJson(
|
|
||||||
notificationJson['data'],
|
|
||||||
type,
|
|
||||||
isRead: readDate != null,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
NotificationType.chosenPlanFinishedToConsultant => ChosenPlanFinishedToConsultantNotification.fromJson(
|
|
||||||
notificationJson['data'],
|
|
||||||
type,
|
|
||||||
isRead: readDate != null,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
NotificationType.chosenPlanLastDayFinishedForUser => ChosenPlanLastDayFinishedForUser.fromJson(
|
|
||||||
notificationJson['data'],
|
|
||||||
type,
|
|
||||||
isRead: readDate != null,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
// Новые типы уведомлений
|
|
||||||
NotificationType.chosenPlanConsultantLastDayStarted => ChosenPlanConsultantLastDayStarted.fromJson(
|
|
||||||
notificationJson['data'],
|
|
||||||
type,
|
|
||||||
isRead: readDate != null,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
NotificationType.complaintToModerationForUser => ComplaintToModerationForUser.fromJson(
|
|
||||||
notificationJson['data'],
|
|
||||||
type,
|
|
||||||
isRead: readDate != null,
|
|
||||||
createdAt: createdAt,
|
|
||||||
),
|
|
||||||
NotificationType.commentReplied => CommentReplied.fromJson(notificationJson['data'], type, isRead: readDate != null, createdAt: createdAt),
|
|
||||||
NotificationType.scoreAdded => ScoreAdded.fromJson(notificationJson['data'], type, isRead: readDate != null, createdAt: createdAt),
|
|
||||||
NotificationType.entityCommented => EntityCommented.fromJson(notificationJson['data'], type, isRead: readDate != null, createdAt: createdAt),
|
|
||||||
NotificationType.unknown => UnknownNotification(type: type, createdAt: parseDate(createdAt)),
|
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return UnknownNotification(type: NotificationType.unknown, createdAt: DateTime.now());
|
return UnknownNotification(type: NotificationType.unknown);
|
||||||
}
|
}
|
||||||
}).toList();
|
}).toList();
|
||||||
|
return notifications.where((notification) => notification.type != NotificationType.unknown).toList();
|
||||||
// Извлекаем информацию о пагинации из корня ответа
|
|
||||||
final currentPage = response.data['current_page'] as int;
|
|
||||||
final lastPage = response.data['last_page'] as int;
|
|
||||||
final perPage = response.data['per_page'] as int;
|
|
||||||
final total = response.data['total'] as int;
|
|
||||||
|
|
||||||
return {
|
|
||||||
'notifications': notifications,
|
|
||||||
'pagination': {'current_page': currentPage, 'last_page': lastPage, 'per_page': perPage, 'total': total},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -151,5 +64,3 @@ class NotificationsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DateTime parseDate(String date) => DateTime.parse(date).toLocal();
|
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import 'package:eatvio_app/features/diets/presentation/widgets/author_card.dart'
|
|||||||
import 'package:eatvio_app/features/diets/presentation/widgets/expandable_text.dart';
|
import 'package:eatvio_app/features/diets/presentation/widgets/expandable_text.dart';
|
||||||
import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart';
|
import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart';
|
||||||
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
|
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
|
||||||
import 'package:eatvio_app/core/comments/comments_cubit/comments_cubit.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart';
|
||||||
import 'package:eatvio_app/core/comments/comments_cubit/comments_state.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_state.dart';
|
||||||
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
|
||||||
import 'package:eatvio_app/features/feed/presentation/widgets/comments_widget.dart';
|
import 'package:eatvio_app/features/feed/presentation/widgets/comments_widget.dart';
|
||||||
import 'package:eatvio_app/features/plans/presentation/cubit/about_plan_cubit.dart';
|
import 'package:eatvio_app/features/plans/presentation/cubit/about_plan_cubit.dart';
|
||||||
@@ -208,7 +208,7 @@ class _AboutPlanScreenState extends State<AboutPlanScreen> {
|
|||||||
GoRouter.of(context).pushReplacementNamed(
|
GoRouter.of(context).pushReplacementNamed(
|
||||||
'chatScreen',
|
'chatScreen',
|
||||||
extra: {
|
extra: {
|
||||||
'uuid': chosenPlan.chatId,
|
'chatId': chosenPlan.chatId,
|
||||||
'sendInitialMessage': true,
|
'sendInitialMessage': true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,11 +14,9 @@ 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,7 +25,6 @@ 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(
|
||||||
@@ -45,7 +44,6 @@ 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();
|
||||||
@@ -125,7 +123,7 @@ class ProfileCubit extends Cubit<ProfileState> {
|
|||||||
AppRouter.navigatorKey.currentContext?.pushNamed(
|
AppRouter.navigatorKey.currentContext?.pushNamed(
|
||||||
'chatScreen',
|
'chatScreen',
|
||||||
extra: {
|
extra: {
|
||||||
'uuid': chatIdOrError,
|
'chatId': 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: {
|
||||||
'uuid': await sl<ProfileCubit>().getChatRoomIdBySlug(
|
'chatId': await sl<ProfileCubit>().getChatRoomIdBySlug(
|
||||||
author: Author(
|
author: Author(
|
||||||
authorSlug: widget.slug,
|
authorSlug: widget.slug,
|
||||||
authorName: widget.name,
|
authorName: widget.name,
|
||||||
|
|||||||
@@ -203,12 +203,4 @@ class ProfileRepository {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteAccount() async {
|
|
||||||
try {
|
|
||||||
await profileService.deleteAccount();
|
|
||||||
} catch (e) {
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,7 +119,4 @@ 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();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ import 'package:eatvio_app/features/diets/presentation/widgets/author_card.dart'
|
|||||||
import 'package:eatvio_app/features/diets/presentation/widgets/expandable_text.dart';
|
import 'package:eatvio_app/features/diets/presentation/widgets/expandable_text.dart';
|
||||||
import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart';
|
import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart';
|
||||||
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
|
import 'package:eatvio_app/core/widgets/main_background_bouble.dart';
|
||||||
import 'package:eatvio_app/core/comments/comments_cubit/comments_cubit.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_cubit.dart';
|
||||||
import 'package:eatvio_app/core/comments/comments_cubit/comments_state.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/comments_cubit/comments_state.dart';
|
||||||
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
|
||||||
import 'package:eatvio_app/features/feed/presentation/widgets/comments_widget.dart';
|
import 'package:eatvio_app/features/feed/presentation/widgets/comments_widget.dart';
|
||||||
import 'package:eatvio_app/features/recipe/presentation/cubit/about_recipe_cubit.dart';
|
import 'package:eatvio_app/features/recipe/presentation/cubit/about_recipe_cubit.dart';
|
||||||
@@ -205,14 +205,14 @@ class _AboutRecipeScreenState extends State<AboutRecipeScreen> {
|
|||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
leading: BackButton(
|
leading: BackButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (context.canPop()) {
|
if (context.canPop()) {
|
||||||
GoRouter.of(context).pop();
|
GoRouter.of(context).pop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
GoRouter.of(context).go('/feed-screen');
|
GoRouter.of(context).go('/feed-screen');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
recipeState.recipe.title ?? '',
|
recipeState.recipe.title ?? '',
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
@@ -356,7 +356,6 @@ class _AboutRecipeScreenState extends State<AboutRecipeScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
|
||||||
ExpandableText(text: recipeState.recipe.description),
|
ExpandableText(text: recipeState.recipe.description),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Padding(
|
Padding(
|
||||||
@@ -401,14 +400,14 @@ class _AboutRecipeScreenState extends State<AboutRecipeScreen> {
|
|||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
leading: BackButton(
|
leading: BackButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (context.canPop()) {
|
if (context.canPop()) {
|
||||||
GoRouter.of(context).pop();
|
GoRouter.of(context).pop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
GoRouter.of(context).go('/feed-screen');
|
GoRouter.of(context).go('/feed-screen');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
'',
|
'',
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
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';
|
||||||
@@ -52,19 +51,25 @@ 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 {
|
||||||
try {
|
if (await registrationRepository.emailIsReachable(state.sheet.email)) {
|
||||||
final token = await registrationRepository.tryRegister(
|
try {
|
||||||
state.sheet.copyWith(
|
final token = await registrationRepository.tryRegister(
|
||||||
phone: state.sheet.phone.replaceAll(' ', '').replaceAll('(', '').replaceAll(')', ''),
|
state.sheet.copyWith(
|
||||||
responseBody: '{}',
|
phone: state.sheet.phone.replaceAll(' ', '').replaceAll('(', '').replaceAll(')', ''),
|
||||||
),
|
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,6 +7,5 @@ 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,161 +43,142 @@ 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: BlocListener<RegistrationCubit, RegistrationState>(
|
child: BlocBuilder<RegistrationCubit, RegistrationState>(
|
||||||
listener: (context, state) {
|
builder: (BuildContext context, RegistrationState state) {
|
||||||
if (state.error != null && state.error!.isNotEmpty) {
|
return Scaffold(
|
||||||
showDialog(
|
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
|
||||||
context: context,
|
floatingActionButton: Row(
|
||||||
builder: (context) => AlertDialog(
|
children: [
|
||||||
title: const Text('Ошибка при регистрации'),
|
Container(
|
||||||
content: Text(state.error!),
|
height: 48,
|
||||||
actions: [
|
width: MediaQuery.of(context).size.width * 0.2,
|
||||||
TextButton(
|
padding: const EdgeInsets.only(left: 16, right: 4),
|
||||||
onPressed: () => Navigator.pop(context),
|
child: FloatingActionButton.extended(
|
||||||
child: const Text('OK'),
|
heroTag: 'backButtonRegistration',
|
||||||
|
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: BlocBuilder<RegistrationCubit, RegistrationState>(
|
child: FloatingActionButton.extended(
|
||||||
builder: (BuildContext context, RegistrationState state) {
|
heroTag: 'nextButtonRegistration',
|
||||||
return Scaffold(
|
highlightElevation: 0,
|
||||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
|
elevation: 0,
|
||||||
floatingActionButton: Row(
|
focusElevation: 0,
|
||||||
children: [
|
shape: RoundedRectangleBorder(
|
||||||
Container(
|
borderRadius: BorderRadius.circular(20),
|
||||||
height: 48,
|
),
|
||||||
width: MediaQuery.of(context).size.width * 0.2,
|
backgroundColor: appTheme.blueA400,
|
||||||
padding: const EdgeInsets.only(left: 16, right: 4),
|
onPressed: () async {
|
||||||
child: FloatingActionButton.extended(
|
if (await canGoNext(state, context)) {
|
||||||
heroTag: 'backButtonRegistration',
|
|
||||||
highlightElevation: 0,
|
|
||||||
focusElevation: 0,
|
|
||||||
elevation: 0,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
|
||||||
backgroundColor: const Color(0xFFEAF2FD),
|
|
||||||
onPressed: () async {
|
|
||||||
setState(() {
|
setState(() {
|
||||||
pageController.previousPage(
|
pageController.nextPage(
|
||||||
duration: const Duration(milliseconds: 350),
|
duration: const Duration(milliseconds: 350),
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
label: Icon(
|
},
|
||||||
Icons.arrow_back_ios_new_rounded,
|
label: Text(
|
||||||
color: appTheme.blueA400,
|
'Продолжить',
|
||||||
),
|
style: theme.textTheme.titleSmall!.copyWith(color: Colors.white),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
),
|
||||||
height: 48,
|
],
|
||||||
width: MediaQuery.of(context).size.width * 0.8,
|
),
|
||||||
padding: const EdgeInsets.only(left: 4, right: 16),
|
appBar: AppBar(
|
||||||
child: FloatingActionButton.extended(
|
automaticallyImplyLeading: false,
|
||||||
heroTag: 'nextButtonRegistration',
|
leading: Builder(
|
||||||
highlightElevation: 0,
|
builder: (context) => IconButton(
|
||||||
elevation: 0,
|
icon: const Icon(Icons.close_rounded),
|
||||||
focusElevation: 0,
|
onPressed: () => GoRouter.of(context).pop(),
|
||||||
shape: RoundedRectangleBorder(
|
),
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
|
||||||
backgroundColor: appTheme.blueA400,
|
|
||||||
onPressed: () async {
|
|
||||||
if (await canGoNext(state, context)) {
|
|
||||||
setState(() {
|
|
||||||
pageController.nextPage(
|
|
||||||
duration: const Duration(milliseconds: 350),
|
|
||||||
curve: Curves.easeInOut,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
label: Text(
|
|
||||||
'Продолжить',
|
|
||||||
style: theme.textTheme.titleSmall!.copyWith(color: Colors.white),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
appBar: AppBar(
|
centerTitle: true,
|
||||||
automaticallyImplyLeading: false,
|
title: SvgPicture.asset(
|
||||||
leading: Builder(
|
ImageConstant.imgLogoEatvio,
|
||||||
builder: (context) => IconButton(
|
height: 48,
|
||||||
icon: const Icon(Icons.close_rounded),
|
fit: BoxFit.contain,
|
||||||
onPressed: () => GoRouter.of(context).pop(),
|
),
|
||||||
),
|
actions: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 16),
|
||||||
|
child: Text('шаг $step из 15'),
|
||||||
),
|
),
|
||||||
centerTitle: true,
|
],
|
||||||
title: SvgPicture.asset(
|
bottom: PreferredSize(
|
||||||
ImageConstant.imgLogoEatvio,
|
preferredSize: Size(MediaQuery.of(context).size.width, 32),
|
||||||
height: 48,
|
child: Padding(
|
||||||
fit: BoxFit.contain,
|
padding: const EdgeInsets.all(16),
|
||||||
),
|
child: TweenAnimationBuilder<double>(
|
||||||
actions: [
|
duration: const Duration(milliseconds: 350),
|
||||||
Padding(
|
curve: Curves.easeInOut,
|
||||||
padding: const EdgeInsets.only(right: 16),
|
tween: Tween<double>(
|
||||||
child: Text('шаг $step из 15'),
|
begin: 1 / 15,
|
||||||
),
|
end: progressValue,
|
||||||
],
|
),
|
||||||
bottom: PreferredSize(
|
builder: (context, value, _) => LinearProgressIndicator(
|
||||||
preferredSize: Size(MediaQuery.of(context).size.width, 32),
|
value: value,
|
||||||
child: Padding(
|
borderRadius: BorderRadius.circular(16),
|
||||||
padding: const EdgeInsets.all(16),
|
minHeight: 8,
|
||||||
child: TweenAnimationBuilder<double>(
|
backgroundColor: Colors.grey.shade100,
|
||||||
duration: const Duration(milliseconds: 350),
|
valueColor: AlwaysStoppedAnimation<Color>(appTheme.blueA400),
|
||||||
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(
|
),
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
body: PageView(
|
||||||
controller: pageController,
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
onPageChanged: (index) {
|
controller: pageController,
|
||||||
setState(() {
|
onPageChanged: (index) {
|
||||||
step = index + 1;
|
setState(() {
|
||||||
progressValue = step / 15;
|
step = index + 1;
|
||||||
});
|
progressValue = step / 15;
|
||||||
},
|
});
|
||||||
children: [
|
},
|
||||||
// LanguageSelectScreen(sheet: state.sheet),
|
children: [
|
||||||
PhoneScreen(sheet: state.sheet),
|
// LanguageSelectScreen(sheet: state.sheet),
|
||||||
CredentialsScreen(sheet: state.sheet),
|
PhoneScreen(sheet: state.sheet),
|
||||||
NameScreen(sheet: state.sheet),
|
CredentialsScreen(sheet: state.sheet),
|
||||||
GenderScreen(sheet: state.sheet),
|
NameScreen(sheet: state.sheet),
|
||||||
DateOfBirthScreen(sheet: state.sheet),
|
GenderScreen(sheet: state.sheet),
|
||||||
CityOfLivingScreen(sheet: state.sheet),
|
DateOfBirthScreen(sheet: state.sheet),
|
||||||
BodyCircumferencesScreen(sheet: state.sheet),
|
CityOfLivingScreen(sheet: state.sheet),
|
||||||
WeightScreen(sheet: state.sheet),
|
BodyCircumferencesScreen(sheet: state.sheet),
|
||||||
HeightScreen(sheet: state.sheet),
|
WeightScreen(sheet: state.sheet),
|
||||||
BloodPressureScreen(sheet: state.sheet),
|
HeightScreen(sheet: state.sheet),
|
||||||
AllergiesScreen(sheet: state.sheet),
|
BloodPressureScreen(sheet: state.sheet),
|
||||||
MedicineIntakeScreen(sheet: state.sheet),
|
AllergiesScreen(sheet: state.sheet),
|
||||||
FavoriteSportsScreen(sheet: state.sheet),
|
MedicineIntakeScreen(sheet: state.sheet),
|
||||||
FavoriteFoodsScreen(sheet: state.sheet),
|
FavoriteSportsScreen(sheet: state.sheet),
|
||||||
AdditionalInformationScreen(sheet: state.sheet),
|
FavoriteFoodsScreen(sheet: state.sheet),
|
||||||
],
|
AdditionalInformationScreen(sheet: state.sheet),
|
||||||
),
|
],
|
||||||
);
|
),
|
||||||
},
|
);
|
||||||
),
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
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';
|
||||||
@@ -20,7 +19,6 @@ 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() {
|
||||||
@@ -29,11 +27,6 @@ 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();
|
||||||
@@ -112,17 +105,11 @@ class _VerifyScreenState extends State<VerifyScreen> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(toolbarHeight: 0),
|
appBar: AppBar(
|
||||||
|
toolbarHeight: 0,
|
||||||
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Stack(
|
child: WebViewWidget(controller: _controller),
|
||||||
children: [
|
|
||||||
WebViewWidget(controller: _controller),
|
|
||||||
if (_isLoading)
|
|
||||||
const Center(
|
|
||||||
child: Loader(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user