forked from eatvio/eatvio_app
feat(feed): fixed comments state updating in feed, feat(consultantRegistration): fixed registration flow for users with unverified phone, feat(tests): fixed tests coverage
This commit is contained in:
@@ -78,9 +78,7 @@ class _BlogPostScreenState extends State<BlogPostScreen> {
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: true,
|
||||
bottomSheet: Container(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).padding.bottom + 8,
|
||||
),
|
||||
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom + 8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
border: Border(top: BorderSide(color: Colors.black.withValues(alpha: 0.1))),
|
||||
@@ -147,14 +145,18 @@ class _BlogPostScreenState extends State<BlogPostScreen> {
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
onTap: () async {
|
||||
if (textEditingController.text.length > 2) {
|
||||
commentsCubit.sendComment(
|
||||
final isSent = await commentsCubit.sendComment(
|
||||
id: widget.id.toString(),
|
||||
commentText: textEditingController.text,
|
||||
parentCommentId: replyId,
|
||||
commentSourceType: CommentSourceType.blogPost,
|
||||
);
|
||||
if (!context.mounted) return;
|
||||
|
||||
if (isSent) context.read<FeedCubit>().incrementCommentsState(widget.id);
|
||||
|
||||
context.read<CommentsCubit>().setReplyId(null);
|
||||
setState(() {
|
||||
textEditingController.clear();
|
||||
@@ -237,9 +239,7 @@ class _BlogPostScreenState extends State<BlogPostScreen> {
|
||||
if (blogPostState.blogPostModel?.description.isNotEmpty ?? false)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: MarkdownHtmlWidget(
|
||||
markdownText: blogPostState.blogPostModel!.description,
|
||||
),
|
||||
child: MarkdownHtmlWidget(markdownText: blogPostState.blogPostModel!.description),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (blogPostState.blogPostModel?.image.isNotEmpty ?? false)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart';
|
||||
import 'package:eatvio_app/features/consultant/become_consultant/models/consultant_profile_model.dart';
|
||||
@@ -20,45 +21,53 @@ class RegisterConsultantCubit extends Cubit<RegisterConsultantState> {
|
||||
);
|
||||
|
||||
Future<void> getMyRequestStatus(String slug) async {
|
||||
_update = true;
|
||||
final myRequestStatus = await consultantRepository.getMyRequestStatus(slug);
|
||||
final certPhotos = await consultantRepository.getConsultantCertPhotos();
|
||||
try {
|
||||
_update = true;
|
||||
final myRequestStatus = await consultantRepository.getMyRequestStatus(slug);
|
||||
final certPhotos = await consultantRepository.getConsultantCertPhotos();
|
||||
|
||||
List<CertificatesModel>? certificates = myRequestStatus.educationModel.completedCourses?.map((courseMap) {
|
||||
final course = courseMap.values.first;
|
||||
List<CertificatesModel>? certificates = myRequestStatus.educationModel.completedCourses?.map((courseMap) {
|
||||
final course = courseMap.values.first;
|
||||
|
||||
return CertificatesModel(title: course.title, date: course.date);
|
||||
}).toList();
|
||||
return CertificatesModel(title: course.title, date: course.date);
|
||||
}).toList();
|
||||
|
||||
String? sanitizeLink(String? link) {
|
||||
return (link != null && link.isNotEmpty) ? link : null;
|
||||
}
|
||||
String? sanitizeLink(String? link) {
|
||||
return (link != null && link.isNotEmpty) ? link : null;
|
||||
}
|
||||
|
||||
emit(
|
||||
RegisterConsultantState(
|
||||
consultant: ConsultantProfileModel(
|
||||
certificates: certificates,
|
||||
socialLinks: SocialLinksModel(
|
||||
vkLink: sanitizeLink(myRequestStatus.educationModel.vkLink),
|
||||
tgLink: sanitizeLink(myRequestStatus.educationModel.tgLink),
|
||||
fbLink: sanitizeLink(myRequestStatus.educationModel.fbLink),
|
||||
instaLink: sanitizeLink(myRequestStatus.educationModel.instaLink),
|
||||
personalSiteLink: sanitizeLink(myRequestStatus.educationModel.personalSiteLink),
|
||||
emit(
|
||||
RegisterConsultantState(
|
||||
consultant: ConsultantProfileModel(
|
||||
certificates: certificates,
|
||||
socialLinks: SocialLinksModel(
|
||||
vkLink: sanitizeLink(myRequestStatus.educationModel.vkLink),
|
||||
tgLink: sanitizeLink(myRequestStatus.educationModel.tgLink),
|
||||
fbLink: sanitizeLink(myRequestStatus.educationModel.fbLink),
|
||||
instaLink: sanitizeLink(myRequestStatus.educationModel.instaLink),
|
||||
personalSiteLink: sanitizeLink(myRequestStatus.educationModel.personalSiteLink),
|
||||
),
|
||||
hasMedicalEducation: myRequestStatus.educationModel.hasMedicalEducation == 1,
|
||||
),
|
||||
hasMedicalEducation: myRequestStatus.educationModel.hasMedicalEducation == 1,
|
||||
acceptOffer: false,
|
||||
loading: false,
|
||||
requestModel: myRequestStatus,
|
||||
certPhotos: certPhotos,
|
||||
isUpdating: _update,
|
||||
),
|
||||
acceptOffer: false,
|
||||
loading: false,
|
||||
requestModel: myRequestStatus,
|
||||
certPhotos: certPhotos,
|
||||
isUpdating: _update
|
||||
),
|
||||
);
|
||||
);
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> disableLoading() async {
|
||||
final certPhotos = await consultantRepository.getConsultantCertPhotos();
|
||||
emit(state.copyWith(loading: false, certPhotos: certPhotos));
|
||||
try {
|
||||
final certPhotos = await consultantRepository.getConsultantCertPhotos();
|
||||
emit(state.copyWith(loading: false, certPhotos: certPhotos));
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
void updateConsultantProfileModel(ConsultantProfileModel consultant, [bool acceptOffer = false]) {
|
||||
|
||||
@@ -17,18 +17,13 @@ class _BecomeConsultantScreenState extends State<BecomeConsultantScreen> {
|
||||
title: const Text('Стать консультантом'),
|
||||
automaticallyImplyLeading: false,
|
||||
centerTitle: false,
|
||||
leading: IconButton(
|
||||
onPressed: () => GoRouter.of(context).pop(),
|
||||
icon: const Icon(Icons.close),
|
||||
),
|
||||
leading: IconButton(onPressed: () => GoRouter.of(context).pop(), icon: const Icon(Icons.close)),
|
||||
),
|
||||
body: const Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Text(
|
||||
'Чтобы стать консультантом необходимо пройти короткую регистрацию. Подготовьте свои дипломы и/или сертификаты в формате jpg, они Вам пригодятся для проверки и подтверждения. Также необходимо будет указать ссылки на Ваши соц. сети и указать тип лица (физ. лицо или юр. лицо). На данный момент доступна регистрация в качестве физ. лица.\n\nВнутри Итвио разница между обычным пользователем и консультантом заключается в дополнительных возможностях последнего создавать публичные диеты, рецепты и планы питания; участвовать в ежемесячных рейтингах.',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
),
|
||||
bottomSheet: _startRegistrationButton(),
|
||||
@@ -37,24 +32,16 @@ class _BecomeConsultantScreenState extends State<BecomeConsultantScreen> {
|
||||
|
||||
InkWell _startRegistrationButton() {
|
||||
return InkWell(
|
||||
onTap: () => GoRouter.of(context).pushReplacementNamed('stepperBecomeConsultantScreen'),
|
||||
onTap: () => GoRouter.of(context).pushReplacementNamed('stepperBecomeConsultantScreen', extra: {'slug': null}),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: Ink(
|
||||
height: 52,
|
||||
width: MediaQuery.of(context).size.width - 32,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: appTheme.blueA400,
|
||||
),
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(20), color: appTheme.blueA400),
|
||||
child: const Center(
|
||||
child: Text(
|
||||
'Начать регистрацию',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
height: 1,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 18,
|
||||
),
|
||||
style: TextStyle(color: Colors.white, height: 1, fontWeight: FontWeight.w600, fontSize: 18),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -26,6 +26,17 @@ class FeedCubit extends Cubit<FeedState> {
|
||||
}
|
||||
}
|
||||
|
||||
void incrementCommentsState(int id) {
|
||||
final List<FeedBlogPostModel> feedBlogPostList = [...(state as FeedStateLoaded).feedBlogPostList];
|
||||
final int index = feedBlogPostList.indexWhere((e) => e.id == id);
|
||||
if (index == -1) return;
|
||||
final current = feedBlogPostList[index];
|
||||
feedBlogPostList[index] = current.copyWith(totalComments: current.totalComments + 1);
|
||||
if (!isClosed) {
|
||||
emit(FeedState.loaded(feedBlogPostList: feedBlogPostList, isLoading: isLoading));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadFeedBlogPostList({String? query}) async {
|
||||
try {
|
||||
_query = query;
|
||||
|
||||
@@ -33,7 +33,7 @@ import '../widgets/recipes.dart';
|
||||
|
||||
class FeedScreen extends StatelessWidget {
|
||||
const FeedScreen({super.key, this.initialTabIndex = 0});
|
||||
final int initialTabIndex;
|
||||
final int? initialTabIndex;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MultiBlocProvider(
|
||||
@@ -51,7 +51,7 @@ class FeedScreen extends StatelessWidget {
|
||||
|
||||
class FeedScreenContent extends StatefulWidget {
|
||||
const FeedScreenContent({super.key, required this.initialTabIndex});
|
||||
final int initialTabIndex;
|
||||
final int? initialTabIndex;
|
||||
@override
|
||||
State<FeedScreenContent> createState() => _FeedScreenContentState();
|
||||
}
|
||||
@@ -72,7 +72,7 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
|
||||
final consultantsScrollController = ScrollController();
|
||||
@override
|
||||
void initState() {
|
||||
_selectedChipIndex = widget.initialTabIndex;
|
||||
_selectedChipIndex = widget.initialTabIndex ?? 0;
|
||||
chipsLabelList = hidePlans ? ['Все', 'Консультанты', 'Рецепты', 'Диеты'] : ['Все', 'Планы питания', 'Консультанты', 'Рецепты', 'Диеты'];
|
||||
consultantsScrollController.addListener(_onScrollConsultants);
|
||||
_checkRegistrationDate(context);
|
||||
@@ -92,8 +92,11 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
|
||||
@override
|
||||
void didUpdateWidget(FeedScreenContent oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
final initialTabIndex = widget.initialTabIndex;
|
||||
setState(() {
|
||||
_selectedChipIndex = widget.initialTabIndex;
|
||||
if (initialTabIndex != null) {
|
||||
_selectedChipIndex = initialTabIndex;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -13,9 +13,12 @@ class AboutPlanCubit extends Cubit<AboutPlanState> {
|
||||
final PlanRepository planRepository;
|
||||
AboutPlanCubit({required this.planRepository}) : super(AboutPlanState.initial());
|
||||
|
||||
Future<void> getPlan(String slug) async {
|
||||
Future<void> getPlan({required String slug, int? index}) async {
|
||||
try {
|
||||
final plan = await planRepository.getPlan(slug);
|
||||
if (index != null) {
|
||||
planRepository.emitCommentsStateToFeed(CommentsState(index: index, totalComments: plan.totalComments));
|
||||
}
|
||||
emit(state.copyWith(planModel: plan, error: null));
|
||||
} catch (e, stackTrace) {
|
||||
log(e.toString(), stackTrace: stackTrace);
|
||||
@@ -27,13 +30,7 @@ class AboutPlanCubit extends Cubit<AboutPlanState> {
|
||||
try {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
final plan = await planRepository.getPlan(slug);
|
||||
emit(
|
||||
state.copyWith(
|
||||
planModel: plan,
|
||||
error: null,
|
||||
isLoading: false,
|
||||
),
|
||||
);
|
||||
emit(state.copyWith(planModel: plan, error: null, isLoading: false));
|
||||
} catch (e, stackTrace) {
|
||||
log(e.toString(), stackTrace: stackTrace);
|
||||
emit(state.copyWith(error: e.toString(), isLoading: false));
|
||||
@@ -59,21 +56,17 @@ class AboutPlanCubit extends Cubit<AboutPlanState> {
|
||||
}
|
||||
}
|
||||
|
||||
EasyDebounce.debounce(
|
||||
'like_plan_$slug',
|
||||
const Duration(seconds: 1),
|
||||
() async {
|
||||
try {
|
||||
await planRepository.likePlan(slug);
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
if (index != null) {
|
||||
planRepository.emitLikesStateToFeed(LikesState(wasLikedByMe: currentPlan.wasLikedByMe, totalLikes: currentPlan.totalLikes, index: index));
|
||||
}
|
||||
emit(state.copyWith(planModel: currentPlan, error: null));
|
||||
EasyDebounce.debounce('like_plan_$slug', const Duration(seconds: 1), () async {
|
||||
try {
|
||||
await planRepository.likePlan(slug);
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
if (index != null) {
|
||||
planRepository.emitLikesStateToFeed(LikesState(wasLikedByMe: currentPlan.wasLikedByMe, totalLikes: currentPlan.totalLikes, index: index));
|
||||
}
|
||||
},
|
||||
);
|
||||
emit(state.copyWith(planModel: currentPlan, error: null));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> addToFavorite({required String slug, required int? index}) async {
|
||||
@@ -84,22 +77,18 @@ class AboutPlanCubit extends Cubit<AboutPlanState> {
|
||||
if (index != null) {
|
||||
planRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: true, index: index));
|
||||
}
|
||||
EasyDebounce.debounce(
|
||||
'add_or_remove_plan_to_favorites',
|
||||
const Duration(seconds: 1),
|
||||
() async {
|
||||
try {
|
||||
await planRepository.addToFavorite(slug);
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
|
||||
if (index != null) {
|
||||
planRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: currentPlan.isFavorite, index: index));
|
||||
}
|
||||
emit(state.copyWith(planModel: currentPlan, error: null));
|
||||
EasyDebounce.debounce('add_or_remove_plan_to_favorites', const Duration(seconds: 1), () async {
|
||||
try {
|
||||
await planRepository.addToFavorite(slug);
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
|
||||
if (index != null) {
|
||||
planRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: currentPlan.isFavorite, index: index));
|
||||
}
|
||||
},
|
||||
);
|
||||
emit(state.copyWith(planModel: currentPlan, error: null));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> removeFromFavorite({required String slug, required int? index}) async {
|
||||
@@ -110,22 +99,18 @@ class AboutPlanCubit extends Cubit<AboutPlanState> {
|
||||
if (index != null) {
|
||||
planRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: false, index: index));
|
||||
}
|
||||
EasyDebounce.debounce(
|
||||
'add_or_remove_plan_to_favorites',
|
||||
const Duration(seconds: 1),
|
||||
() async {
|
||||
try {
|
||||
await planRepository.removeFromFavorite(slug);
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
|
||||
if (index != null) {
|
||||
planRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: currentPlan.isFavorite, index: index));
|
||||
}
|
||||
emit(state.copyWith(planModel: currentPlan, error: null));
|
||||
EasyDebounce.debounce('add_or_remove_plan_to_favorites', const Duration(seconds: 1), () async {
|
||||
try {
|
||||
await planRepository.removeFromFavorite(slug);
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
|
||||
if (index != null) {
|
||||
planRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: currentPlan.isFavorite, index: index));
|
||||
}
|
||||
},
|
||||
);
|
||||
emit(state.copyWith(planModel: currentPlan, error: null));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<(ChosenPlanModel?, String?)> chosePlan() async {
|
||||
@@ -149,8 +134,8 @@ class AboutPlanCubit extends Cubit<AboutPlanState> {
|
||||
}
|
||||
|
||||
String _getOptionNameFromIndex(int index) => switch (index) {
|
||||
1 => 'options2',
|
||||
2 => 'options3',
|
||||
_ => 'options1',
|
||||
};
|
||||
1 => 'options2',
|
||||
2 => 'options3',
|
||||
_ => 'options1',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ class _AboutPlanScreenState extends State<AboutPlanScreen> {
|
||||
const SizedBox(width: 16),
|
||||
],
|
||||
),
|
||||
body: DataErrorWidget(onReload: () => context.read<AboutPlanCubit>().getPlan(widget.slug)),
|
||||
body: DataErrorWidget(onReload: () => context.read<AboutPlanCubit>().getPlan(slug: widget.slug)),
|
||||
);
|
||||
}
|
||||
return Scaffold(
|
||||
@@ -295,7 +295,7 @@ class _AboutPlanScreenState extends State<AboutPlanScreen> {
|
||||
|
||||
Future.delayed(const Duration(milliseconds: 500), () {
|
||||
if (context.mounted) {
|
||||
context.read<AboutPlanCubit>().getPlan(widget.slug);
|
||||
context.read<AboutPlanCubit>().getPlan(slug: widget.slug, index: widget.index);
|
||||
}
|
||||
});
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
|
||||
Reference in New Issue
Block a user