forked from eatvio/eatvio_app
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45774c3875 | ||
|
|
62c0914fe2 | ||
|
|
48f5b64223 | ||
|
|
91bb5cf483 | ||
|
|
c4dedac3f3 | ||
|
|
8ed243f63c | ||
|
|
a10fe6d2f5 | ||
|
|
35b78bdeee | ||
|
|
ef037eb4d5 | ||
|
|
5a3385cacf | ||
|
|
ef4d91a5d8 | ||
|
|
dca4b4c471 | ||
|
|
cfbd7597fa | ||
|
|
412ac0aeeb |
@@ -144,7 +144,11 @@ jobs:
|
|||||||
run: flutter test
|
run: flutter test
|
||||||
|
|
||||||
- name: Build apk
|
- name: Build apk
|
||||||
run: flutter build apk --release --dart-define=HIDE_PLANS=false
|
run: |
|
||||||
|
BUILD_DATE=$(TZ=Europe/Moscow date +'%Y-%m-%d %H:%M:%S')
|
||||||
|
flutter build apk --release \
|
||||||
|
--dart-define=HIDE_PLANS=false \
|
||||||
|
--dart-define=BUILD_DATE="$BUILD_DATE"
|
||||||
|
|
||||||
- name: Rename and move apk
|
- name: Rename and move apk
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -396,7 +396,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
|
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
CURRENT_PROJECT_VERSION = 67;
|
CURRENT_PROJECT_VERSION = 68;
|
||||||
DEVELOPMENT_TEAM = VL68C36HYS;
|
DEVELOPMENT_TEAM = VL68C36HYS;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
@@ -546,7 +546,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
|
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
CURRENT_PROJECT_VERSION = 67;
|
CURRENT_PROJECT_VERSION = 68;
|
||||||
DEVELOPMENT_TEAM = VL68C36HYS;
|
DEVELOPMENT_TEAM = VL68C36HYS;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
@@ -584,7 +584,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
|
CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
CURRENT_PROJECT_VERSION = 67;
|
CURRENT_PROJECT_VERSION = 68;
|
||||||
DEVELOPMENT_TEAM = VL68C36HYS;
|
DEVELOPMENT_TEAM = VL68C36HYS;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>67</string>
|
<string>68</string>
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
|||||||
@@ -46,13 +46,15 @@ platform :ios do
|
|||||||
|
|
||||||
match(type: "appstore", readonly: true)
|
match(type: "appstore", readonly: true)
|
||||||
increment_build_number(xcodeproj: "Runner.xcodeproj")
|
increment_build_number(xcodeproj: "Runner.xcodeproj")
|
||||||
|
|
||||||
|
build_date = Time.now.getlocal("+03:00").strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
build_app(
|
build_app(
|
||||||
workspace: "Runner.xcworkspace",
|
workspace: "Runner.xcworkspace",
|
||||||
scheme: "Runner",
|
scheme: "Runner",
|
||||||
export_method: "app-store",
|
export_method: "app-store",
|
||||||
codesigning_identity: "Apple Distribution",
|
codesigning_identity: "Apple Distribution",
|
||||||
xcargs: "--dart-define=HIDE_PLANS=true"
|
xcargs: "--dart-define=HIDE_PLANS=true --dart-define=BUILD_DATE='#{build_date}'"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:eatvio_app/core/env_config.dart';
|
|||||||
|
|
||||||
String baseUrl = EnvConfig.baseUrl;
|
String baseUrl = EnvConfig.baseUrl;
|
||||||
bool hidePlans = const bool.fromEnvironment('HIDE_PLANS', defaultValue: false);
|
bool hidePlans = const bool.fromEnvironment('HIDE_PLANS', defaultValue: false);
|
||||||
|
const buildDate = String.fromEnvironment('BUILD_DATE', defaultValue: '');
|
||||||
|
|
||||||
const List<int> durationValues = [7, 14, 21];
|
const List<int> durationValues = [7, 14, 21];
|
||||||
|
|
||||||
|
|||||||
@@ -73,10 +73,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AppRouter.navigatorKey.currentContext?.go(
|
AppRouter.navigatorKey.currentContext?.go('/feed-screen', extra: {'needToInitializeWebSocket': false});
|
||||||
'/feed-screen',
|
|
||||||
extra: {'needToInitializeWebSocket': false},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -102,11 +99,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
GoRouter.of(context).pushNamed(DevToolsScreen.name);
|
GoRouter.of(context).pushNamed(DevToolsScreen.name);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(ImageConstant.imgLogoEatvio, height: isFocused ? 64 : MediaQuery.of(context).size.width - 64, fit: BoxFit.contain),
|
||||||
ImageConstant.imgLogoEatvio,
|
|
||||||
height: isFocused ? 64 : MediaQuery.of(context).size.width - 64,
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
@@ -137,10 +130,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
hidePassword = !hidePassword;
|
hidePassword = !hidePassword;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
icon: Icon(
|
icon: Icon(hidePassword ? Icons.visibility_outlined : Icons.visibility_off_outlined, color: Colors.grey),
|
||||||
hidePassword ? Icons.visibility_outlined : Icons.visibility_off_outlined,
|
|
||||||
color: Colors.grey,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
@@ -149,11 +139,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
onTap: () => GoRouter.of(context).goNamed('forgetPasswordScreen', extra: false),
|
onTap: () => GoRouter.of(context).goNamed('forgetPasswordScreen', extra: false),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Забыли пароль?',
|
'Забыли пароль?',
|
||||||
style: TextStyle(
|
style: TextStyle(color: appTheme.blueA400, decorationColor: appTheme.blueA400, decoration: TextDecoration.underline),
|
||||||
color: appTheme.blueA400,
|
|
||||||
decorationColor: appTheme.blueA400,
|
|
||||||
decoration: TextDecoration.underline,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -166,11 +152,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
onTap: () => GoRouter.of(context).goNamed('registrationScreen'),
|
onTap: () => GoRouter.of(context).goNamed('registrationScreen'),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Зарегистрироваться',
|
'Зарегистрироваться',
|
||||||
style: TextStyle(
|
style: TextStyle(color: appTheme.blueA400, decorationColor: appTheme.blueA400, decoration: TextDecoration.underline),
|
||||||
color: appTheme.blueA400,
|
|
||||||
decorationColor: appTheme.blueA400,
|
|
||||||
decoration: TextDecoration.underline,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -181,23 +163,22 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
text: 'Нажимая "Войти" или "Зарегистрироваться", Вы соглашаетесь \n',
|
text: 'Нажимая "Войти" или "Зарегистрироваться", Вы соглашаетесь \n',
|
||||||
style: const TextStyle(
|
style: const TextStyle(fontSize: 12, color: Colors.black),
|
||||||
fontSize: 12,
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
children: [
|
children: [
|
||||||
const TextSpan(text: 'c '),
|
const TextSpan(text: 'c '),
|
||||||
TextSpan(
|
if (!hidePlans) ...[
|
||||||
text: ' Публичной офертой',
|
TextSpan(
|
||||||
style: TextStyle(
|
text: ' Публичной офертой',
|
||||||
fontSize: 12,
|
style: TextStyle(
|
||||||
color: appTheme.blueA400,
|
fontSize: 12,
|
||||||
decorationColor: appTheme.blueA400,
|
color: appTheme.blueA400,
|
||||||
decoration: TextDecoration.underline,
|
decorationColor: appTheme.blueA400,
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
),
|
||||||
|
recognizer: TapGestureRecognizer()..onTap = () async => await launchUrl(Uri.parse('$baseUrl/oferta_bs.pdf')),
|
||||||
),
|
),
|
||||||
recognizer: TapGestureRecognizer()..onTap = () async => await launchUrl(Uri.parse('$baseUrl/oferta_bs.pdf')),
|
const TextSpan(text: ' и '),
|
||||||
),
|
],
|
||||||
const TextSpan(text: ' и '),
|
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: 'Политикой конфиденциальности\nи защитой персональных данных',
|
text: 'Политикой конфиденциальности\nи защитой персональных данных',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -223,17 +204,12 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
highlightElevation: 0,
|
highlightElevation: 0,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
focusElevation: 0,
|
focusElevation: 0,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||||
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(
|
final isAuthenticated = await authCubit.getTokenByCredentials(emailController.text, passwordController.text);
|
||||||
emailController.text,
|
|
||||||
passwordController.text,
|
|
||||||
);
|
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
_reconnectWebSocket(context);
|
_reconnectWebSocket(context);
|
||||||
@@ -242,10 +218,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
},
|
},
|
||||||
label: state is AuthStateLoading
|
label: state is AuthStateLoading
|
||||||
? const CupertinoActivityIndicator()
|
? const CupertinoActivityIndicator()
|
||||||
: Text(
|
: Text('Войти', style: theme.textTheme.titleSmall!.copyWith(color: Colors.white)),
|
||||||
'Войти',
|
|
||||||
style: theme.textTheme.titleSmall!.copyWith(color: Colors.white),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
@@ -257,9 +230,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
highlightElevation: 0,
|
highlightElevation: 0,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
focusElevation: 0,
|
focusElevation: 0,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||||
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
|
||||||
@@ -274,14 +245,10 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const Text(
|
const Text('Где взять QR-код?\n', style: TextStyle(fontSize: 18)),
|
||||||
'Где взять QR-код?\n',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
Text(
|
||||||
'1. Войдите на сайте $baseUrl в свой аккаунт\n\n2. Перейдите в настройки профиля\n\n3. В разделе "Доп информация" перейдите на "QR Login"'),
|
'1. Войдите на сайте $baseUrl в свой аккаунт\n\n2. Перейдите в настройки профиля\n\n3. В разделе "Доп информация" перейдите на "QR Login"',
|
||||||
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
@@ -293,15 +260,10 @@ 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(
|
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)),
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
child: const Text(
|
child: const Text(
|
||||||
'Отмена',
|
'Отмена',
|
||||||
style: TextStyle(
|
style: TextStyle(color: Colors.black, fontWeight: FontWeight.w400),
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -314,15 +276,10 @@ 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(
|
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)),
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
child: Text(
|
child: Text(
|
||||||
'Перейти',
|
'Перейти',
|
||||||
style: TextStyle(
|
style: TextStyle(color: appTheme.blueA400, fontWeight: FontWeight.w600),
|
||||||
color: appTheme.blueA400,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -337,10 +294,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
|||||||
},
|
},
|
||||||
label: state is AuthStateLoading
|
label: state is AuthStateLoading
|
||||||
? const CupertinoActivityIndicator()
|
? const CupertinoActivityIndicator()
|
||||||
: const Icon(
|
: const Icon(Icons.qr_code_scanner_rounded, color: Colors.white),
|
||||||
Icons.qr_code_scanner_rounded,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import 'package:eatvio_app/features/chat/presentation/pages/photo_view_screen.da
|
|||||||
import 'package:eatvio_app/features/chat/presentation/widgets/about_order_button.dart';
|
import 'package:eatvio_app/features/chat/presentation/widgets/about_order_button.dart';
|
||||||
import 'package:eatvio_app/features/chat/presentation/widgets/chat_bubble_widget.dart';
|
import 'package:eatvio_app/features/chat/presentation/widgets/chat_bubble_widget.dart';
|
||||||
import 'package:eatvio_app/core/locator_service.dart';
|
import 'package:eatvio_app/core/locator_service.dart';
|
||||||
|
import 'package:eatvio_app/features/menu/presentation/blocked_user_cubit/blocked_users_cubit.dart';
|
||||||
|
import 'package:eatvio_app/features/menu/presentation/blocked_user_cubit/blocked_users_state.dart';
|
||||||
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||||
import 'package:eatvio_app/features/chat/presentation/widgets/notification_banner.dart';
|
import 'package:eatvio_app/features/chat/presentation/widgets/notification_banner.dart';
|
||||||
@@ -446,7 +448,27 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
InkWell(
|
InkWell(
|
||||||
customBorder: const CircleBorder(),
|
customBorder: const CircleBorder(),
|
||||||
onTap: () {
|
onTap: () async {
|
||||||
|
// Проверяем, заблокирован ли пользователь
|
||||||
|
final blockedUsersCubit = sl<BlockedUsersCubit>();
|
||||||
|
await blockedUsersCubit.getBlockedUsers();
|
||||||
|
final blockedState = blockedUsersCubit.state;
|
||||||
|
|
||||||
|
if (blockedState is BlockedUsersStateLoaded) {
|
||||||
|
final isBlocked = blockedState.profilesList.any((profile) => profile.slug == state.chatRoom.users.receiver.slug);
|
||||||
|
if (isBlocked) {
|
||||||
|
if (context.mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Text('Вы не можете отправить сообщение заблокированному пользователю'),
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (textEditingController.text.isNotEmpty || pickedFiles.isNotEmpty) {
|
if (textEditingController.text.isNotEmpty || pickedFiles.isNotEmpty) {
|
||||||
chatRoomCubit.sendMessage(
|
chatRoomCubit.sendMessage(
|
||||||
data: textEditingController.text.isEmpty ? null : textEditingController.text.trim(),
|
data: textEditingController.text.isEmpty ? null : textEditingController.text.trim(),
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import 'package:flutter/cupertino.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:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
|
import 'package:eatvio_app/features/menu/presentation/blocked_user_cubit/blocked_users_cubit.dart';
|
||||||
|
import 'package:eatvio_app/features/menu/presentation/blocked_user_cubit/blocked_users_state.dart';
|
||||||
|
|
||||||
class EmptyChatScreen extends StatefulWidget {
|
class EmptyChatScreen extends StatefulWidget {
|
||||||
final Author author;
|
final Author author;
|
||||||
@@ -133,21 +135,45 @@ class _EmptyChatScreenState extends State<EmptyChatScreen> {
|
|||||||
InkWell(
|
InkWell(
|
||||||
customBorder: const CircleBorder(),
|
customBorder: const CircleBorder(),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
|
// Проверяем, заблокирован ли пользователь
|
||||||
|
final blockedUsersCubit = sl<BlockedUsersCubit>();
|
||||||
|
await blockedUsersCubit.getBlockedUsers();
|
||||||
|
final blockedState = blockedUsersCubit.state;
|
||||||
|
|
||||||
|
if (blockedState is BlockedUsersStateLoaded) {
|
||||||
|
final isBlocked = blockedState.profilesList.any((profile) => profile.slug == widget.author.authorSlug);
|
||||||
|
if (isBlocked) {
|
||||||
|
if (context.mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Text('Вы не можете отправить сообщение заблокированному пользователю'),
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (textEditingController.text.isNotEmpty) {
|
if (textEditingController.text.isNotEmpty) {
|
||||||
await sl<ChatRepository>().sendMessage(
|
await sl<ChatRepository>().sendMessage(
|
||||||
data: textEditingController.text.trim(),
|
data: textEditingController.text.trim(),
|
||||||
slug: widget.author.authorSlug,
|
slug: widget.author.authorSlug,
|
||||||
);
|
);
|
||||||
setState(() {
|
if (mounted) {
|
||||||
textEditingController.clear();
|
setState(() {
|
||||||
});
|
textEditingController.clear();
|
||||||
|
});
|
||||||
|
}
|
||||||
final chatIdOrError = await sl<ChatRepository>().getChatByUserSlug(widget.author.authorSlug);
|
final chatIdOrError = await sl<ChatRepository>().getChatByUserSlug(widget.author.authorSlug);
|
||||||
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
if (context.mounted) {
|
||||||
'chatScreen',
|
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
||||||
extra: {
|
'chatScreen',
|
||||||
'uuid': chatIdOrError,
|
extra: {
|
||||||
},
|
'uuid': chatIdOrError,
|
||||||
);
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Ink(
|
child: Ink(
|
||||||
@@ -175,20 +201,44 @@ class _EmptyChatScreenState extends State<EmptyChatScreen> {
|
|||||||
CupertinoActionSheetAction(
|
CupertinoActionSheetAction(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
|
||||||
|
// Проверяем, заблокирован ли пользователь
|
||||||
|
final blockedUsersCubit = sl<BlockedUsersCubit>();
|
||||||
|
await blockedUsersCubit.getBlockedUsers();
|
||||||
|
final blockedState = blockedUsersCubit.state;
|
||||||
|
|
||||||
|
if (blockedState is BlockedUsersStateLoaded) {
|
||||||
|
final isBlocked = blockedState.profilesList.any((profile) => profile.slug == widget.author.authorSlug);
|
||||||
|
if (isBlocked) {
|
||||||
|
if (context.mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Text('Вы не можете отправить сообщение заблокированному пользователю'),
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final picker = ImagePicker();
|
final picker = ImagePicker();
|
||||||
XFile? image = await picker.pickImage(source: ImageSource.camera);
|
List<XFile>? images = await picker.pickMultiImage();
|
||||||
if (image != null) {
|
if (images.isNotEmpty) {
|
||||||
|
List<File> files = images.map((image) => File(image.path)).toList();
|
||||||
await sl<ChatRepository>().sendMessage(
|
await sl<ChatRepository>().sendMessage(
|
||||||
files: [File(image.path)],
|
files: files,
|
||||||
slug: widget.author.authorSlug,
|
slug: widget.author.authorSlug,
|
||||||
);
|
);
|
||||||
final chatIdOrError = await sl<ChatRepository>().getChatByUserSlug(widget.author.authorSlug);
|
final chatIdOrError = await sl<ChatRepository>().getChatByUserSlug(widget.author.authorSlug);
|
||||||
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
if (mounted) {
|
||||||
'chatScreen',
|
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
||||||
extra: {
|
'chatScreen',
|
||||||
'uuid': chatIdOrError,
|
extra: {
|
||||||
},
|
'uuid': chatIdOrError,
|
||||||
);
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: const Row(
|
child: const Row(
|
||||||
@@ -211,12 +261,14 @@ class _EmptyChatScreenState extends State<EmptyChatScreen> {
|
|||||||
slug: widget.author.authorSlug,
|
slug: widget.author.authorSlug,
|
||||||
);
|
);
|
||||||
final chatIdOrError = await sl<ChatRepository>().getChatByUserSlug(widget.author.authorSlug);
|
final chatIdOrError = await sl<ChatRepository>().getChatByUserSlug(widget.author.authorSlug);
|
||||||
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
if (mounted) {
|
||||||
'chatScreen',
|
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
||||||
extra: {
|
'chatScreen',
|
||||||
'uuid': chatIdOrError,
|
extra: {
|
||||||
},
|
'uuid': chatIdOrError,
|
||||||
);
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: const Row(
|
child: const Row(
|
||||||
@@ -261,12 +313,14 @@ class _EmptyChatScreenState extends State<EmptyChatScreen> {
|
|||||||
slug: widget.author.authorSlug,
|
slug: widget.author.authorSlug,
|
||||||
);
|
);
|
||||||
final chatIdOrError = await sl<ChatRepository>().getChatByUserSlug(widget.author.authorSlug);
|
final chatIdOrError = await sl<ChatRepository>().getChatByUserSlug(widget.author.authorSlug);
|
||||||
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
if (mounted) {
|
||||||
'chatScreen',
|
AppRouter.navigatorKey.currentContext?.pushReplacementNamed(
|
||||||
extra: {
|
'chatScreen',
|
||||||
'uuid': chatIdOrError,
|
extra: {
|
||||||
},
|
'uuid': chatIdOrError,
|
||||||
);
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: const Row(
|
child: const Row(
|
||||||
|
|||||||
@@ -25,24 +25,16 @@ class _LastStepScreenState extends State<LastStepScreen> {
|
|||||||
children: [
|
children: [
|
||||||
const Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: Text(
|
child: Text('Последний шаг', style: TextStyle(fontSize: 24, fontWeight: FontWeight.w700, height: 1.1)),
|
||||||
'Последний шаг',
|
|
||||||
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: const Text(
|
title: Text(
|
||||||
'Я ознакомлен и принимаю договор публичной офёрты',
|
hidePlans
|
||||||
style: TextStyle(
|
? 'Я ознакомлен и принимаю политику конфиденциальности и защиту персональных данных'
|
||||||
fontSize: 18,
|
: 'Я ознакомлен и принимаю договор публичной офёрты',
|
||||||
height: 1,
|
style: const TextStyle(fontSize: 18, height: 1),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
onChanged: (_) {
|
onChanged: (_) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -53,25 +45,14 @@ 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(padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), child: _divider(context)),
|
||||||
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('$baseUrl/oferta_bs.pdf')),
|
onPressed: () async => await launchUrl(Uri.parse(hidePlans ? '$baseUrl/docs/privacy.pdf' : '$baseUrl/oferta_bs.pdf')),
|
||||||
child: const Text(
|
child: Text(
|
||||||
'Договор офёрты',
|
hidePlans ? 'Политика конфиденциальности и пользовательское соглашение' : 'Договор офёрты',
|
||||||
style: TextStyle(
|
style: const TextStyle(height: 1, fontSize: 16, color: Color(0xFF323F4B), decoration: TextDecoration.underline),
|
||||||
height: 1,
|
|
||||||
fontSize: 16,
|
|
||||||
color: Color(0xFF323F4B),
|
|
||||||
decoration: TextDecoration.underline,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -84,10 +65,7 @@ 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(
|
decoration: BoxDecoration(color: Colors.black26, borderRadius: BorderRadius.circular(16)),
|
||||||
color: Colors.black26,
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ 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);
|
||||||
|
|||||||
@@ -31,9 +31,7 @@ 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
|
||||||
: state.consultationFilterStatus == ConsultationFilterStatus.inProgress
|
: FiltersComparisonEnum.equal,
|
||||||
? FiltersComparisonEnum.notInArray
|
|
||||||
: FiltersComparisonEnum.equal,
|
|
||||||
);
|
);
|
||||||
emit(state.copyWith(consultations: consultations, isLoading: false));
|
emit(state.copyWith(consultations: consultations, isLoading: false));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -67,20 +65,17 @@ 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',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ class _ConsultationsArchiveScreenState extends State<ConsultationsArchiveScreen>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
state.consultations[index].startDate.format('yyyy.MM.dd'),
|
state.consultations[index].startDate.format('dd.MM.yy'),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Color(0xFF6D6F71),
|
color: Color(0xFF6D6F71),
|
||||||
height: 1,
|
height: 1,
|
||||||
|
|||||||
@@ -156,7 +156,13 @@ class _AppInfoState extends State<AppInfo> {
|
|||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsetsGeometry.only(left: 16),
|
padding: const EdgeInsetsGeometry.only(left: 16),
|
||||||
child: Text('Версия приложения: $version ($buildNumber)', style: TextStyles.textXSSemibold.copyWith(color: appTheme.gray)),
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Версия приложения: $version ($buildNumber)', style: TextStyles.textXSSemibold.copyWith(color: appTheme.gray)),
|
||||||
|
if (buildDate != '') Text('Дата сборки: $buildDate', style: TextStyles.textXSSemibold.copyWith(color: appTheme.gray)),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -538,6 +538,297 @@ class EntityCommented implements BaseNotificationModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PlanSubscriptionExpiredNotification implements BaseNotificationModel {
|
||||||
|
@override
|
||||||
|
final NotificationType type;
|
||||||
|
@override
|
||||||
|
final bool isRead;
|
||||||
|
final String planName;
|
||||||
|
final String customerName;
|
||||||
|
@override
|
||||||
|
final DateTime createdAt;
|
||||||
|
|
||||||
|
PlanSubscriptionExpiredNotification({
|
||||||
|
required this.type,
|
||||||
|
required this.planName,
|
||||||
|
required this.customerName,
|
||||||
|
required this.isRead,
|
||||||
|
required this.createdAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory PlanSubscriptionExpiredNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
||||||
|
return PlanSubscriptionExpiredNotification(
|
||||||
|
type: type,
|
||||||
|
planName: json['plan_name'],
|
||||||
|
customerName: json['user_name'],
|
||||||
|
isRead: isRead ?? false,
|
||||||
|
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PlanSubscriptionRenewedNotification implements BaseNotificationModel {
|
||||||
|
@override
|
||||||
|
final NotificationType type;
|
||||||
|
@override
|
||||||
|
final bool isRead;
|
||||||
|
final String planName;
|
||||||
|
final String customerName;
|
||||||
|
final String renewalDate;
|
||||||
|
@override
|
||||||
|
final DateTime createdAt;
|
||||||
|
|
||||||
|
PlanSubscriptionRenewedNotification({
|
||||||
|
required this.type,
|
||||||
|
required this.planName,
|
||||||
|
required this.customerName,
|
||||||
|
required this.renewalDate,
|
||||||
|
required this.isRead,
|
||||||
|
required this.createdAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory PlanSubscriptionRenewedNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
||||||
|
return PlanSubscriptionRenewedNotification(
|
||||||
|
type: type,
|
||||||
|
planName: json['plan_name'],
|
||||||
|
customerName: json['user_name'],
|
||||||
|
renewalDate: json['renewal_date'],
|
||||||
|
isRead: isRead ?? false,
|
||||||
|
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConsultantAssignedNotification implements BaseNotificationModel {
|
||||||
|
@override
|
||||||
|
final NotificationType type;
|
||||||
|
@override
|
||||||
|
final bool isRead;
|
||||||
|
final String consultantName;
|
||||||
|
final String planName;
|
||||||
|
final int planId;
|
||||||
|
@override
|
||||||
|
final DateTime createdAt;
|
||||||
|
|
||||||
|
ConsultantAssignedNotification({
|
||||||
|
required this.type,
|
||||||
|
required this.consultantName,
|
||||||
|
required this.planName,
|
||||||
|
required this.planId,
|
||||||
|
required this.isRead,
|
||||||
|
required this.createdAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory ConsultantAssignedNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
||||||
|
return ConsultantAssignedNotification(
|
||||||
|
type: type,
|
||||||
|
consultantName: json['consultant_name'],
|
||||||
|
planName: json['plan_name'],
|
||||||
|
planId: json['plan_id'],
|
||||||
|
isRead: isRead ?? false,
|
||||||
|
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConsultantUnassignedNotification implements BaseNotificationModel {
|
||||||
|
@override
|
||||||
|
final NotificationType type;
|
||||||
|
@override
|
||||||
|
final bool isRead;
|
||||||
|
final String consultantName;
|
||||||
|
final String planName;
|
||||||
|
final int planId;
|
||||||
|
@override
|
||||||
|
final DateTime createdAt;
|
||||||
|
|
||||||
|
ConsultantUnassignedNotification({
|
||||||
|
required this.type,
|
||||||
|
required this.consultantName,
|
||||||
|
required this.planName,
|
||||||
|
required this.planId,
|
||||||
|
required this.isRead,
|
||||||
|
required this.createdAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory ConsultantUnassignedNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
||||||
|
return ConsultantUnassignedNotification(
|
||||||
|
type: type,
|
||||||
|
consultantName: json['consultant_name'],
|
||||||
|
planName: json['plan_name'],
|
||||||
|
planId: json['plan_id'],
|
||||||
|
isRead: isRead ?? false,
|
||||||
|
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PaymentFailedNotification implements BaseNotificationModel {
|
||||||
|
@override
|
||||||
|
final NotificationType type;
|
||||||
|
@override
|
||||||
|
final bool isRead;
|
||||||
|
final String planName;
|
||||||
|
final String errorMessage;
|
||||||
|
final String paymentMethod;
|
||||||
|
@override
|
||||||
|
final DateTime createdAt;
|
||||||
|
|
||||||
|
PaymentFailedNotification({
|
||||||
|
required this.type,
|
||||||
|
required this.planName,
|
||||||
|
required this.errorMessage,
|
||||||
|
required this.paymentMethod,
|
||||||
|
required this.isRead,
|
||||||
|
required this.createdAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory PaymentFailedNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
||||||
|
return PaymentFailedNotification(
|
||||||
|
type: type,
|
||||||
|
planName: json['plan_name'],
|
||||||
|
errorMessage: json['error_message'],
|
||||||
|
paymentMethod: json['payment_method'],
|
||||||
|
isRead: isRead ?? false,
|
||||||
|
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PaymentSuccessfulNotification implements BaseNotificationModel {
|
||||||
|
@override
|
||||||
|
final NotificationType type;
|
||||||
|
@override
|
||||||
|
final bool isRead;
|
||||||
|
final String planName;
|
||||||
|
final double amount;
|
||||||
|
final String currency;
|
||||||
|
final String transactionId;
|
||||||
|
@override
|
||||||
|
final DateTime createdAt;
|
||||||
|
|
||||||
|
PaymentSuccessfulNotification({
|
||||||
|
required this.type,
|
||||||
|
required this.planName,
|
||||||
|
required this.amount,
|
||||||
|
required this.currency,
|
||||||
|
required this.transactionId,
|
||||||
|
required this.isRead,
|
||||||
|
required this.createdAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory PaymentSuccessfulNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
||||||
|
return PaymentSuccessfulNotification(
|
||||||
|
type: type,
|
||||||
|
planName: json['plan_name'],
|
||||||
|
amount: (json['amount'] as num).toDouble(),
|
||||||
|
currency: json['currency'],
|
||||||
|
transactionId: json['transaction_id'],
|
||||||
|
isRead: isRead ?? false,
|
||||||
|
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GoalAchievedNotification implements BaseNotificationModel {
|
||||||
|
@override
|
||||||
|
final NotificationType type;
|
||||||
|
@override
|
||||||
|
final bool isRead;
|
||||||
|
final String goalName;
|
||||||
|
final String goalType;
|
||||||
|
final double achievedValue;
|
||||||
|
final double targetValue;
|
||||||
|
@override
|
||||||
|
final DateTime createdAt;
|
||||||
|
|
||||||
|
GoalAchievedNotification({
|
||||||
|
required this.type,
|
||||||
|
required this.goalName,
|
||||||
|
required this.goalType,
|
||||||
|
required this.achievedValue,
|
||||||
|
required this.targetValue,
|
||||||
|
required this.isRead,
|
||||||
|
required this.createdAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory GoalAchievedNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
||||||
|
return GoalAchievedNotification(
|
||||||
|
type: type,
|
||||||
|
goalName: json['goal_name'],
|
||||||
|
goalType: json['goal_type'],
|
||||||
|
achievedValue: (json['achieved_value'] as num).toDouble(),
|
||||||
|
targetValue: (json['target_value'] as num).toDouble(),
|
||||||
|
isRead: isRead ?? false,
|
||||||
|
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WorkoutReminderNotification implements BaseNotificationModel {
|
||||||
|
@override
|
||||||
|
final NotificationType type;
|
||||||
|
@override
|
||||||
|
final bool isRead;
|
||||||
|
final String workoutName;
|
||||||
|
final String workoutType;
|
||||||
|
final int duration;
|
||||||
|
@override
|
||||||
|
final DateTime createdAt;
|
||||||
|
|
||||||
|
WorkoutReminderNotification({
|
||||||
|
required this.type,
|
||||||
|
required this.workoutName,
|
||||||
|
required this.workoutType,
|
||||||
|
required this.duration,
|
||||||
|
required this.isRead,
|
||||||
|
required this.createdAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory WorkoutReminderNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
||||||
|
return WorkoutReminderNotification(
|
||||||
|
type: type,
|
||||||
|
workoutName: json['workout_name'],
|
||||||
|
workoutType: json['workout_type'],
|
||||||
|
duration: json['duration'],
|
||||||
|
isRead: isRead ?? false,
|
||||||
|
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MealReminderNotification implements BaseNotificationModel {
|
||||||
|
@override
|
||||||
|
final NotificationType type;
|
||||||
|
@override
|
||||||
|
final bool isRead;
|
||||||
|
final String mealName;
|
||||||
|
final String mealType;
|
||||||
|
final int calories;
|
||||||
|
@override
|
||||||
|
final DateTime createdAt;
|
||||||
|
|
||||||
|
MealReminderNotification({
|
||||||
|
required this.type,
|
||||||
|
required this.mealName,
|
||||||
|
required this.mealType,
|
||||||
|
required this.calories,
|
||||||
|
required this.isRead,
|
||||||
|
required this.createdAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory MealReminderNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
|
||||||
|
return MealReminderNotification(
|
||||||
|
type: type,
|
||||||
|
mealName: json['meal_name'],
|
||||||
|
mealType: json['meal_type'],
|
||||||
|
calories: json['calories'],
|
||||||
|
isRead: isRead ?? false,
|
||||||
|
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class UnknownNotification implements BaseNotificationModel {
|
class UnknownNotification implements BaseNotificationModel {
|
||||||
@override
|
@override
|
||||||
final NotificationType type;
|
final NotificationType type;
|
||||||
@@ -570,6 +861,16 @@ enum NotificationType {
|
|||||||
commentReplied,
|
commentReplied,
|
||||||
scoreAdded,
|
scoreAdded,
|
||||||
entityCommented,
|
entityCommented,
|
||||||
|
// Дополнительные типы уведомлений
|
||||||
|
planSubscriptionExpired,
|
||||||
|
planSubscriptionRenewed,
|
||||||
|
consultantAssigned,
|
||||||
|
consultantUnassigned,
|
||||||
|
paymentFailed,
|
||||||
|
paymentSuccessful,
|
||||||
|
goalAchieved,
|
||||||
|
workoutReminder,
|
||||||
|
mealReminder,
|
||||||
unknown;
|
unknown;
|
||||||
|
|
||||||
static NotificationType getTypeFromString(String type) => switch (type) {
|
static NotificationType getTypeFromString(String type) => switch (type) {
|
||||||
@@ -593,6 +894,16 @@ enum NotificationType {
|
|||||||
'App\\Notifications\\Comment\\Replied' => NotificationType.commentReplied,
|
'App\\Notifications\\Comment\\Replied' => NotificationType.commentReplied,
|
||||||
'App\\Notifications\\Score\\ScoreAdded' => NotificationType.scoreAdded,
|
'App\\Notifications\\Score\\ScoreAdded' => NotificationType.scoreAdded,
|
||||||
'App\\Notifications\\Comment\\EntityCommented' => NotificationType.entityCommented,
|
'App\\Notifications\\Comment\\EntityCommented' => NotificationType.entityCommented,
|
||||||
|
// Дополнительные маппинги
|
||||||
|
'App\\Notifications\\Plan\\PlanSubscriptionExpired' => NotificationType.planSubscriptionExpired,
|
||||||
|
'App\\Notifications\\Plan\\PlanSubscriptionRenewed' => NotificationType.planSubscriptionRenewed,
|
||||||
|
'App\\Notifications\\Plan\\ConsultantAssigned' => NotificationType.consultantAssigned,
|
||||||
|
'App\\Notifications\\Plan\\ConsultantUnassigned' => NotificationType.consultantUnassigned,
|
||||||
|
'App\\Notifications\\Payment\\PaymentFailed' => NotificationType.paymentFailed,
|
||||||
|
'App\\Notifications\\Payment\\PaymentSuccessful' => NotificationType.paymentSuccessful,
|
||||||
|
'App\\Notifications\\Goal\\GoalAchieved' => NotificationType.goalAchieved,
|
||||||
|
'App\\Notifications\\Workout\\WorkoutReminder' => NotificationType.workoutReminder,
|
||||||
|
'App\\Notifications\\Meal\\MealReminder' => NotificationType.mealReminder,
|
||||||
_ => NotificationType.unknown,
|
_ => NotificationType.unknown,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,6 +113,16 @@ class NotificationsCubit extends Cubit<NotificationsState> {
|
|||||||
NotificationType.commentReplied => CommentReplied.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.scoreAdded => ScoreAdded.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
|
||||||
NotificationType.entityCommented => EntityCommented.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
|
NotificationType.entityCommented => EntityCommented.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
|
||||||
|
// Новые типы уведомлений
|
||||||
|
NotificationType.planSubscriptionExpired => PlanSubscriptionExpiredNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
|
||||||
|
NotificationType.planSubscriptionRenewed => PlanSubscriptionRenewedNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
|
||||||
|
NotificationType.consultantAssigned => ConsultantAssignedNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
|
||||||
|
NotificationType.consultantUnassigned => ConsultantUnassignedNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
|
||||||
|
NotificationType.paymentFailed => PaymentFailedNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
|
||||||
|
NotificationType.paymentSuccessful => PaymentSuccessfulNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
|
||||||
|
NotificationType.goalAchieved => GoalAchievedNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
|
||||||
|
NotificationType.workoutReminder => WorkoutReminderNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
|
||||||
|
NotificationType.mealReminder => MealReminderNotification.fromJson(notificationJson, type, isRead: isRead, createdAt: createdAt),
|
||||||
NotificationType.unknown => UnknownNotification(type: type),
|
NotificationType.unknown => UnknownNotification(type: type),
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -47,6 +47,16 @@ class NotificationWidget extends StatelessWidget {
|
|||||||
CommentReplied() => _CommentRepliedWidget(notification: notification as CommentReplied),
|
CommentReplied() => _CommentRepliedWidget(notification: notification as CommentReplied),
|
||||||
ScoreAdded() => _ScoreAddedWidget(notification: notification as ScoreAdded),
|
ScoreAdded() => _ScoreAddedWidget(notification: notification as ScoreAdded),
|
||||||
EntityCommented() => _EntityCommentedWidget(notification: notification as EntityCommented),
|
EntityCommented() => _EntityCommentedWidget(notification: notification as EntityCommented),
|
||||||
|
// Новые типы уведомлений
|
||||||
|
PlanSubscriptionExpiredNotification() => _PlanSubscriptionExpiredWidget(notification: notification as PlanSubscriptionExpiredNotification),
|
||||||
|
PlanSubscriptionRenewedNotification() => _PlanSubscriptionRenewedWidget(notification: notification as PlanSubscriptionRenewedNotification),
|
||||||
|
ConsultantAssignedNotification() => _ConsultantAssignedWidget(notification: notification as ConsultantAssignedNotification),
|
||||||
|
ConsultantUnassignedNotification() => _ConsultantUnassignedWidget(notification: notification as ConsultantUnassignedNotification),
|
||||||
|
PaymentFailedNotification() => _PaymentFailedWidget(notification: notification as PaymentFailedNotification),
|
||||||
|
PaymentSuccessfulNotification() => _PaymentSuccessfulWidget(notification: notification as PaymentSuccessfulNotification),
|
||||||
|
GoalAchievedNotification() => _GoalAchievedWidget(notification: notification as GoalAchievedNotification),
|
||||||
|
WorkoutReminderNotification() => _WorkoutReminderWidget(notification: notification as WorkoutReminderNotification),
|
||||||
|
MealReminderNotification() => _MealReminderWidget(notification: notification as MealReminderNotification),
|
||||||
UnknownNotification() => _UnknownNotificationWidget(notification: notification as UnknownNotification),
|
UnknownNotification() => _UnknownNotificationWidget(notification: notification as UnknownNotification),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -443,3 +453,121 @@ class _UnknownNotificationWidget extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Новые виджеты для дополнительных типов уведомлений
|
||||||
|
class _PlanSubscriptionExpiredWidget extends StatelessWidget {
|
||||||
|
const _PlanSubscriptionExpiredWidget({required this.notification});
|
||||||
|
final PlanSubscriptionExpiredNotification notification;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return _BaseNotificationWidget(
|
||||||
|
isRead: notification.isRead,
|
||||||
|
notificationsText: 'Подписка на план "${notification.planName}" истекла',
|
||||||
|
createdAt: notification.createdAt,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PlanSubscriptionRenewedWidget extends StatelessWidget {
|
||||||
|
const _PlanSubscriptionRenewedWidget({required this.notification});
|
||||||
|
final PlanSubscriptionRenewedNotification notification;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return _BaseNotificationWidget(
|
||||||
|
isRead: notification.isRead,
|
||||||
|
notificationsText: 'Подписка на план "${notification.planName}" продлена до ${notification.renewalDate}',
|
||||||
|
createdAt: notification.createdAt,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ConsultantAssignedWidget extends StatelessWidget {
|
||||||
|
const _ConsultantAssignedWidget({required this.notification});
|
||||||
|
final ConsultantAssignedNotification notification;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return _BaseNotificationWidget(
|
||||||
|
isRead: notification.isRead,
|
||||||
|
notificationsText: 'К плану "${notification.planName}" назначен консультант ${notification.consultantName}',
|
||||||
|
createdAt: notification.createdAt,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ConsultantUnassignedWidget extends StatelessWidget {
|
||||||
|
const _ConsultantUnassignedWidget({required this.notification});
|
||||||
|
final ConsultantUnassignedNotification notification;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return _BaseNotificationWidget(
|
||||||
|
isRead: notification.isRead,
|
||||||
|
notificationsText: 'Консультант ${notification.consultantName} отстранен от плана "${notification.planName}"',
|
||||||
|
createdAt: notification.createdAt,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PaymentFailedWidget extends StatelessWidget {
|
||||||
|
const _PaymentFailedWidget({required this.notification});
|
||||||
|
final PaymentFailedNotification notification;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return _BaseNotificationWidget(
|
||||||
|
isRead: notification.isRead,
|
||||||
|
notificationsText: 'Ошибка оплаты плана "${notification.planName}": ${notification.errorMessage}',
|
||||||
|
createdAt: notification.createdAt,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PaymentSuccessfulWidget extends StatelessWidget {
|
||||||
|
const _PaymentSuccessfulWidget({required this.notification});
|
||||||
|
final PaymentSuccessfulNotification notification;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return _BaseNotificationWidget(
|
||||||
|
isRead: notification.isRead,
|
||||||
|
notificationsText: 'Успешная оплата плана "${notification.planName}" на сумму ${notification.amount} ${notification.currency}',
|
||||||
|
createdAt: notification.createdAt,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _GoalAchievedWidget extends StatelessWidget {
|
||||||
|
const _GoalAchievedWidget({required this.notification});
|
||||||
|
final GoalAchievedNotification notification;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return _BaseNotificationWidget(
|
||||||
|
isRead: notification.isRead,
|
||||||
|
notificationsText: 'Достигнута цель "${notification.goalName}": ${notification.achievedValue}/${notification.targetValue}',
|
||||||
|
createdAt: notification.createdAt,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _WorkoutReminderWidget extends StatelessWidget {
|
||||||
|
const _WorkoutReminderWidget({required this.notification});
|
||||||
|
final WorkoutReminderNotification notification;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return _BaseNotificationWidget(
|
||||||
|
isRead: notification.isRead,
|
||||||
|
notificationsText: 'Напоминание о тренировке "${notification.workoutName}" (${notification.duration} мин)',
|
||||||
|
createdAt: notification.createdAt,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MealReminderWidget extends StatelessWidget {
|
||||||
|
const _MealReminderWidget({required this.notification});
|
||||||
|
final MealReminderNotification notification;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return _BaseNotificationWidget(
|
||||||
|
isRead: notification.isRead,
|
||||||
|
notificationsText: 'Напоминание о приеме пищи "${notification.mealName}" (${notification.calories} ккал)',
|
||||||
|
createdAt: notification.createdAt,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -119,6 +119,61 @@ class NotificationsService {
|
|||||||
NotificationType.commentReplied => CommentReplied.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.scoreAdded => ScoreAdded.fromJson(notificationJson['data'], type, isRead: readDate != null, createdAt: createdAt),
|
||||||
NotificationType.entityCommented => EntityCommented.fromJson(notificationJson['data'], type, isRead: readDate != null, createdAt: createdAt),
|
NotificationType.entityCommented => EntityCommented.fromJson(notificationJson['data'], type, isRead: readDate != null, createdAt: createdAt),
|
||||||
|
// Новые типы уведомлений
|
||||||
|
NotificationType.planSubscriptionExpired => PlanSubscriptionExpiredNotification.fromJson(
|
||||||
|
notificationJson['data'],
|
||||||
|
type,
|
||||||
|
isRead: readDate != null,
|
||||||
|
createdAt: createdAt,
|
||||||
|
),
|
||||||
|
NotificationType.planSubscriptionRenewed => PlanSubscriptionRenewedNotification.fromJson(
|
||||||
|
notificationJson['data'],
|
||||||
|
type,
|
||||||
|
isRead: readDate != null,
|
||||||
|
createdAt: createdAt,
|
||||||
|
),
|
||||||
|
NotificationType.consultantAssigned => ConsultantAssignedNotification.fromJson(
|
||||||
|
notificationJson['data'],
|
||||||
|
type,
|
||||||
|
isRead: readDate != null,
|
||||||
|
createdAt: createdAt,
|
||||||
|
),
|
||||||
|
NotificationType.consultantUnassigned => ConsultantUnassignedNotification.fromJson(
|
||||||
|
notificationJson['data'],
|
||||||
|
type,
|
||||||
|
isRead: readDate != null,
|
||||||
|
createdAt: createdAt,
|
||||||
|
),
|
||||||
|
NotificationType.paymentFailed => PaymentFailedNotification.fromJson(
|
||||||
|
notificationJson['data'],
|
||||||
|
type,
|
||||||
|
isRead: readDate != null,
|
||||||
|
createdAt: createdAt,
|
||||||
|
),
|
||||||
|
NotificationType.paymentSuccessful => PaymentSuccessfulNotification.fromJson(
|
||||||
|
notificationJson['data'],
|
||||||
|
type,
|
||||||
|
isRead: readDate != null,
|
||||||
|
createdAt: createdAt,
|
||||||
|
),
|
||||||
|
NotificationType.goalAchieved => GoalAchievedNotification.fromJson(
|
||||||
|
notificationJson['data'],
|
||||||
|
type,
|
||||||
|
isRead: readDate != null,
|
||||||
|
createdAt: createdAt,
|
||||||
|
),
|
||||||
|
NotificationType.workoutReminder => WorkoutReminderNotification.fromJson(
|
||||||
|
notificationJson['data'],
|
||||||
|
type,
|
||||||
|
isRead: readDate != null,
|
||||||
|
createdAt: createdAt,
|
||||||
|
),
|
||||||
|
NotificationType.mealReminder => MealReminderNotification.fromJson(
|
||||||
|
notificationJson['data'],
|
||||||
|
type,
|
||||||
|
isRead: readDate != null,
|
||||||
|
createdAt: createdAt,
|
||||||
|
),
|
||||||
NotificationType.unknown => UnknownNotification(type: type, createdAt: parseDate(createdAt)),
|
NotificationType.unknown => UnknownNotification(type: type, createdAt: parseDate(createdAt)),
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ abstract class ProfileModel with _$ProfileModel {
|
|||||||
@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_blocked_by_me", fromJson: parseBool) required bool isBlockedByMe,
|
||||||
|
@JsonKey(name: "is_blocked_by_user", fromJson: parseBool, defaultValue: false) required bool isBlockedByUser,
|
||||||
@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,
|
||||||
|
|||||||
@@ -93,6 +93,39 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
|||||||
}
|
}
|
||||||
isFirstFetch = false;
|
isFirstFetch = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Проверяем, заблокировал ли пользователь меня
|
||||||
|
if (state.profile.isBlockedByUser) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(),
|
||||||
|
body: Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(32.0),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.block,
|
||||||
|
size: 64,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'Пользователь ограничил доступ к своей странице',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return widget.author == null || widget.author!.authorIsMe ? _myProfileScreen(state) : _profileScreen(state);
|
return widget.author == null || widget.author!.authorIsMe ? _myProfileScreen(state) : _profileScreen(state);
|
||||||
} else if (state is ProfileStateError) {
|
} else if (state is ProfileStateError) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@@ -174,10 +207,10 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
|||||||
),
|
),
|
||||||
_subscriptionsAndSubscribers(state),
|
_subscriptionsAndSubscribers(state),
|
||||||
if (state.profile.badges.isNotEmpty) Padding(padding: const EdgeInsets.only(bottom: 16), child: _badges(state)),
|
if (state.profile.badges.isNotEmpty) Padding(padding: const EdgeInsets.only(bottom: 16), child: _badges(state)),
|
||||||
if (state.profile.roles.contains('consultant') && state.consultantInfo != null) _socialLinks(state),
|
if (state.profile.roles.contains('consultant') && state.consultantInfo != null && !state.profile.isBlockedByMe) _socialLinks(state),
|
||||||
if (state.profile.roles.contains('consultant') && state.consultantInfo != null) _education(state),
|
if (state.profile.roles.contains('consultant') && state.consultantInfo != null && !state.profile.isBlockedByMe) _education(state),
|
||||||
if (state.profile.roles.contains('consultant') && state.consultantInfo != null) _diplomasAndCertificates(state),
|
if (state.profile.roles.contains('consultant') && state.consultantInfo != null && !state.profile.isBlockedByMe) _diplomasAndCertificates(state),
|
||||||
if (state.profile.roles.contains('consultant') && state.consultantInfo != null && !hidePlans) _activePlans(state),
|
if (state.profile.roles.contains('consultant') && state.consultantInfo != null && !hidePlans && !state.profile.isBlockedByMe) _activePlans(state),
|
||||||
_blogPosts(state),
|
_blogPosts(state),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:eatvio_app/core/locator_service.dart';
|
import 'package:eatvio_app/core/locator_service.dart';
|
||||||
|
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||||
|
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||||
import 'package:eatvio_app/core/utils/toasts.dart';
|
import 'package:eatvio_app/core/utils/toasts.dart';
|
||||||
|
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
|
||||||
import 'package:eatvio_app/features/registration/presentation/bloc/verify_email_cubit/verify_email_cubit.dart';
|
import 'package:eatvio_app/features/registration/presentation/bloc/verify_email_cubit/verify_email_cubit.dart';
|
||||||
import 'package:eatvio_app/features/registration/presentation/bloc/verify_email_cubit/verify_email_state.dart';
|
import 'package:eatvio_app/features/registration/presentation/bloc/verify_email_cubit/verify_email_state.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -18,11 +23,14 @@ class _VerifyEmailScreenState extends State<VerifyEmailScreen> {
|
|||||||
final List<TextEditingController> _controllers = List.generate(4, (_) => TextEditingController());
|
final List<TextEditingController> _controllers = List.generate(4, (_) => TextEditingController());
|
||||||
String email = '';
|
String email = '';
|
||||||
late final GoRouter goRouter;
|
late final GoRouter goRouter;
|
||||||
|
late Timer _timer;
|
||||||
|
int _secondsRemaining = 60;
|
||||||
|
bool _canSendAgain = false;
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
goRouter = GoRouter.of(context);
|
goRouter = GoRouter.of(context);
|
||||||
|
_startTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -36,6 +44,23 @@ class _VerifyEmailScreenState extends State<VerifyEmailScreen> {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _startTimer() {
|
||||||
|
setState(() {
|
||||||
|
_canSendAgain = false;
|
||||||
|
_secondsRemaining = 60;
|
||||||
|
});
|
||||||
|
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||||
|
setState(() {
|
||||||
|
if (_secondsRemaining > 0) {
|
||||||
|
_secondsRemaining--;
|
||||||
|
} else {
|
||||||
|
_canSendAgain = true;
|
||||||
|
_timer.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void _checkAndSubmitCode(BuildContext context) {
|
void _checkAndSubmitCode(BuildContext context) {
|
||||||
final code = _controllers.map((c) => c.text).join();
|
final code = _controllers.map((c) => c.text).join();
|
||||||
if (code.length == 4) {
|
if (code.length == 4) {
|
||||||
@@ -74,18 +99,25 @@ class _VerifyEmailScreenState extends State<VerifyEmailScreen> {
|
|||||||
children: [
|
children: [
|
||||||
Text("Код отправлен на почту\n$email", textAlign: TextAlign.center),
|
Text("Код отправлен на почту\n$email", textAlign: TextAlign.center),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Row(
|
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: List.generate(4, (index) => _buildCodeField(context, index))),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
const SizedBox(height: 40),
|
||||||
children: List.generate(4, (index) => _buildCodeField(context, index)),
|
CustomOutlinedButton(
|
||||||
),
|
text: 'Запросить код повторно',
|
||||||
const SizedBox(height: 20),
|
height: 52,
|
||||||
const Text("Запросить код повторно", style: TextStyle(color: Colors.grey)),
|
isDisabled: !_canSendAgain,
|
||||||
TextButton(
|
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
|
||||||
onPressed: () {
|
buttonStyle: ButtonStyle(
|
||||||
context.read<VerifyEmailCubit>().sendEmailCode();
|
side: const WidgetStatePropertyAll(BorderSide.none),
|
||||||
|
backgroundColor: WidgetStatePropertyAll(_canSendAgain ? appTheme.blueA400 : appTheme.gray),
|
||||||
|
),
|
||||||
|
onPressed: () async {
|
||||||
|
await context.read<VerifyEmailCubit>().sendEmailCode();
|
||||||
|
_startTimer();
|
||||||
},
|
},
|
||||||
child: const Text("Не приходит код?"),
|
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
if (!_canSendAgain)
|
||||||
|
Text("Зпросить код повторно можно через $_secondsRemaining с.", style: TextStyles.textSMedium.copyWith(color: appTheme.gray)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -51,9 +51,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
builder: (BuildContext context, SettingsState state) {
|
builder: (BuildContext context, SettingsState state) {
|
||||||
if (state is SettingsStateLoaded) {
|
if (state is SettingsStateLoaded) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(title: const Text('Настройки')),
|
||||||
title: const Text('Настройки'),
|
|
||||||
),
|
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
@@ -64,8 +62,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
_notifications(state),
|
_notifications(state),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
_aboutEatvio(state),
|
if (!hidePlans) ...[_aboutEatvio(state), const SizedBox(height: 12)],
|
||||||
const SizedBox(height: 12),
|
|
||||||
_becomeConsultantButton(state),
|
_becomeConsultantButton(state),
|
||||||
_deleteAccountButton(state, settingsCubit),
|
_deleteAccountButton(state, settingsCubit),
|
||||||
],
|
],
|
||||||
@@ -76,16 +73,12 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
}
|
}
|
||||||
if (state is SettingsStateError) {
|
if (state is SettingsStateError) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(title: const Text('Настройки')),
|
||||||
title: const Text('Настройки'),
|
|
||||||
),
|
|
||||||
body: DataErrorWidget(onReload: () => settingsCubit.init()),
|
body: DataErrorWidget(onReload: () => settingsCubit.init()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(title: const Text('Настройки')),
|
||||||
title: const Text('Настройки'),
|
|
||||||
),
|
|
||||||
body: const Loader(),
|
body: const Loader(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -101,22 +94,13 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Дополнительаная информация',
|
'Дополнительаная информация',
|
||||||
style: TextStyle(
|
style: TextStyle(fontSize: 16, height: 1, color: Colors.grey, fontFamily: 'Mulish'),
|
||||||
fontSize: 16,
|
|
||||||
height: 1,
|
|
||||||
color: Colors.grey,
|
|
||||||
fontFamily: 'Mulish',
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_tile(
|
_tile(
|
||||||
onTap: () => GoRouter.of(context).pushNamed(
|
onTap: () => GoRouter.of(context).pushNamed(
|
||||||
'generalInfoScreen',
|
'generalInfoScreen',
|
||||||
extra: SettingsEntity(
|
extra: SettingsEntity(profileSettingsModel: state.profileSettings, profileDetailsModel: state.profileDetails, settingsCubit: settingsCubit),
|
||||||
profileSettingsModel: state.profileSettings,
|
|
||||||
profileDetailsModel: state.profileDetails,
|
|
||||||
settingsCubit: settingsCubit,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
title: 'Общая информация',
|
title: 'Общая информация',
|
||||||
),
|
),
|
||||||
@@ -124,11 +108,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
_tile(
|
_tile(
|
||||||
onTap: () => GoRouter.of(context).pushNamed(
|
onTap: () => GoRouter.of(context).pushNamed(
|
||||||
'physicalFormScreen',
|
'physicalFormScreen',
|
||||||
extra: SettingsEntity(
|
extra: SettingsEntity(profileSettingsModel: state.profileSettings, profileDetailsModel: state.profileDetails, settingsCubit: settingsCubit),
|
||||||
profileSettingsModel: state.profileSettings,
|
|
||||||
profileDetailsModel: state.profileDetails,
|
|
||||||
settingsCubit: settingsCubit,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
title: 'Физическая форма',
|
title: 'Физическая форма',
|
||||||
),
|
),
|
||||||
@@ -136,11 +116,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
_tile(
|
_tile(
|
||||||
onTap: () => GoRouter.of(context).pushNamed(
|
onTap: () => GoRouter.of(context).pushNamed(
|
||||||
'pressureScreen',
|
'pressureScreen',
|
||||||
extra: SettingsEntity(
|
extra: SettingsEntity(profileSettingsModel: state.profileSettings, profileDetailsModel: state.profileDetails, settingsCubit: settingsCubit),
|
||||||
profileSettingsModel: state.profileSettings,
|
|
||||||
profileDetailsModel: state.profileDetails,
|
|
||||||
settingsCubit: settingsCubit,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
title: 'Давление',
|
title: 'Давление',
|
||||||
),
|
),
|
||||||
@@ -148,11 +124,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
_tile(
|
_tile(
|
||||||
onTap: () => GoRouter.of(context).pushNamed(
|
onTap: () => GoRouter.of(context).pushNamed(
|
||||||
'otherInfoScreen',
|
'otherInfoScreen',
|
||||||
extra: SettingsEntity(
|
extra: SettingsEntity(profileSettingsModel: state.profileSettings, profileDetailsModel: state.profileDetails, settingsCubit: settingsCubit),
|
||||||
profileSettingsModel: state.profileSettings,
|
|
||||||
profileDetailsModel: state.profileDetails,
|
|
||||||
settingsCubit: settingsCubit,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
title: 'Прочая информация',
|
title: 'Прочая информация',
|
||||||
),
|
),
|
||||||
@@ -160,31 +132,17 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
_tile(
|
_tile(
|
||||||
onTap: () => GoRouter.of(context).pushNamed(
|
onTap: () => GoRouter.of(context).pushNamed(
|
||||||
'physicalActivityScreen',
|
'physicalActivityScreen',
|
||||||
extra: SettingsEntity(
|
extra: SettingsEntity(profileSettingsModel: state.profileSettings, profileDetailsModel: state.profileDetails, settingsCubit: settingsCubit),
|
||||||
profileSettingsModel: state.profileSettings,
|
|
||||||
profileDetailsModel: state.profileDetails,
|
|
||||||
settingsCubit: settingsCubit,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
title: 'Физическая нагрузка',
|
title: 'Физическая нагрузка',
|
||||||
),
|
),
|
||||||
_divider(context),
|
_divider(context),
|
||||||
_tile(
|
_tile(onTap: () => GoRouter.of(context).pushNamed('forgetPasswordScreen', extra: true), title: 'Сменить пароль'),
|
||||||
onTap: () => GoRouter.of(context).pushNamed(
|
|
||||||
'forgetPasswordScreen',
|
|
||||||
extra: true,
|
|
||||||
),
|
|
||||||
title: 'Сменить пароль',
|
|
||||||
),
|
|
||||||
_divider(context),
|
_divider(context),
|
||||||
_tile(
|
_tile(
|
||||||
onTap: () => GoRouter.of(context).pushNamed(
|
onTap: () => GoRouter.of(context).pushNamed(
|
||||||
'changeEmailScreen',
|
'changeEmailScreen',
|
||||||
extra: SettingsEntity(
|
extra: SettingsEntity(profileSettingsModel: state.profileSettings, profileDetailsModel: state.profileDetails, settingsCubit: settingsCubit),
|
||||||
profileSettingsModel: state.profileSettings,
|
|
||||||
profileDetailsModel: state.profileDetails,
|
|
||||||
settingsCubit: settingsCubit,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
title: 'Сменить Email',
|
title: 'Сменить Email',
|
||||||
),
|
),
|
||||||
@@ -198,14 +156,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
children: [
|
children: [
|
||||||
const Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: Text(
|
child: Text('Уведомления', style: TextStyle(fontSize: 16, height: 1, color: Colors.grey)),
|
||||||
'Уведомления',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
height: 1,
|
|
||||||
color: Colors.grey,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
_tile(
|
_tile(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
@@ -213,11 +164,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
updatesNotificationsSwitchState = !updatesNotificationsSwitchState;
|
updatesNotificationsSwitchState = !updatesNotificationsSwitchState;
|
||||||
});
|
});
|
||||||
settingsCubit.updateProfileSettings(
|
settingsCubit.updateProfileSettings(profileSettingsModel: state.profileSettings.copyWith(newUpdatesNotifications: updatesNotificationsSwitchState));
|
||||||
profileSettingsModel: state.profileSettings.copyWith(
|
|
||||||
newUpdatesNotifications: updatesNotificationsSwitchState,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
title: 'Уведомление о новых обновлениях',
|
title: 'Уведомление о новых обновлениях',
|
||||||
trailing: CupertinoSwitch(
|
trailing: CupertinoSwitch(
|
||||||
@@ -228,9 +175,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
updatesNotificationsSwitchState = newValue;
|
updatesNotificationsSwitchState = newValue;
|
||||||
});
|
});
|
||||||
settingsCubit.updateProfileSettings(
|
settingsCubit.updateProfileSettings(
|
||||||
profileSettingsModel: state.profileSettings.copyWith(
|
profileSettingsModel: state.profileSettings.copyWith(newUpdatesNotifications: updatesNotificationsSwitchState),
|
||||||
newUpdatesNotifications: updatesNotificationsSwitchState,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -243,9 +188,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
fillDiaryNotificationsSwitchState = !fillDiaryNotificationsSwitchState;
|
fillDiaryNotificationsSwitchState = !fillDiaryNotificationsSwitchState;
|
||||||
});
|
});
|
||||||
settingsCubit.updateProfileSettings(
|
settingsCubit.updateProfileSettings(
|
||||||
profileSettingsModel: state.profileSettings.copyWith(
|
profileSettingsModel: state.profileSettings.copyWith(diaryReminderNotifications: fillDiaryNotificationsSwitchState),
|
||||||
diaryReminderNotifications: fillDiaryNotificationsSwitchState,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
title: 'Напоминание о заполнении дневника',
|
title: 'Напоминание о заполнении дневника',
|
||||||
@@ -257,9 +200,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
fillDiaryNotificationsSwitchState = newValue;
|
fillDiaryNotificationsSwitchState = newValue;
|
||||||
});
|
});
|
||||||
settingsCubit.updateProfileSettings(
|
settingsCubit.updateProfileSettings(
|
||||||
profileSettingsModel: state.profileSettings.copyWith(
|
profileSettingsModel: state.profileSettings.copyWith(diaryReminderNotifications: fillDiaryNotificationsSwitchState),
|
||||||
diaryReminderNotifications: fillDiaryNotificationsSwitchState,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -271,11 +212,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
moodSwitchState = !moodSwitchState;
|
moodSwitchState = !moodSwitchState;
|
||||||
});
|
});
|
||||||
settingsCubit.updateProfileSettings(
|
settingsCubit.updateProfileSettings(profileSettingsModel: state.profileSettings.copyWith(isMoodFillable: moodSwitchState));
|
||||||
profileSettingsModel: state.profileSettings.copyWith(
|
|
||||||
isMoodFillable: moodSwitchState,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
title: 'Указывать “настроение” в заполнение дневника',
|
title: 'Указывать “настроение” в заполнение дневника',
|
||||||
trailing: CupertinoSwitch(
|
trailing: CupertinoSwitch(
|
||||||
@@ -285,11 +222,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
moodSwitchState = newValue;
|
moodSwitchState = newValue;
|
||||||
});
|
});
|
||||||
settingsCubit.updateProfileSettings(
|
settingsCubit.updateProfileSettings(profileSettingsModel: state.profileSettings.copyWith(isMoodFillable: moodSwitchState));
|
||||||
profileSettingsModel: state.profileSettings.copyWith(
|
|
||||||
isMoodFillable: moodSwitchState,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -300,11 +233,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
bedTimeSwitchState = !bedTimeSwitchState;
|
bedTimeSwitchState = !bedTimeSwitchState;
|
||||||
});
|
});
|
||||||
settingsCubit.updateProfileSettings(
|
settingsCubit.updateProfileSettings(profileSettingsModel: state.profileSettings.copyWith(isBedtimeFillable: bedTimeSwitchState));
|
||||||
profileSettingsModel: state.profileSettings.copyWith(
|
|
||||||
isBedtimeFillable: bedTimeSwitchState,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
title: 'Указывать “отход ко сну” в заполнение дневника',
|
title: 'Указывать “отход ко сну” в заполнение дневника',
|
||||||
trailing: CupertinoSwitch(
|
trailing: CupertinoSwitch(
|
||||||
@@ -314,11 +243,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
bedTimeSwitchState = newValue;
|
bedTimeSwitchState = newValue;
|
||||||
});
|
});
|
||||||
settingsCubit.updateProfileSettings(
|
settingsCubit.updateProfileSettings(profileSettingsModel: state.profileSettings.copyWith(isBedtimeFillable: bedTimeSwitchState));
|
||||||
profileSettingsModel: state.profileSettings.copyWith(
|
|
||||||
isBedtimeFillable: bedTimeSwitchState,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -332,24 +257,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
children: [
|
children: [
|
||||||
const Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: Text(
|
child: Text('О Eatvio', style: TextStyle(fontSize: 16, height: 1, color: Colors.grey)),
|
||||||
'О Eatvio',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
height: 1,
|
|
||||||
color: Colors.grey,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
_tile(
|
|
||||||
title: 'О компании',
|
|
||||||
onTap: () async => await launchUrl(Uri.parse('$baseUrl/ru/company-details')),
|
|
||||||
),
|
),
|
||||||
|
_tile(title: 'О компании', onTap: () async => await launchUrl(Uri.parse('$baseUrl/ru/company-details'))),
|
||||||
_divider(context),
|
_divider(context),
|
||||||
_tile(
|
_tile(title: 'Пользовательское соглашение', onTap: () async => await launchUrl(Uri.parse('$baseUrl/oferta_bs.pdf'))),
|
||||||
title: 'Пользовательское соглашение',
|
|
||||||
onTap: () async => await launchUrl(Uri.parse('$baseUrl/oferta_bs.pdf')),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -364,19 +276,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
child: Ink(
|
child: Ink(
|
||||||
height: 52,
|
height: 52,
|
||||||
width: MediaQuery.of(context).size.width - 16,
|
width: MediaQuery.of(context).size.width - 16,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(borderRadius: BorderRadius.circular(20), color: appTheme.blueA400),
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
color: appTheme.blueA400,
|
|
||||||
),
|
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: Text(
|
child: Text('Стать консультантом', style: TextStyle(color: Colors.white, height: 1, fontSize: 18)),
|
||||||
'Стать консультантом',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
height: 1,
|
|
||||||
fontSize: 18,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -388,19 +290,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
child: Ink(
|
child: Ink(
|
||||||
height: 52,
|
height: 52,
|
||||||
width: MediaQuery.of(context).size.width - 16,
|
width: MediaQuery.of(context).size.width - 16,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(borderRadius: BorderRadius.circular(20), color: const Color(0xFFEAEAEA)),
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
color: const Color(0xFFEAEAEA),
|
|
||||||
),
|
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: Text(
|
child: Text('Данные на проверке', style: TextStyle(color: Colors.black, height: 1, fontSize: 18)),
|
||||||
'Данные на проверке',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.black,
|
|
||||||
height: 1,
|
|
||||||
fontSize: 18,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -413,19 +305,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
child: Ink(
|
child: Ink(
|
||||||
height: 52,
|
height: 52,
|
||||||
width: MediaQuery.of(context).size.width - 16,
|
width: MediaQuery.of(context).size.width - 16,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(borderRadius: BorderRadius.circular(20), color: const Color(0x33FFCC00)),
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
color: const Color(0x33FFCC00),
|
|
||||||
),
|
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: Text(
|
child: Text('Заявка обработана', style: TextStyle(color: Colors.black, height: 1, fontSize: 18)),
|
||||||
'Заявка обработана',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.black,
|
|
||||||
height: 1,
|
|
||||||
fontSize: 18,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -444,57 +326,28 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
userRootNavigator: true,
|
userRootNavigator: true,
|
||||||
context: context,
|
context: context,
|
||||||
height: null,
|
height: null,
|
||||||
child: DeleteAccountBottomSheet(
|
child: DeleteAccountBottomSheet(cubit: settingsCubit),
|
||||||
cubit: settingsCubit,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
child: Ink(
|
child: Ink(
|
||||||
height: 52,
|
height: 52,
|
||||||
width: MediaQuery.of(context).size.width - 16,
|
width: MediaQuery.of(context).size.width - 16,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(borderRadius: BorderRadius.circular(20), color: const Color(0xFFEAEAEA)),
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
color: const Color(0xFFEAEAEA),
|
|
||||||
),
|
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: Text(
|
child: Text('Удалить аккаунт', style: TextStyle(color: Colors.black, height: 1, fontSize: 18)),
|
||||||
'Удалить аккаунт',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.black,
|
|
||||||
height: 1,
|
|
||||||
fontSize: 18,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ListTile _tile({
|
ListTile _tile({required String title, required Function() onTap, Widget? trailing, EdgeInsets? padding}) {
|
||||||
required String title,
|
|
||||||
required Function() onTap,
|
|
||||||
Widget? trailing,
|
|
||||||
EdgeInsets? padding,
|
|
||||||
}) {
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
contentPadding: padding,
|
contentPadding: padding,
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
title: Text(
|
title: Text(title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500, height: 1)),
|
||||||
title,
|
trailing: trailing ?? const Icon(Icons.arrow_forward_ios_rounded, size: 12, color: Colors.black38),
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
height: 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
trailing: trailing ??
|
|
||||||
const Icon(
|
|
||||||
Icons.arrow_forward_ios_rounded,
|
|
||||||
size: 12,
|
|
||||||
color: Colors.black38,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,10 +356,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
child: Container(
|
child: Container(
|
||||||
height: 0.4,
|
height: 0.4,
|
||||||
width: MediaQuery.of(context).size.width - 32,
|
width: MediaQuery.of(context).size.width - 32,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(color: Colors.black26, borderRadius: BorderRadius.circular(16)),
|
||||||
color: Colors.black26,
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -530,33 +380,23 @@ class _DeleteAccountBottomSheet extends State<DeleteAccountBottomSheet> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(
|
const SizedBox(height: 8),
|
||||||
height: 8,
|
|
||||||
),
|
|
||||||
const BottomSheetHandlerBar(),
|
const BottomSheetHandlerBar(),
|
||||||
const SizedBox(
|
const SizedBox(height: 20),
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
Text(s.accountDeleting, style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black)),
|
Text(s.accountDeleting, style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black)),
|
||||||
const SizedBox(
|
const SizedBox(height: 16),
|
||||||
height: 16,
|
|
||||||
),
|
|
||||||
Text(
|
Text(
|
||||||
s.youShureYouWantToDeleteAccount,
|
s.youShureYouWantToDeleteAccount,
|
||||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(height: 8),
|
||||||
height: 8,
|
|
||||||
),
|
|
||||||
Text(
|
Text(
|
||||||
s.itCantBeRecovered,
|
s.itCantBeRecovered,
|
||||||
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(height: 8),
|
||||||
height: 8,
|
|
||||||
),
|
|
||||||
CustomOutlinedButton(
|
CustomOutlinedButton(
|
||||||
text: s.delete,
|
text: s.delete,
|
||||||
height: 52,
|
height: 52,
|
||||||
@@ -573,9 +413,7 @@ class _DeleteAccountBottomSheet extends State<DeleteAccountBottomSheet> {
|
|||||||
showErrorDialog(context, description: s.cannotDeleteAccount);
|
showErrorDialog(context, description: s.cannotDeleteAccount);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(height: 40),
|
||||||
height: 40,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -69,15 +69,17 @@ class SupportScreen extends StatelessWidget {
|
|||||||
child: Text('Правовые документы', style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500)),
|
child: Text('Правовые документы', style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500)),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
InkWell(
|
if (!hidePlans) ...[
|
||||||
onTap: () async => await launchUrl(Uri.parse('$baseUrl/docs/public_offer.pdf')),
|
InkWell(
|
||||||
borderRadius: BorderRadius.circular(16),
|
onTap: () async => await launchUrl(Uri.parse('$baseUrl/docs/public_offer.pdf')),
|
||||||
child: const Padding(
|
borderRadius: BorderRadius.circular(16),
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
child: const Padding(
|
||||||
child: Text('Публичная офёрта', style: TextStyle(fontSize: 16)),
|
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||||
|
child: Text('Публичная офёрта', style: TextStyle(fontSize: 16)),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 8),
|
||||||
const SizedBox(height: 8),
|
],
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () async => await launchUrl(Uri.parse('$baseUrl/docs/privacy.pdf')),
|
onTap: () async => await launchUrl(Uri.parse('$baseUrl/docs/privacy.pdf')),
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
name: eatvio_app
|
name: eatvio_app
|
||||||
description: A new Flutter project.
|
description: A new Flutter project.
|
||||||
version: 1.2.8+68
|
version: 1.2.9+69
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ void main() {
|
|||||||
(_) async => await Future.value(
|
(_) async => await Future.value(
|
||||||
const ProfileModel(
|
const ProfileModel(
|
||||||
id: 0,
|
id: 0,
|
||||||
|
isBlockedByUser: false,
|
||||||
name: 'Alexander Avanesyan',
|
name: 'Alexander Avanesyan',
|
||||||
email: 'mso7nueh@mail.ru',
|
email: 'mso7nueh@mail.ru',
|
||||||
phoneConfirmationState: '',
|
phoneConfirmationState: '',
|
||||||
|
|||||||
Reference in New Issue
Block a user