feat(views): added view handling for diets, recipes, plans and posts

This commit is contained in:
misha
2025-09-11 18:04:35 +03:00
parent 8b7180c2da
commit ff49562e86
12 changed files with 222 additions and 166 deletions

View File

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:easy_debounce/easy_debounce.dart';
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/widgets/add_item_button.dart';
@@ -60,18 +62,19 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
final consultantsController = TextEditingController();
late final List<String> chipsLabelList;
late final AppLifecycleListener _listener;
int _selectedChipIndex = 0;
final blogPostsScrollController = ScrollController();
final recipesScrollController = ScrollController();
final dietsScrollController = ScrollController();
final plansScrollController = ScrollController();
final consultantsScrollController = ScrollController();
final _feedCubit = sl<FeedCubit>();
@override
void initState() {
// Initialize chipsLabelList based on hidePlans setting
chipsLabelList = hidePlans ? ['Все', 'Рецепты', 'Диеты', 'Консультанты'] : ['Все', 'Рецепты', 'Диеты', 'Планы питания', 'Консультанты'];
_listener = AppLifecycleListener(onDetach: _sendViews);
consultantsScrollController.addListener(_onScrollConsultants);
super.initState();
}
@@ -80,6 +83,21 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
if (consultantsScrollController.offset >= consultantsScrollController.position.maxScrollExtent && !consultantsScrollController.position.outOfRange) {}
}
void _sendViews() {
if (postsIds.isNotEmpty) {
_feedCubit.viewContent(type: ViewableContentType.posts, ids: postsIds.toList());
}
if (plansIds.isNotEmpty) {
_feedCubit.viewContent(type: ViewableContentType.plans, ids: plansIds.toList());
}
if (dietsIds.isNotEmpty) {
_feedCubit.viewContent(type: ViewableContentType.diets, ids: dietsIds.toList());
}
if (recipesIds.isNotEmpty) {
_feedCubit.viewContent(type: ViewableContentType.recipes, ids: recipesIds.toList());
}
}
@override
void dispose() {
blogPostsScrollController.dispose();
@@ -87,9 +105,64 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
dietsScrollController.dispose();
plansScrollController.dispose();
consultantsScrollController.dispose();
for (final map in _visibilityTimers.values) {
for (final t in map.values) {
t.cancel();
}
map.clear();
}
_listener.dispose();
_sendViews();
super.dispose();
}
final Set<int> postsIds = <int>{};
final Set<int> plansIds = <int>{};
final Set<int> dietsIds = <int>{};
final Set<int> recipesIds = <int>{};
final Map<ViewableContentType, Map<int, Timer>> _visibilityTimers = {
ViewableContentType.posts: <int, Timer>{},
ViewableContentType.plans: <int, Timer>{},
ViewableContentType.diets: <int, Timer>{},
ViewableContentType.recipes: <int, Timer>{},
};
Set<int> _idsForType(ViewableContentType type) {
switch (type) {
case ViewableContentType.posts:
return postsIds;
case ViewableContentType.plans:
return plansIds;
case ViewableContentType.diets:
return dietsIds;
case ViewableContentType.recipes:
return recipesIds;
}
}
void _handleVisibility({required double visibleFraction, required int id, required ViewableContentType type}) {
final timers = _visibilityTimers[type]!;
final ids = _idsForType(type);
if (ids.contains(id)) {
timers[id]?.cancel();
timers.remove(id);
return;
}
if (visibleFraction >= 0.5) {
if (timers.containsKey(id)) return;
timers[id] = Timer(const Duration(seconds: 1), () {
ids.add(id);
timers.remove(id);
});
} else {
timers[id]?.cancel();
timers.remove(id);
}
}
@override
Widget build(BuildContext context) {
FlutterNativeSplash.remove();
@@ -228,10 +301,10 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
];
},
body: switch (_selectedChipIndex) {
0 => const BlogPosts(),
1 => const Recipes(),
2 => const Diets(),
3 when !hidePlans => const Plans(),
0 => BlogPosts(handleVisibility: _handleVisibility),
1 => Recipes(handleVisibility: _handleVisibility),
2 => Diets(handleVisibility: _handleVisibility),
3 when !hidePlans => Plans(handleVisibility: _handleVisibility),
3 when hidePlans => const Consultants(),
4 when !hidePlans => const Consultants(),
_ => Center(child: Text(chipsLabelList[_selectedChipIndex])),
@@ -240,3 +313,20 @@ class _FeedScreenContentState extends State<FeedScreenContent> {
);
}
}
enum ViewableContentType {
plans,
posts,
diets,
recipes;
String getStringifiedVersion() => switch (this) {
ViewableContentType.plans => 'plans',
ViewableContentType.posts => 'blog_posts',
ViewableContentType.diets => 'diets',
ViewableContentType.recipes => 'recipes',
};
}