diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 63d0dd02..b7e87af6 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -388,7 +388,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 25; + CURRENT_PROJECT_VERSION = 26; DEVELOPMENT_TEAM = VL68C36HYS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -538,7 +538,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 25; + CURRENT_PROJECT_VERSION = 26; DEVELOPMENT_TEAM = VL68C36HYS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -576,7 +576,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Distribution: ITVIO, OOO (VL68C36HYS)"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 25; + CURRENT_PROJECT_VERSION = 26; DEVELOPMENT_TEAM = VL68C36HYS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 2f074053..793b8e28 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 25 + 26 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS @@ -32,11 +32,11 @@ NSCameraUsageDescription - Нужно для загрузки фото + Предоставление доступа позволит Вам делать фотографии и добавлять к записям NSLocationWhenInUseUsageDescription NSPhotoLibraryUsageDescription - Нужно для загрузки фото + Предоставление доступа позволит Вам добавлять фото к записям UIApplicationSupportsIndirectInputEvents UIBackgroundModes diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart index c849628d..0bd17c58 100644 --- a/lib/core/app_router.dart +++ b/lib/core/app_router.dart @@ -1,5 +1,9 @@ +import 'dart:io'; +import 'package:device_info_plus/device_info_plus.dart'; import 'package:dio/dio.dart'; import 'package:eatvio_app/core/const.dart'; +import 'package:eatvio_app/core/firebase_api.dart'; +import 'package:eatvio_app/core/token_storage.dart'; import 'package:eatvio_app/core/websockets/ws_manager.dart'; import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/features/auth/presentation/pages/auth_screen.dart'; @@ -154,6 +158,23 @@ class AppRouter { name: 'welcomeScreen', builder: (context, state) => const WelcomeScreen(), redirect: (context, state) async { + try { + if (await TokenStorage.haveTokenInStorage()) { + final firebaseApi = sl(); + final fbToken = await firebaseApi.getToken(); + final deviceInfo = DeviceInfoPlugin(); + await sl().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, + }, + ); + } + } catch (e) { + rethrow; + } final sharedPreferences = await SharedPreferences.getInstance(); final welcomeScreenWasShowed = sharedPreferences.getBool('welcomeScreenWasShowed'); if (welcomeScreenWasShowed == true) { diff --git a/lib/core/const.dart b/lib/core/const.dart index 31c47e9c..618a9866 100644 --- a/lib/core/const.dart +++ b/lib/core/const.dart @@ -1,5 +1,4 @@ String baseUrl = 'https://eatvio.ru'; -// String baseUrl = 'https://dev2.eatvio.com'; const List durationValues = [7, 14, 21]; diff --git a/lib/core/firebase_api.dart b/lib/core/firebase_api.dart index f83d47d6..fbf8e5c1 100644 --- a/lib/core/firebase_api.dart +++ b/lib/core/firebase_api.dart @@ -1,11 +1,10 @@ import 'package:firebase_messaging/firebase_messaging.dart'; class FirebaseApi { - final _firebaseMessaging = FirebaseMessaging.instance; - Future getToken() async{ + Future getToken() async { await _firebaseMessaging.requestPermission(); return await _firebaseMessaging.getToken(); } -} \ No newline at end of file +} diff --git a/lib/core/locator_service.dart b/lib/core/locator_service.dart index cd8731a3..ad23cce1 100644 --- a/lib/core/locator_service.dart +++ b/lib/core/locator_service.dart @@ -1,6 +1,7 @@ import 'package:dio/dio.dart'; import 'package:eatvio_app/core/const.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/interceptors/token_interceptor.dart'; import 'package:eatvio_app/core/websockets/ws_manager.dart'; import 'package:eatvio_app/core/websockets/ws_service.dart'; @@ -204,6 +205,7 @@ Future init() async { sl.registerLazySingleton(() => CreatePlanService(sl())); sl.registerLazySingleton(() => NotificationsService(sl())); sl.registerLazySingleton(() => MyRecipesService(sl())); + sl.registerLazySingleton(() => FirebaseApi()); // External sl.registerLazySingleton( diff --git a/lib/features/auth/presentation/pages/auth_screen.dart b/lib/features/auth/presentation/pages/auth_screen.dart index 580b1f47..5455d5fd 100644 --- a/lib/features/auth/presentation/pages/auth_screen.dart +++ b/lib/features/auth/presentation/pages/auth_screen.dart @@ -1,6 +1,4 @@ import 'dart:async'; - -import 'package:easy_debounce/easy_debounce.dart'; import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; @@ -9,8 +7,10 @@ import 'package:eatvio_app/core/utils/toasts.dart'; import 'package:eatvio_app/core/websockets/ws_manager.dart'; import 'package:eatvio_app/core/widgets/custom_text_form_field.dart'; import 'package:eatvio_app/features/auth/presentation/bloc/auth_cubit.dart'; +import 'package:eatvio_app/features/auth/presentation/bloc/auth_state.dart'; import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart'; import 'package:eatvio_app/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart'; +import 'package:eatvio_app/features/dev_tools/presentation/dev_tools_screen.dart'; import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart'; import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/cupertino.dart'; @@ -23,8 +23,6 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:go_router/go_router.dart'; import 'package:url_launcher/url_launcher.dart'; -import '../bloc/auth_state.dart'; - class AuthScreen extends StatefulWidget { const AuthScreen({super.key}); @@ -41,6 +39,7 @@ class _AuthScreenState extends State { late StreamSubscription keyboardSubscription; bool isFocused = false; int tapsCount = 0; + @override void initState() { super.initState(); @@ -83,11 +82,8 @@ class _AuthScreenState extends State { tapsCount++; if (tapsCount == 6) { tapsCount = 0; - // showModalBottomSheet(context: context, builder: (context) => ); } - EasyDebounce.debounce('change env', Durations.medium4, () { - tapsCount = 0; - }); + GoRouter.of(context).pushNamed(DevToolsScreen.name); }, child: SvgPicture.asset( ImageConstant.imgLogoEatvio, @@ -153,7 +149,7 @@ class _AuthScreenState extends State { child: RichText( textAlign: TextAlign.center, text: TextSpan( - text: 'Нажимая "Войти", Вы соглашаетесь \n', + text: 'Нажимая "Войти" или "Зарегистрироваться", Вы соглашаетесь \n', style: const TextStyle( fontSize: 12, color: Colors.black, @@ -170,6 +166,17 @@ class _AuthScreenState extends State { ), recognizer: TapGestureRecognizer()..onTap = () async => await launchUrl(Uri.parse('$baseUrl/oferta_bs.pdf')), ), + const TextSpan(text: ' и '), + TextSpan( + text: 'Политикой конфиденциальности\nи защитой персональных данных', + style: TextStyle( + fontSize: 12, + color: appTheme.blueA400, + decorationColor: appTheme.blueA400, + decoration: TextDecoration.underline, + ), + recognizer: TapGestureRecognizer()..onTap = () async => await launchUrl(Uri.parse('$baseUrl/docs/privacy.pdf')), + ), ], ), ), diff --git a/lib/features/chat/models/chat_user_model.dart b/lib/features/chat/models/chat_user_model.dart index 14feb316..ad876727 100644 --- a/lib/features/chat/models/chat_user_model.dart +++ b/lib/features/chat/models/chat_user_model.dart @@ -9,7 +9,6 @@ abstract class ChatUserModel with _$ChatUserModel { required int id, required String name, required String slug, - @JsonKey(name: 'profile_photo_path') String? profilePhotoPath, @JsonKey(name: 'profile_photo_url') String? profilePhotoUrl, }) = _ChatUserModel; diff --git a/lib/features/chat/presentation/pages/chat_screen.dart b/lib/features/chat/presentation/pages/chat_screen.dart index 9cd3ff71..5ec98c6c 100644 --- a/lib/features/chat/presentation/pages/chat_screen.dart +++ b/lib/features/chat/presentation/pages/chat_screen.dart @@ -2,7 +2,6 @@ import 'dart:io'; import 'dart:math'; import 'package:easy_debounce/easy_debounce.dart'; -import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/widgets/custom_outlined_button.dart'; import 'package:eatvio_app/core/widgets/data_error_widget.dart'; import 'package:eatvio_app/core/widgets/loader.dart'; @@ -155,7 +154,7 @@ class _ChatScreenState extends State { if (state.chatRoom.users.receiver.profilePhotoUrl != null) CircleAvatar( radius: 20, - foregroundImage: NetworkImage(baseUrl + state.chatRoom.users.receiver.profilePhotoUrl!), + foregroundImage: NetworkImage(state.chatRoom.users.receiver.profilePhotoUrl!), backgroundImage: const AssetImage('assets/images/no_avatar.png'), ), if (state.chatRoom.users.receiver.profilePhotoUrl == null) diff --git a/lib/features/chat/presentation/widgets/chat_bubble_widget.dart b/lib/features/chat/presentation/widgets/chat_bubble_widget.dart index c0857612..b80b12ac 100644 --- a/lib/features/chat/presentation/widgets/chat_bubble_widget.dart +++ b/lib/features/chat/presentation/widgets/chat_bubble_widget.dart @@ -1,6 +1,5 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:dismissible_page/dismissible_page.dart'; -import 'package:eatvio_app/core/const.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/date_time_utils.dart'; @@ -81,7 +80,7 @@ class ChatBubbleWidget extends StatelessWidget { contentPadding: const EdgeInsets.all(4), horizontalTitleGap: 8, onTap: () async { - if (!await launchUrl(Uri.parse('$baseUrl/storage/${mediaData[index]}'))) { + if (!await launchUrl(Uri.parse('${mediaData[index]}'))) { throw Exception('Could not launch ${mediaData[index].toString()}'); } }, @@ -108,18 +107,18 @@ class ChatBubbleWidget extends StatelessWidget { onTap: () { context.pushTransparentRoute( PhotoViewScreen( - imageUrls: mediaData.map((e) => '$baseUrl/storage/$e').toList(), + imageUrls: mediaData.map((e) => '$e').toList(), index: index, ), ); }, child: Hero( - tag: '$baseUrl/storage/${mediaData[index]}', + tag: '${mediaData[index]}', transitionOnUserGestures: true, child: ClipRRect( borderRadius: BorderRadius.circular(16), child: CachedNetworkImage( - imageUrl: '$baseUrl/storage/${mediaData[index]}', + imageUrl: '${mediaData[index]}', ), ), ), diff --git a/lib/features/chat/presentation/widgets/chat_entry_widget.dart b/lib/features/chat/presentation/widgets/chat_entry_widget.dart index a6d5b3bb..1338c60b 100644 --- a/lib/features/chat/presentation/widgets/chat_entry_widget.dart +++ b/lib/features/chat/presentation/widgets/chat_entry_widget.dart @@ -1,4 +1,3 @@ -import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/utils/date_time_utils.dart'; import 'package:eatvio_app/features/chat/models/chat_room_pagination_model.dart'; import 'package:eatvio_app/core/theme/custom_text_style.dart'; @@ -42,7 +41,7 @@ class _ChatEntryWidgetState extends State { CircleAvatar( radius: 24, foregroundImage: NetworkImage( - baseUrl + widget.chatRoom.users.toChatUser.profilePhotoUrl, + widget.chatRoom.users.toChatUser.profilePhotoUrl, ), backgroundImage: const AssetImage('assets/images/no_avatar.png'), backgroundColor: Colors.white, diff --git a/lib/features/profile/presentation/pages/profile_screen.dart b/lib/features/profile/presentation/pages/profile_screen.dart index ef9aac1a..363c2152 100644 --- a/lib/features/profile/presentation/pages/profile_screen.dart +++ b/lib/features/profile/presentation/pages/profile_screen.dart @@ -440,12 +440,12 @@ class _ProfileScreenState extends State { padding: const EdgeInsets.only(bottom: 16), child: Column( children: [ - const Padding( - padding: EdgeInsets.symmetric(horizontal: 16), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( + const Text( 'Дипломы\nи сертификаты', style: TextStyle( color: Color(0xFF323F4B), @@ -454,19 +454,19 @@ class _ProfileScreenState extends State { fontWeight: FontWeight.w600, ), ), - // GestureDetector( - // onTap: () { - // GoRouter.of(context).pushNamed('stepperBecomeConsultantScreen', extra: state.profile.slug); - // }, - // child: Text( - // widget.author == null || widget.author!.authorIsMe ? 'Редактировать' : '', - // style: const TextStyle( - // color: Color(0xFF2F80ED), - // fontSize: 16, - // fontWeight: FontWeight.w500, - // ), - // ), - // ), + GestureDetector( + onTap: () { + GoRouter.of(context).pushNamed('stepperBecomeConsultantScreen', extra: state.profile.slug); + }, + child: Text( + widget.author == null || widget.author!.authorIsMe ? 'Редактировать' : '', + style: const TextStyle( + color: Color(0xFF2F80ED), + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + ), ], ), ), @@ -541,12 +541,12 @@ class _ProfileScreenState extends State { padding: const EdgeInsets.only(bottom: 16), child: Column( children: [ - const Padding( - padding: EdgeInsets.symmetric(horizontal: 16), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( + const Text( 'Образование', style: TextStyle( color: Color(0xFF323F4B), @@ -554,19 +554,19 @@ class _ProfileScreenState extends State { fontWeight: FontWeight.w600, ), ), - // GestureDetector( - // onTap: () { - // GoRouter.of(context).pushNamed('stepperBecomeConsultantScreen', extra: state.profile.slug); - // }, - // child: Text( - // widget.author == null || widget.author!.authorIsMe ? 'Редактировать' : '', - // style: const TextStyle( - // color: Color(0xFF2F80ED), - // fontSize: 16, - // fontWeight: FontWeight.w500, - // ), - // ), - // ), + GestureDetector( + onTap: () { + GoRouter.of(context).pushNamed('stepperBecomeConsultantScreen', extra: state.profile.slug); + }, + child: Text( + widget.author == null || widget.author!.authorIsMe ? 'Редактировать' : '', + style: const TextStyle( + color: Color(0xFF2F80ED), + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + ), ], ), ), diff --git a/lib/features/registration/presentation/bloc/registration_cubit/registration_cubit.dart b/lib/features/registration/presentation/bloc/registration_cubit/registration_cubit.dart index af748b69..1ccbde80 100644 --- a/lib/features/registration/presentation/bloc/registration_cubit/registration_cubit.dart +++ b/lib/features/registration/presentation/bloc/registration_cubit/registration_cubit.dart @@ -3,6 +3,7 @@ import 'dart:developer'; import 'package:eatvio_app/core/app_router.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/presentation/bloc/registration_cubit/registration_state.dart'; import 'package:eatvio_app/features/registration/repositories/registration_repository.dart'; @@ -50,17 +51,25 @@ class RegistrationCubit extends Cubit { updateSheet(UserRegistrationSheet sheet) => emit(RegistrationState(sheet: sheet)); Future tryRegistration() async { - try { - final token = await registrationRepository.tryRegister( - state.sheet.copyWith( - phone: state.sheet.phone.replaceAll(' ', '').replaceAll('(', '').replaceAll(')', ''), - responseBody: '{}', - ), + if (await registrationRepository.emailIsReachable(state.sheet.email)) { + try { + final token = await registrationRepository.tryRegister( + state.sheet.copyWith( + 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); - AppRouter.navigatorKey.currentContext?.pushNamed('verifyEmailScreen'); - } catch (e) { - log(e.toString()); } } diff --git a/lib/features/registration/repositories/registration_repository.dart b/lib/features/registration/repositories/registration_repository.dart index e5a32e94..7a4a14ae 100644 --- a/lib/features/registration/repositories/registration_repository.dart +++ b/lib/features/registration/repositories/registration_repository.dart @@ -1,3 +1,5 @@ +import 'package:dio/dio.dart'; +import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/features/registration/models/user_registration_model.dart'; import 'package:eatvio_app/features/registration/models/location_model.dart'; import 'package:eatvio_app/features/registration/services/registration_service.dart'; @@ -30,4 +32,17 @@ class RegistrationRepository { rethrow; } } + + Future emailIsReachable(String email) async { + try { + final response = await sl().getUri(Uri.parse('https://api.validate.email/validate?email=$email')); + if (response.data['result']['reachable'] != 'invalid') { + return true; + } else { + return false; + } + } catch (e) { + return false; + } + } } diff --git a/pubspec.yaml b/pubspec.yaml index 34560571..c9e7430b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: eatvio_app description: A new Flutter project. -version: 1.1.3+33 +version: 1.1.4+34 publish_to: 'none' environment: @@ -69,6 +69,7 @@ dependencies: shared_preferences: ^2.5.3 version: ^3.0.2 package_info_plus: ^8.3.0 + device_info_plus: ^11.4.0 dev_dependencies: build_runner: ^2.4.15