From 1e1005a09c8fedc8997b2b7da9c2c933dc1867a5 Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Fri, 23 May 2025 16:52:50 +0300 Subject: [PATCH 1/4] feat(email) add email verify screen, add my recipe screen, plans/index -> my_plans --- .gitea/workflows/build.yml | 2 +- .gitea/workflows/docker-run-tests.yml | 2 +- .gitea/workflows/test.yml | 2 +- ios/Flutter/ephemeral/flutter_lldb_helper.py | 32 +++ ios/Flutter/ephemeral/flutter_lldbinit | 5 + ios/Runner.xcodeproj/project.pbxproj | 6 +- ios/Runner/Info.plist | 2 +- lib/core/app_router.dart | 14 +- .../menu/presentation/pages/menu_screen.dart | 3 +- .../my_plans/services/my_plans_service.dart | 2 +- .../{ => pages}/about_recipe_screen.dart | 0 .../presentation/pages/my_recipes_screen.dart | 196 ++++++++++++++++++ .../presentation/widgets/my_recipes_tab.dart | 68 ++++++ .../pages/verify_email_screen.dart | 81 ++++++++ pubspec.yaml | 3 - 15 files changed, 404 insertions(+), 14 deletions(-) create mode 100644 ios/Flutter/ephemeral/flutter_lldb_helper.py create mode 100644 ios/Flutter/ephemeral/flutter_lldbinit rename lib/features/recipe/presentation/{ => pages}/about_recipe_screen.dart (100%) create mode 100644 lib/features/recipe/presentation/pages/my_recipes_screen.dart create mode 100644 lib/features/recipe/presentation/widgets/my_recipes_tab.dart create mode 100644 lib/features/registration/presentation/pages/verify_email_screen.dart diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 2d390f02..3abc813c 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -3,7 +3,7 @@ name: Eatvio App Android Test on: [ pull_request ] env: - flutter_version: "3.29.1" + flutter_version: "3.32.0" java_version: "21.x" flutter_channel: "stable" diff --git a/.gitea/workflows/docker-run-tests.yml b/.gitea/workflows/docker-run-tests.yml index cf14247c..85d14070 100644 --- a/.gitea/workflows/docker-run-tests.yml +++ b/.gitea/workflows/docker-run-tests.yml @@ -3,7 +3,7 @@ name: Eatvio App Android Test on custom docker on: [ pull_request ] env: - flutter_version: "3.29.1" + flutter_version: "3.32.0" java_version: "21.x" flutter_channel: "stable" my_local_cache_dir: "/opt/hostedtoolcache/" diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index 80825645..70ff32b8 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -3,7 +3,7 @@ name: Eatvio App Android Test on: [ pull_request ] env: - flutter_version: "3.29.3" + flutter_version: "3.32.0" java_version: "21.x" flutter_channel: "stable" my_local_cache_dir: "/opt/hostedtoolcache/" diff --git a/ios/Flutter/ephemeral/flutter_lldb_helper.py b/ios/Flutter/ephemeral/flutter_lldb_helper.py new file mode 100644 index 00000000..a88caf99 --- /dev/null +++ b/ios/Flutter/ephemeral/flutter_lldb_helper.py @@ -0,0 +1,32 @@ +# +# Generated file, do not edit. +# + +import lldb + +def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict): + """Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages.""" + base = frame.register["x0"].GetValueAsAddress() + page_len = frame.register["x1"].GetValueAsUnsigned() + + # Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the + # first page to see if handled it correctly. This makes diagnosing + # misconfiguration (e.g. missing breakpoint) easier. + data = bytearray(page_len) + data[0:8] = b'IHELPED!' + + error = lldb.SBError() + frame.GetThread().GetProcess().WriteMemory(base, data, error) + if not error.Success(): + print(f'Failed to write into {base}[+{page_len}]', error) + return + +def __lldb_init_module(debugger: lldb.SBDebugger, _): + target = debugger.GetDummyTarget() + # Caveat: must use BreakpointCreateByRegEx here and not + # BreakpointCreateByName. For some reasons callback function does not + # get carried over from dummy target for the later. + bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$") + bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__)) + bp.SetAutoContinue(True) + print("-- LLDB integration loaded --") diff --git a/ios/Flutter/ephemeral/flutter_lldbinit b/ios/Flutter/ephemeral/flutter_lldbinit new file mode 100644 index 00000000..e3ba6fbe --- /dev/null +++ b/ios/Flutter/ephemeral/flutter_lldbinit @@ -0,0 +1,5 @@ +# +# Generated file, do not edit. +# + +command script import --relative-to-command-file flutter_lldb_helper.py diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 2566b21b..ff6e8a89 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 = 16; + CURRENT_PROJECT_VERSION = 17; 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 = 16; + CURRENT_PROJECT_VERSION = 17; 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 = 16; + CURRENT_PROJECT_VERSION = 17; DEVELOPMENT_TEAM = VL68C36HYS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 24f859fd..90850ad3 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 16 + 17 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart index dbcea1bc..d8805f73 100644 --- a/lib/core/app_router.dart +++ b/lib/core/app_router.dart @@ -40,8 +40,9 @@ import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart'; import 'package:eatvio_app/features/plans/presentation/pages/about_plan_screen.dart'; import 'package:eatvio_app/features/profile/presentation/pages/profile_screen.dart'; import 'package:eatvio_app/features/profile/presentation/pages/subs_screen.dart'; -import 'package:eatvio_app/features/recipe/presentation/about_recipe_screen.dart'; +import 'package:eatvio_app/features/recipe/presentation/pages/about_recipe_screen.dart'; import 'package:eatvio_app/features/create_recipe/presentation/pages/recipe_create_screen.dart'; +import 'package:eatvio_app/features/recipe/presentation/pages/my_recipes_screen.dart'; import 'package:eatvio_app/features/registration/presentation/pages/registration_screen.dart'; import 'package:eatvio_app/features/registration/presentation/pages/verify_screen.dart'; import 'package:eatvio_app/features/settings/entities/settings_entity.dart'; @@ -77,7 +78,11 @@ class AppRouter { builder: (context, state) => const AuthScreen(), routes: [ // GoRoute(path: 'webView-registration-screen', name: 'webViewRegistrationScreen', builder: (context, state) => const WebViewRegistrationScreen()), - GoRoute(path: 'registration-screen', name: 'registrationScreen', builder: (context, state) => const RegistrationScreen()), + GoRoute( + path: 'registration-screen', + name: 'registrationScreen', + builder: (context, state) => const RegistrationScreen(), + ), ], ), ShellRoute( @@ -355,6 +360,11 @@ class AppRouter { name: 'verifyScreen', builder: (context, state) => VerifyScreen(passKey: state.extra as String), ), + GoRoute( + path: MyRecipesScreen.path, + name: MyRecipesScreen.name, + builder: (context, state) => const MyRecipesScreen(), + ), ], ); } diff --git a/lib/features/menu/presentation/pages/menu_screen.dart b/lib/features/menu/presentation/pages/menu_screen.dart index 255c8510..6ccb0973 100644 --- a/lib/features/menu/presentation/pages/menu_screen.dart +++ b/lib/features/menu/presentation/pages/menu_screen.dart @@ -8,6 +8,7 @@ import 'package:eatvio_app/features/dev_tools/presentation/dev_tools_screen.dart import 'package:eatvio_app/features/my_diets/presentation/my_diets_screen.dart'; import 'package:eatvio_app/features/my_plans/presentation/my_plans_screen.dart'; import 'package:eatvio_app/features/profile/models/profile_model.dart'; +import 'package:eatvio_app/features/recipe/presentation/pages/my_recipes_screen.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -28,7 +29,7 @@ class MenuScreen extends StatelessWidget { _tile(title: 'Настройки профиля', leading: ImageConstant.icnProfileSettings, onTap: () => GoRouter.of(context).push('/settings-screen')), if (profileModel.roles.contains('consultant')) _divider(context), if (profileModel.roles.contains('consultant')) - _tile(leading: ImageConstant.icnRecipes, title: 'Мои рецепты', onTap: () => GoRouter.of(context).push('/recipe-select-screen')), + _tile(leading: ImageConstant.icnRecipes, title: 'Мои рецепты', onTap: () => GoRouter.of(context).push(MyRecipesScreen.path)), if (profileModel.roles.contains('consultant')) _divider(context), if (profileModel.roles.contains('consultant')) _tile(leading: 'assets/icons/diets.svg', title: 'Мои диеты', onTap: () => GoRouter.of(context).push(MyDietsScreen.path)), diff --git a/lib/features/my_plans/services/my_plans_service.dart b/lib/features/my_plans/services/my_plans_service.dart index 9c5cbf0c..bc24aec8 100644 --- a/lib/features/my_plans/services/my_plans_service.dart +++ b/lib/features/my_plans/services/my_plans_service.dart @@ -8,7 +8,7 @@ part 'my_plans_service.g.dart'; abstract class MyPlansService { factory MyPlansService(Dio dio, {String baseUrl}) = _MyPlansService; - @GET('/api/plans/index') + @GET('/api/profile/my_plans') Future getPlans({ @Query('page') required int page, @Query('filters[name][\$startsWith]') String? query, diff --git a/lib/features/recipe/presentation/about_recipe_screen.dart b/lib/features/recipe/presentation/pages/about_recipe_screen.dart similarity index 100% rename from lib/features/recipe/presentation/about_recipe_screen.dart rename to lib/features/recipe/presentation/pages/about_recipe_screen.dart diff --git a/lib/features/recipe/presentation/pages/my_recipes_screen.dart b/lib/features/recipe/presentation/pages/my_recipes_screen.dart new file mode 100644 index 00000000..ad30f146 --- /dev/null +++ b/lib/features/recipe/presentation/pages/my_recipes_screen.dart @@ -0,0 +1,196 @@ +import 'package:easy_debounce/easy_debounce.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/widgets/add_item_button.dart'; +import 'package:eatvio_app/core/widgets/custom_search_bar.dart'; +import 'package:eatvio_app/core/widgets/data_error_widget.dart'; +import 'package:eatvio_app/core/widgets/loader.dart'; +import 'package:eatvio_app/features/create_diet/presentation/create_diet_screen.dart'; +import 'package:eatvio_app/features/feed/presentation/widgets/diet_filter_bottom_sheet.dart'; +import 'package:eatvio_app/features/feed/presentation/widgets/filter_badge.dart'; +import 'package:eatvio_app/features/my_diets/presentation/cubit/my_diets_cubit.dart'; +import 'package:eatvio_app/features/my_diets/presentation/cubit/my_diets_state.dart'; +import 'package:eatvio_app/features/my_diets/presentation/widgets/my_diet_widget.dart'; +import 'package:eatvio_app/features/recipe/presentation/widgets/my_recipes_tab.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; + +class MyRecipesScreen extends StatelessWidget { + const MyRecipesScreen({super.key}); + static const name = 'myRecipesScreen'; + static const path = '/my-recipes-screen'; + + @override + Widget build(BuildContext context) { + return BlocProvider(create: (context) => sl()..loadAllDiets(), child: MyRecipesScreenContent()); + } +} + +class MyRecipesScreenContent extends StatelessWidget { + MyRecipesScreenContent({super.key}); + + final activeDietsScrollController = ScrollController(); + final draftDietsScrollController = ScrollController(); + + final activeDietsSearchController = SearchController(); + final draftDietsSearchController = SearchController(); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: appTheme.gray100, + appBar: AppBar( + centerTitle: true, + title: Text( + 'Мои рецепты', + maxLines: 2, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + softWrap: true, + style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black), + ), + ), + body: BlocBuilder( + buildWhen: (previous, current) => previous.selectedStateIndex != current.selectedStateIndex, + builder: (context, state) { + return NestedScrollView( + controller: switch (state.selectedStateIndex) { + 1 => draftDietsScrollController, + _ => activeDietsScrollController + }, + headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { + return [ + SliverAppBar( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(bottom: Radius.circular(12)), + ), + backgroundColor: appTheme.gray100, + foregroundColor: appTheme.gray100, + automaticallyImplyLeading: false, + stretchTriggerOffset: 1, + floating: true, + primary: false, + snap: true, + scrolledUnderElevation: 0, + bottom: PreferredSize( + preferredSize: Size( + MediaQuery.sizeOf(context).width, + 64, + ), + child: Padding( + padding: const EdgeInsets.only(bottom: 0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 8), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 4), + child: MyRecipesTab(), + ), + const SizedBox(height: 8), + Row( + children: [ + const SizedBox(width: 4), + Expanded( + child: CustomSearchBar( + enableIcon: false, + backgroundColor: appTheme.whiteA700, + borderRadius: 100, + searchController: switch (state.selectedStateIndex) { + 1 => draftDietsSearchController, + _ => activeDietsSearchController + }, + onChanged: (query) { + EasyDebounce.debounce( + 'searchDiets', + const Duration(milliseconds: 500), + () { + context.read().loadCurrentDiets(query: query == '' ? null : query); + }, + ); + }, + ), + ), + const SizedBox(width: 12), + AddItemButton( + onTap: () => GoRouter.of(context).pushNamed(CreateDietScreen.name), + ), + const SizedBox(width: 4), + ], + ), + const SizedBox(height: 16), + ], + ), + ), + ), + ), + ]; + }, + body: RefreshIndicator( + onRefresh: () => context.read().loadCurrentDiets(), + child: BlocBuilder( + builder: (BuildContext context, MyDietsState state) { + if (state.isLoading) { + return Scaffold( + backgroundColor: Colors.grey.shade200, + body: const Loader(), + ); + } else if (state.error != null) { + return DataErrorWidget(onReload: () => context.read().loadAllDiets()); + } else { + final gotDietLoadingError = state.activeDietsError != null && state.selectedStateIndex == 0 || + state.draftDietsError != null && state.selectedStateIndex == 1; + + if (gotDietLoadingError) { + return DataErrorWidget(onReload: () => context.read().loadCurrentDiets()); + } + + if (state.isLoading) { + return const Loader(); + } + + final diets = switch (state.selectedStateIndex) { + 1 => state.draftDiets, + _ => state.activeDiets, + }; + + return Stack( + children: [ + const SizedBox.expand(), + ListView.separated( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + itemBuilder: (context, index) { + return MyDietWidget( + diet: diets[index], + key: ValueKey(diets[index].id), + ); + }, + separatorBuilder: (context, index) { + return const SizedBox(height: 20); + }, + itemCount: diets.length, + ), + Positioned( + right: 0, + bottom: 74, + child: FilterBadge( + filterBottomSheet: DietFilterBottomSheet( + myDietsCubit: context.read(), + ), + ), + ), + ], + ); + } + }, + ), + ), + ); + }, + ), + ); + } +} diff --git a/lib/features/recipe/presentation/widgets/my_recipes_tab.dart b/lib/features/recipe/presentation/widgets/my_recipes_tab.dart new file mode 100644 index 00000000..f6de8669 --- /dev/null +++ b/lib/features/recipe/presentation/widgets/my_recipes_tab.dart @@ -0,0 +1,68 @@ +import 'package:eatvio_app/core/theme/custom_text_style.dart'; +import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:eatvio_app/core/widgets/animated_option_button.dart'; +import 'package:eatvio_app/generated/l10n.dart'; +import 'package:flutter/widgets.dart'; + +class MyRecipesTab extends StatefulWidget { + const MyRecipesTab({super.key, this.additionalOnTap}); + + final void Function()? additionalOnTap; + + @override + State createState() => _MyRecipesTabState(); +} + +class _MyRecipesTabState extends State { + final List tabTitles = []; + + @override + void didChangeDependencies() { + final s = S.of(context); + if (tabTitles.isEmpty) { + tabTitles.add(s.published); + tabTitles.add(s.drafted); + } + super.didChangeDependencies(); + } + + @override + Widget build(BuildContext context) { + return Align( + alignment: Alignment.centerLeft, + child: Row( + children: [ + Expanded( + child: Container( + height: 48, + decoration: BoxDecoration( + color: appTheme.whiteA700, + borderRadius: BorderRadius.circular(100), + ), + padding: const EdgeInsets.all(4), + child: Row( + children: [ + ...List.generate( + 2, + (index) => + Expanded( + child: AnimatedOptionButton( + textStyle: TextStyles.textSMedium, + isSelected: false, // state.selectedStateIndex == index, + text: tabTitles[index], + onTap: () { + widget.additionalOnTap?.call(); + // context.read().changeSelectedStateIndex(index); + }, + ), + ), + ), + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/features/registration/presentation/pages/verify_email_screen.dart b/lib/features/registration/presentation/pages/verify_email_screen.dart new file mode 100644 index 00000000..d801b778 --- /dev/null +++ b/lib/features/registration/presentation/pages/verify_email_screen.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; + +class VerifyEmailScreen extends StatefulWidget { + const VerifyEmailScreen({super.key}); + + @override + State createState() => _VerifyEmailScreenState(); +} + +class _VerifyEmailScreenState extends State { + final List _focusNodes = List.generate(4, (_) => FocusNode()); + final List _controllers = List.generate(4, (_) => TextEditingController()); + + @override + void dispose() { + for (var controller in _controllers) { + controller.dispose(); + } + for (var node in _focusNodes) { + node.dispose(); + } + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text("Подтвердите почту")), + body: Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text("Код отправлен на почту\nnazvanie@mail.com", textAlign: TextAlign.center), + SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: List.generate(4, (index) => _buildCodeField(index)), + ), + SizedBox(height: 20), + Text("Запросить код повторно", style: TextStyle(color: Colors.grey)), + TextButton( + onPressed: () { + // логика запроса нового кода + }, + child: Text("Не приходит код?"), + ) + ], + ), + ), + ); + } + + Widget _buildCodeField(int index) { + return SizedBox( + width: 50, + child: TextField( + controller: _controllers[index], + focusNode: _focusNodes[index], + keyboardType: TextInputType.number, + textAlign: TextAlign.center, + maxLength: 1, + style: TextStyle(fontSize: 24), + decoration: InputDecoration( + counterText: "", + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8.0), + ), + ), + onChanged: (value) { + if (value.isNotEmpty && index < 3) { + FocusScope.of(context).requestFocus(_focusNodes[index + 1]); + } + if (value.isEmpty && index > 0) { + FocusScope.of(context).requestFocus(_focusNodes[index - 1]); + } + }, + ), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 5400a274..dff61dd4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -71,9 +71,6 @@ dependencies: version: ^3.0.2 package_info_plus: ^8.3.0 -dependency_overrides: - intl: ^0.19.0 - dev_dependencies: build_runner: ^2.4.15 freezed: ^3.0.6 From 29b3de325c0bea2081f880013abeb4544fada2d9 Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Sat, 24 May 2025 16:52:23 +0300 Subject: [PATCH 2/4] fix: analyzer --- .../kotlin-compiler-3965398408914680734.salive | 0 .../presentation/pages/verify_email_screen.dart | 14 +++++++------- 2 files changed, 7 insertions(+), 7 deletions(-) create mode 100644 android/.kotlin/sessions/kotlin-compiler-3965398408914680734.salive diff --git a/android/.kotlin/sessions/kotlin-compiler-3965398408914680734.salive b/android/.kotlin/sessions/kotlin-compiler-3965398408914680734.salive new file mode 100644 index 00000000..e69de29b diff --git a/lib/features/registration/presentation/pages/verify_email_screen.dart b/lib/features/registration/presentation/pages/verify_email_screen.dart index d801b778..9f0da694 100644 --- a/lib/features/registration/presentation/pages/verify_email_screen.dart +++ b/lib/features/registration/presentation/pages/verify_email_screen.dart @@ -25,25 +25,25 @@ class _VerifyEmailScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: Text("Подтвердите почту")), + appBar: AppBar(title: const Text("Подтвердите почту")), body: Padding( padding: const EdgeInsets.all(24.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text("Код отправлен на почту\nnazvanie@mail.com", textAlign: TextAlign.center), - SizedBox(height: 20), + const Text("Код отправлен на почту\nnazvanie@mail.com", textAlign: TextAlign.center), + const SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: List.generate(4, (index) => _buildCodeField(index)), ), - SizedBox(height: 20), - Text("Запросить код повторно", style: TextStyle(color: Colors.grey)), + const SizedBox(height: 20), + const Text("Запросить код повторно", style: TextStyle(color: Colors.grey)), TextButton( onPressed: () { // логика запроса нового кода }, - child: Text("Не приходит код?"), + child: const Text("Не приходит код?"), ) ], ), @@ -60,7 +60,7 @@ class _VerifyEmailScreenState extends State { keyboardType: TextInputType.number, textAlign: TextAlign.center, maxLength: 1, - style: TextStyle(fontSize: 24), + style: const TextStyle(fontSize: 24), decoration: InputDecoration( counterText: "", border: OutlineInputBorder( From cf5b4597567303708d5d81bda06ae41da7b0027d Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Mon, 26 May 2025 13:37:33 +0300 Subject: [PATCH 3/4] fix: phone-verify redirect --- ...kotlin-compiler-3965398408914680734.salive | 0 .../reports/problems/problems-report.html | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- android/settings.gradle | 2 +- assets/lottie_animations/loading.json | 1 + assets/rive_animations/loading.riv | Bin 969 -> 0 bytes lib/core/const.dart | 4 +- lib/core/widgets/custom_popup.dart | 4 +- lib/core/widgets/loader.dart | 14 ++-- .../presentation/pages/verify_screen.dart | 78 ++++++++++++++++++ pubspec.yaml | 2 - 11 files changed, 94 insertions(+), 15 deletions(-) delete mode 100644 android/.kotlin/sessions/kotlin-compiler-3965398408914680734.salive create mode 100644 assets/lottie_animations/loading.json delete mode 100644 assets/rive_animations/loading.riv diff --git a/android/.kotlin/sessions/kotlin-compiler-3965398408914680734.salive b/android/.kotlin/sessions/kotlin-compiler-3965398408914680734.salive deleted file mode 100644 index e69de29b..00000000 diff --git a/android/build/reports/problems/problems-report.html b/android/build/reports/problems/problems-report.html index f2f60fc9..f21ab9cc 100644 --- a/android/build/reports/problems/problems-report.html +++ b/android/build/reports/problems/problems-report.html @@ -650,7 +650,7 @@ code + .copy-button { diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index efdcc4ac..f587a472 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-all.zip diff --git a/android/settings.gradle b/android/settings.gradle index 849ba91f..833c476b 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -20,7 +20,7 @@ plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "com.android.application" version "8.7.3" apply false // START: FlutterFire Configuration - id "com.google.gms.google-services" version "4.3.15" apply false + id "com.google.gms.google-services" version "4.4.2" apply false // END: FlutterFire Configuration id "org.jetbrains.kotlin.android" version "2.0.21" apply false } diff --git a/assets/lottie_animations/loading.json b/assets/lottie_animations/loading.json new file mode 100644 index 00000000..a0ac541f --- /dev/null +++ b/assets/lottie_animations/loading.json @@ -0,0 +1 @@ +{"nm":"Main Scene","ddd":0,"h":500,"w":500,"meta":{"g":"@lottiefiles/creator 1.46.0"},"layers":[{"ty":0,"nm":"Nested Scene 1","sr":1,"st":0,"op":31,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":500,"h":500,"refId":"precomp_newScene_abc3f55b-16ae-4191-8057-2b89484cea7e","ind":1},{"ty":4,"nm":"Слой_x0020_11","sr":1,"st":0,"op":31,"ip":0,"hd":false,"ln":"Слой_x0020_1","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[2116.5,2116.5]},"s":{"a":0,"k":[10.7995,10.7995]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[249.892,323.7606]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","bm":0,"ln":"_2456927096976","hd":false,"nm":"_2456927096976","it":[{"ty":"gr","bm":0,"hd":false,"nm":"Group 2","it":[{"ty":"sh","bm":0,"hd":false,"nm":"Path 11","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[194,83],[145,154],[-70,66],[-67,-70],[-150,-64],[-164,0],[-150,65],[-112,119],[-71,-67],[67,-70],[194,-84],[211,0]],"o":[[-212,0],[-194,-84],[-67,-70],[71,-67],[112,119],[150,65],[163,0],[150,-64],[67,-70],[70,66],[-145,154],[-194,83],[0,0]],"v":[[2118,1902],[1504,1776],[990,1415],[997,1167],[1246,1174],[1643,1452],[2118,1550],[2592,1452],[2989,1174],[3238,1167],[3245,1415],[2731,1776],[2118,1902]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[1,1,1]},"r":2,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[2117.50004629844,1510.5843947960868]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[2117.50004629844,1510.5843947960868]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":true,"nm":"Group 3","it":[{"ty":"sh","bm":0,"hd":false,"nm":"Path 12","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,-882],[881,0],[0,881],[-882,0]],"o":[[881,0],[0,881],[-882,0],[0,-882],[0,0]],"v":[[2118,1],[3713,1270],[2118,2866],[522,1270],[2118,1]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2627,0.651,0.949]},"r":2,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[2117.5,1433.5000000000002]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[2117.5,1433.5000000000002]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"tr","a":{"a":0,"k":[2116.5000000000005,2116.5]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[2116.5000000000005,2116.5]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"ind":2},{"ty":4,"nm":"Слой_x0020_1","sr":1,"st":0,"op":31,"ip":0,"hd":false,"ln":"Слой_x0020_1","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[2117.5,1433.4999999999998]},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[8.78118,8.78118],"t":0},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[12,12],"t":15},{"s":[8.7812,8.7812],"t":30}]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[249.99999499999998,250.00001499999996]},"r":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":0},{"s":[360],"t":30}]},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"shapes":[{"ty":"gr","bm":0,"ln":"_2456927096976","hd":false,"nm":"_2456927096976","it":[{"ty":"gr","bm":0,"hd":true,"nm":"Group 2","it":[{"ty":"sh","bm":0,"hd":false,"nm":"Path 11","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[194,83],[145,154],[-70,66],[-67,-70],[-150,-64],[-164,0],[-150,65],[-112,119],[-71,-67],[67,-70],[194,-84],[211,0]],"o":[[-212,0],[-194,-84],[-67,-70],[71,-67],[112,119],[150,65],[163,0],[150,-64],[67,-70],[70,66],[-145,154],[-194,83],[0,0]],"v":[[2118,1902],[1504,1776],[990,1415],[997,1167],[1246,1174],[1643,1452],[2118,1550],[2592,1452],[2989,1174],[3238,1167],[3245,1415],[2731,1776],[2118,1902]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[1,1,1]},"r":2,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[2117.50004629844,1510.5843947960868]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[2117.50004629844,1510.5843947960868]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"gr","bm":0,"hd":false,"nm":"Group 3","it":[{"ty":"sh","bm":0,"hd":false,"nm":"Path 12","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0,-882],[881,0],[0,881],[-882,0]],"o":[[881,0],[0,881],[-882,0],[0,-882],[0,0]],"v":[[2118,1],[3713,1270],[2118,2866],[522,1270],[2118,1]]}}},{"ty":"fl","bm":0,"hd":false,"nm":"Fill","c":{"a":0,"k":[0.2627,0.651,0.949]},"r":2,"o":{"a":0,"k":100}},{"ty":"tr","a":{"a":0,"k":[2117.5,1433.5000000000002]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[2117.5,1433.5000000000002]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]},{"ty":"tr","a":{"a":0,"k":[2117.5,1433.5000000000002]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[2117.5,1433.5000000000002]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"ind":3}],"v":"5.7.0","fr":30,"op":30,"ip":0,"assets":[{"nm":"Nested Scene 1","id":"precomp_newScene_abc3f55b-16ae-4191-8057-2b89484cea7e","layers":[]}]} \ No newline at end of file diff --git a/assets/rive_animations/loading.riv b/assets/rive_animations/loading.riv deleted file mode 100644 index d328f26d4951ef0b71a5d1e83ba1f03527d176f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 969 zcma)*TSyd97{|Y}dp2buFKcYhX1YXz2&JouS*vq)T!qbgshK%M5NxF-ropzM)nLT!)r{>|nob&ts=lg&EnZq8n zBnGrk`@(1fEucl?B%Z?4cm~hnIXsUS@Dh$-`1jcgXfr@!aU8&!6h;sO@Is0O=$00Z z!U6&u>+<pm-LuUhVd~*GA_p%I6e;1CVWQf`vVt^X@8iV#LaYfX^R}S;=6ac^ zOdimP+VaF2q@9U5oVZ(M)Px6>){y*eSs=>VE(cNFRn7oy3g>d7wdPewI3^{9S{}wNIPt|IMLO9JS5y}>L}QOxXi)nDNWP`7o%%hf8Q z#J#-!Dg>49{kcSa>T!y6EXTq7s4c=0pT~|`Pe6HWW?nAtwi0^hpvH;%*=84?>6iOM zy~^{QeMJ3{noVdV9RACTo`$SDP^OpJtSzQ(>Fey#BV<{|YAiiVG`%FA9zdqXz0>hw z7x(_CW7x%VOiI?j03lS+9%RH&Krz!1J?SBlR@p{45uG2?kocW?CI1$5wO<X1TJu{A^`Sx!tk1uApu|0wLD|w!C;qbt6bEu5+W(iH1SKwi4Kup(M2w zph09O&ge@;MVJ0k_vgfxm-v5RUVi)$zt>-GKjb}mCfWfMG@+P--uiN%-2ntdaNR}* RD1;*LH6iwMO&|p4;U`uuCL#a; diff --git a/lib/core/const.dart b/lib/core/const.dart index 31c47e9c..4f38a54e 100644 --- a/lib/core/const.dart +++ b/lib/core/const.dart @@ -1,5 +1,5 @@ -String baseUrl = 'https://eatvio.ru'; -// String baseUrl = 'https://dev2.eatvio.com'; +// String baseUrl = 'https://eatvio.ru'; +String baseUrl = 'https://dev2.eatvio.com'; const List durationValues = [7, 14, 21]; diff --git a/lib/core/widgets/custom_popup.dart b/lib/core/widgets/custom_popup.dart index 67d87da5..a23c03c6 100644 --- a/lib/core/widgets/custom_popup.dart +++ b/lib/core/widgets/custom_popup.dart @@ -120,14 +120,14 @@ class CustomPopup extends StatelessWidget { if (complainedContentType == ComplainedContent.diet) { final result = await sl().unpublishDiet(id!); if (!result) { - showErrorDialog(context, title: S.current.could_not_delete_blog_post); + showErrorDialog(context, title: "Не удалось снять диету с публикации"); return; } showSuccessToast('Диета перенесена в архив, обновите ленту'); } else if (complainedContentType == ComplainedContent.plan) { final result = await sl().unpublish(slug!); if (!result) { - showErrorDialog(context, title: S.current.could_not_delete_blog_post); + showErrorDialog(context, title: "Не удалось снять план с публикации"); return; } showSuccessToast('План перенесен в архив, обновите ленту'); diff --git a/lib/core/widgets/loader.dart b/lib/core/widgets/loader.dart index dd4727dc..ae69e10f 100644 --- a/lib/core/widgets/loader.dart +++ b/lib/core/widgets/loader.dart @@ -1,17 +1,19 @@ import 'package:flutter/material.dart'; -import 'package:rive/rive.dart'; +import 'package:lottie/lottie.dart'; class Loader extends StatelessWidget { const Loader({super.key}); @override Widget build(BuildContext context) { - return const Center( + return Center( child: SizedBox( - height: 32, - width: 32, - child: RiveAnimation.asset( - 'assets/rive_animations/loading.riv', + height: 42, + width: 42, + child: Lottie.asset( + 'assets/lottie_animations/loading.json', + width: 42, + height: 42, ), ), ); diff --git a/lib/features/registration/presentation/pages/verify_screen.dart b/lib/features/registration/presentation/pages/verify_screen.dart index 69fa009c..6059a0b5 100644 --- a/lib/features/registration/presentation/pages/verify_screen.dart +++ b/lib/features/registration/presentation/pages/verify_screen.dart @@ -1,5 +1,8 @@ import 'package:eatvio_app/core/const.dart'; +import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:lottie/lottie.dart'; import 'package:webview_flutter/webview_flutter.dart'; class VerifyScreen extends StatefulWidget { @@ -22,6 +25,81 @@ class _VerifyScreenState extends State { super.initState(); _controller = WebViewController() ..setJavaScriptMode(JavaScriptMode.unrestricted) + ..setNavigationDelegate( + NavigationDelegate( + onNavigationRequest: (NavigationRequest request) async { + if (request.url.contains('user_detailz') && !request.url.contains('change-phone-verify') || request.url.contains('email/verify')) { + GoRouter.of(context).pop(); + await showModalBottomSheet( + useRootNavigator: true, + showDragHandle: true, + context: context, + builder: (context) => Container( + padding: const EdgeInsets.all(16), + width: MediaQuery.of(context).size.width, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text( + 'Номер телефона\nуспешно привязан', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 20, + height: 1, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 16), + Lottie.asset( + 'assets/lottie_animations/success.json', + width: 160, + height: 160, + ), + const SizedBox(height: 16), + const Text( + 'Теперь Вам доступен весь функционал!', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + height: 1, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 16), + InkWell( + onTap: () { + Navigator.of(context).pop(); + GoRouter.of(context).pop(); + }, + child: Ink( + width: MediaQuery.of(context).size.width - 32, + height: 52, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + color: appTheme.blueA400, + ), + child: const Center( + child: Text( + 'Закрыть', + style: TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + ), + ], + ), + ), + ); + return NavigationDecision.prevent; + } + return NavigationDecision.navigate; + }, + ), + ) ..loadRequest(Uri.parse('$baseUrl/login_with_pass_key?id=${widget.passKey}')); } diff --git a/pubspec.yaml b/pubspec.yaml index dff61dd4..3b1a20b4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -47,7 +47,6 @@ dependencies: file_picker: ^10.1.9 audioplayers: ^6.4.0 intl: ^0.20.2 - rive: ^0.13.20 mask_text_input_formatter: ^2.9.0 lottie: ^3.3.1 flutter_portal: ^1.1.4 @@ -90,7 +89,6 @@ flutter: - assets/audio/ - assets/icons/ - assets/splash/ - - assets/rive_animations/ - assets/lottie_animations/ flutter_intl: From f38eead280db2fdcd27224ff79384a47a806d88f Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Mon, 26 May 2025 13:48:55 +0300 Subject: [PATCH 4/4] fix: consultant profile --- ios/Runner.xcodeproj/project.pbxproj | 6 +++--- ios/Runner/Info.plist | 2 +- lib/core/app_router.dart | 1 - lib/core/const.dart | 4 ++-- .../become_consultant/services/consultant_service.dart | 2 +- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index ff6e8a89..31db8575 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 = 17; + CURRENT_PROJECT_VERSION = 18; 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 = 17; + CURRENT_PROJECT_VERSION = 18; 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 = 17; + CURRENT_PROJECT_VERSION = 18; DEVELOPMENT_TEAM = VL68C36HYS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 90850ad3..83164ead 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 17 + 18 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart index d8805f73..d667c3b5 100644 --- a/lib/core/app_router.dart +++ b/lib/core/app_router.dart @@ -77,7 +77,6 @@ class AppRouter { name: 'authScreen', builder: (context, state) => const AuthScreen(), routes: [ - // GoRoute(path: 'webView-registration-screen', name: 'webViewRegistrationScreen', builder: (context, state) => const WebViewRegistrationScreen()), GoRoute( path: 'registration-screen', name: 'registrationScreen', diff --git a/lib/core/const.dart b/lib/core/const.dart index 4f38a54e..31c47e9c 100644 --- a/lib/core/const.dart +++ b/lib/core/const.dart @@ -1,5 +1,5 @@ -// String baseUrl = 'https://eatvio.ru'; -String baseUrl = 'https://dev2.eatvio.com'; +String baseUrl = 'https://eatvio.ru'; +// String baseUrl = 'https://dev2.eatvio.com'; const List durationValues = [7, 14, 21]; diff --git a/lib/features/consultant/become_consultant/services/consultant_service.dart b/lib/features/consultant/become_consultant/services/consultant_service.dart index cc284993..fbf7541d 100644 --- a/lib/features/consultant/become_consultant/services/consultant_service.dart +++ b/lib/features/consultant/become_consultant/services/consultant_service.dart @@ -20,7 +20,7 @@ abstract class ConsultantService { @Part(name: 'photos[]', contentType: 'multipart/form-data') List? photos, }); - @GET('/api/consultant_request/{slug}/status') + @GET('/api/profile/consultant/{slug}/status') Future getConsultantRequestStatus({ @Path() required String slug, });