diff --git a/lib/models/item_overview.dart b/lib/models/item_overview.dart index 3832850..921d7db 100644 --- a/lib/models/item_overview.dart +++ b/lib/models/item_overview.dart @@ -1,62 +1,26 @@ -import 'package:didvan/models/category.dart'; - -class ItemOverview { +class OverviewData { final int id; final String title; final String image; final String description; - final int? timeToRead; - final String? reference; - final bool? forManagers; final String createdAt; final String? type; - final List? categories; - int? comments; - ItemOverview({ + const OverviewData({ required this.id, required this.title, required this.image, required this.description, - required this.timeToRead, - required this.reference, - required this.forManagers, required this.createdAt, required this.type, - required this.categories, - required this.comments, }); - factory ItemOverview.fromJson(Map json) => ItemOverview( + factory OverviewData.fromJson(Map json) => OverviewData( id: json['id'], title: json['title'], image: json['image'], description: json['description'], - timeToRead: json['timeToRead'], - reference: json['reference'], - forManagers: json['forManagers'], createdAt: json['createdAt'], type: json['type'], - comments: json['comments'], - categories: json['categories'] == null - ? null - : List.from( - json['categories'].map( - (cat) => Category.fromJson(cat), - ), - ), ); - - Map toJson() => { - 'id': id, - 'title': title, - 'image': image, - 'description': description, - 'timeToRead': timeToRead, - 'reference': reference, - 'forManagers': forManagers, - 'createdAt': createdAt, - 'type': type, - 'categories': categories?.map((e) => e.toJson()).toList(), - }; } diff --git a/lib/models/news_overview.dart b/lib/models/news_overview.dart index bdee076..35d2161 100644 --- a/lib/models/news_overview.dart +++ b/lib/models/news_overview.dart @@ -1,21 +1,25 @@ -class NewsOverviewData { - final int id; - final String title; +import 'package:didvan/models/item_overview.dart'; + +class NewsOverviewData extends OverviewData { final String reference; - final String description; - final String image; - final String createdAt; bool marked; NewsOverviewData({ - required this.id, - required this.title, required this.reference, - required this.description, - required this.image, - required this.createdAt, required this.marked, - }); + required id, + required createdAt, + required description, + required title, + required image, + }) : super( + createdAt: createdAt, + description: description, + id: id, + image: image, + title: title, + type: 'news', + ); factory NewsOverviewData.fromJson(Map json) => NewsOverviewData( @@ -25,16 +29,6 @@ class NewsOverviewData { description: json['description'], image: json['image'], createdAt: json['createdAt'], - marked: json['marked'], + marked: json['marked'] ?? true, ); - - Map toJson() => { - 'id': id, - 'title': title, - 'reference': reference, - 'description': description, - 'image': image, - 'createdAt': createdAt, - 'marked': marked, - }; } diff --git a/lib/models/radar_overview.dart b/lib/models/radar_overview.dart index b4e1da8..ba3469e 100644 --- a/lib/models/radar_overview.dart +++ b/lib/models/radar_overview.dart @@ -1,29 +1,33 @@ +import 'package:didvan/models/item_overview.dart'; + import 'category.dart'; -class RadarOverviewData { - final int id; - final String image; - final String title; - final String description; - final int timeToRead; - final String createdAt; +class RadarOverviewData extends OverviewData { final bool forManagers; - bool marked; final List categories; + final int timeToRead; int comments; + bool marked; RadarOverviewData({ - required this.id, - required this.image, - required this.title, - required this.description, - required this.timeToRead, - required this.createdAt, required this.forManagers, - required this.marked, required this.categories, required this.comments, - }); + required this.timeToRead, + required this.marked, + required createdAt, + required description, + required id, + required image, + required title, + }) : super( + createdAt: createdAt, + description: description, + id: id, + image: image, + title: title, + type: 'radar', + ); factory RadarOverviewData.fromJson(Map json) => RadarOverviewData( @@ -42,17 +46,4 @@ class RadarOverviewData { ), ), ); - - Map toJson() => { - 'id': id, - 'image': image, - 'title': title, - 'description': description, - 'timeToRead': timeToRead, - 'createdAt': createdAt, - 'forManagers': forManagers, - 'marked': marked, - 'comment': comments, - 'categories': categories.map((e) => e.toJson()).toList(), - }; } diff --git a/lib/models/view/action_sheet_data.dart b/lib/models/view/action_sheet_data.dart index 3659dd8..a95a4a2 100644 --- a/lib/models/view/action_sheet_data.dart +++ b/lib/models/view/action_sheet_data.dart @@ -13,7 +13,7 @@ class ActionSheetData { final bool withoutButtonMode; final bool smallDismissButton; - ActionSheetData({ + const ActionSheetData({ required this.content, required this.title, this.confrimTitle, diff --git a/lib/pages/home/news/news.dart b/lib/pages/home/news/news.dart index 34615fd..2adeb13 100644 --- a/lib/pages/home/news/news.dart +++ b/lib/pages/home/news/news.dart @@ -11,9 +11,6 @@ import 'package:didvan/pages/home/widgets/date_picker_button.dart'; import 'package:didvan/widgets/item_title.dart'; import 'package:didvan/pages/home/widgets/search_field.dart'; import 'package:didvan/pages/home/widgets/logo_app_bar.dart'; -import 'package:didvan/widgets/didvan/card.dart'; -import 'package:didvan/widgets/didvan/divider.dart'; -import 'package:didvan/widgets/shimmer_placeholder.dart'; import 'package:didvan/widgets/state_handlers/empty_result.dart'; import 'package:didvan/widgets/state_handlers/sliver_state_handler.dart'; import 'package:flutter/material.dart'; @@ -76,7 +73,7 @@ class _NewsState extends State { ), childCount: state.news.length, itemPadding: const EdgeInsets.only(left: 16, right: 16, bottom: 16), - placeholder: const _NewsOverviewPlaceholder(), + placeholder: NewsOverview.placeholder, ), ], ); @@ -141,56 +138,3 @@ class _NewsState extends State { ); } } - -class _NewsOverviewPlaceholder extends StatelessWidget { - const _NewsOverviewPlaceholder({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return DidvanCard( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const ShimmerPlaceholder(height: 64, width: 64), - const SizedBox(width: 8), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: const [ - ShimmerPlaceholder(height: 18, width: 200), - SizedBox(height: 8), - ShimmerPlaceholder(height: 18, width: 100), - ], - ), - ], - ), - const SizedBox(height: 12), - const ShimmerPlaceholder( - height: 16, - width: double.infinity, - ), - const SizedBox(height: 8), - const ShimmerPlaceholder( - height: 16, - width: double.infinity, - ), - const SizedBox(height: 8), - const ShimmerPlaceholder( - height: 16, - width: 100, - ), - const DidvanDivider(verticalPadding: 8), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: const [ - ShimmerPlaceholder(height: 12, width: 150), - ShimmerPlaceholder(height: 24, width: 24), - ], - ), - ], - ), - ); - } -} diff --git a/lib/pages/home/news/news_details/news_details.dart b/lib/pages/home/news/news_details/news_details.dart index 8d00e0b..d30e52f 100644 --- a/lib/pages/home/news/news_details/news_details.dart +++ b/lib/pages/home/news/news_details/news_details.dart @@ -1,3 +1,4 @@ +import 'package:didvan/models/requests/news.dart'; import 'package:didvan/pages/home/news/news_details/news_details_state.dart'; import 'package:didvan/widgets/didvan/page_view.dart'; import 'package:didvan/pages/home/widgets/floating_navigation_bar.dart'; @@ -19,7 +20,7 @@ class _NewsDetailsState extends State { @override void initState() { final state = context.read(); - state.args = widget.pageData['args']; + state.args = widget.pageData['args'] ?? const NewsRequestArgs(page: 0); Future.delayed(Duration.zero, () { state.getNewsDetails(widget.pageData['id']); }); @@ -49,6 +50,8 @@ class _NewsDetailsState extends State { left: 0, right: 0, child: FloatingNavigationBar( + hasUnmarkConfirmation: + widget.pageData['hasUnmarkConfirmation'], scrollController: _scrollController, comments: state.currentNews.comments, id: state.currentNews.id, diff --git a/lib/pages/home/news/news_details/news_details_state.dart b/lib/pages/home/news/news_details/news_details_state.dart index 816287d..ba97db3 100644 --- a/lib/pages/home/news/news_details/news_details_state.dart +++ b/lib/pages/home/news/news_details/news_details_state.dart @@ -29,11 +29,17 @@ class NewsDetailsState extends CoreProvier { _handleTracking(sendRequest: isForward != null); if (service.isSuccess) { final result = service.result; + final newsItem = NewsDetailsData.fromJson(result['news']); + if (args.page == 0) { + news.add(newsItem); + initialIndex = 0; + appState = AppState.idle; + return; + } NewsDetailsData? prevNews; if (result['prevNews'].isNotEmpty) { prevNews = NewsDetailsData.fromJson(result['prevNews']); } - final newsItem = NewsDetailsData.fromJson(result['news']); NewsDetailsData? nextNews; if (result['nextNews'].isNotEmpty) { nextNews = NewsDetailsData.fromJson(result['nextNews']); diff --git a/lib/pages/home/radar/radar.dart b/lib/pages/home/radar/radar.dart index 84eef84..a6bbf3b 100644 --- a/lib/pages/home/radar/radar.dart +++ b/lib/pages/home/radar/radar.dart @@ -18,12 +18,9 @@ import 'package:didvan/pages/home/widgets/search_field.dart'; import 'package:didvan/pages/home/widgets/logo_app_bar.dart'; import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/pages/home/widgets/date_picker_button.dart'; -import 'package:didvan/widgets/didvan/card.dart'; import 'package:didvan/widgets/didvan/checkbox.dart'; -import 'package:didvan/widgets/didvan/divider.dart'; import 'package:didvan/widgets/didvan/text.dart'; import 'package:didvan/widgets/item_title.dart'; -import 'package:didvan/widgets/shimmer_placeholder.dart'; import 'package:didvan/widgets/state_handlers/empty_result.dart'; import 'package:didvan/widgets/state_handlers/sliver_state_handler.dart'; import 'package:flutter/material.dart'; @@ -129,7 +126,7 @@ class _RadarState extends State { child: EmptyResult(onNewSearch: () => _focusNode.requestFocus()), ), - placeholder: const _RadarOverviewPlaceholder(), + placeholder: RadarOverview.placeholder, builder: (context, state, index) { final radar = state.radars[index]; return RadarOverview( @@ -286,71 +283,3 @@ class _RadarState extends State { super.dispose(); } } - -class _RadarOverviewPlaceholder extends StatelessWidget { - const _RadarOverviewPlaceholder({ - Key? key, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return DidvanCard( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const ShimmerPlaceholder( - width: 200, - height: 16, - ), - const SizedBox(height: 8), - const AspectRatio(aspectRatio: 16 / 9, child: ShimmerPlaceholder()), - const SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: const [ - ShimmerPlaceholder( - height: 12, - width: 70, - ), - ShimmerPlaceholder( - height: 12, - width: 70, - ), - ], - ), - const SizedBox(height: 8), - const ShimmerPlaceholder( - height: 16, - ), - const SizedBox(height: 8), - const ShimmerPlaceholder( - height: 16, - ), - const SizedBox(height: 8), - const ShimmerPlaceholder( - height: 16, - ), - const DidvanDivider(), - Row( - children: const [ - ShimmerPlaceholder( - height: 32, - width: 32, - ), - Spacer(), - ShimmerPlaceholder( - height: 32, - width: 32, - ), - SizedBox(width: 16), - ShimmerPlaceholder( - height: 32, - width: 32, - ), - ], - ), - ], - ), - ); - } -} diff --git a/lib/pages/home/radar/radar_details/radar_details.dart b/lib/pages/home/radar/radar_details/radar_details.dart index 4e9bedc..9e83488 100644 --- a/lib/pages/home/radar/radar_details/radar_details.dart +++ b/lib/pages/home/radar/radar_details/radar_details.dart @@ -32,7 +32,7 @@ class _RadarDetailsState extends State { return Scaffold( body: Consumer( builder: (context, state, child) => StateHandler( - onRetry: () => state.getRadarDetails(state.currentRadar.id), + onRetry: () => state.getRadarDetails(widget.pageData['id']), state: state, builder: (context, state) => Stack( children: [ @@ -50,12 +50,15 @@ class _RadarDetailsState extends State { left: 0, right: 0, child: FloatingNavigationBar( + hasUnmarkConfirmation: + widget.pageData['hasUnmarkConfirmation'], comments: state.currentRadar.comments, id: state.currentRadar.id, isRadar: true, marked: state.currentRadar.marked, title: state.currentRadar.title, - onMarkChanged: (value) => widget.pageData['onMarkChanged']( + onMarkChanged: (value) => + widget.pageData['onMarkChanged']?.call( state.currentRadar.id, value, ), @@ -63,7 +66,7 @@ class _RadarDetailsState extends State { scrollController: _scrollController, onCommentsChanged: (count) { state.onCommentsChanged(count); - widget.pageData['onCommentsChanged']( + widget.pageData['onCommentsChanged']?.call( state.currentRadar.id, count, ); diff --git a/lib/pages/home/radar/radar_details/radar_details_state.dart b/lib/pages/home/radar/radar_details/radar_details_state.dart index 10860ff..61f9ccf 100644 --- a/lib/pages/home/radar/radar_details/radar_details_state.dart +++ b/lib/pages/home/radar/radar_details/radar_details_state.dart @@ -35,7 +35,6 @@ class RadarDetailsState extends CoreProvier { _handleTracking(sendRequest: isForward != null); if (service.isSuccess) { final result = service.result; - RadarDetailsData? prevRadar; final radar = RadarDetailsData.fromJson(result['radar']); if (args.page == 0) { radars.add(radar); @@ -44,6 +43,7 @@ class RadarDetailsState extends CoreProvier { return; } + RadarDetailsData? prevRadar; if (result['prevRadar'].isNotEmpty) { prevRadar = RadarDetailsData.fromJson(result['prevRadar']); } diff --git a/lib/pages/home/settings/bookmarks/bookmark_state.dart b/lib/pages/home/settings/bookmarks/bookmark_state.dart index edd2310..5786b50 100644 --- a/lib/pages/home/settings/bookmarks/bookmark_state.dart +++ b/lib/pages/home/settings/bookmarks/bookmark_state.dart @@ -1,17 +1,15 @@ import 'package:didvan/models/enums.dart'; import 'package:didvan/models/item_overview.dart'; import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/providers/user_provider.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; class BookmarksState extends CoreProvier { - final List bookmarks = []; - final List filterdBookmarks = []; + final List bookmarks = []; String search = ''; String lastSearch = ''; - String? groupName; - bool get searching => search != ''; Future getBookmarks() async { @@ -19,50 +17,34 @@ class BookmarksState extends CoreProvier { lastSearch = search; } appState = AppState.busy; - final service = RequestService(RequestHelper.bookmarks(groupName)); + final service = RequestService(RequestHelper.bookmarks()); await service.httpGet(); - String resultKey; - switch (groupName) { - case 'radar': - resultKey = 'radars'; - break; - case 'news': - resultKey = groupName!; - break; - default: - resultKey = 'contents'; - } - if (service.isSuccess) { - final marks = service.result[resultKey]; - if (groupName == null) { - bookmarks.clear(); - for (var i = 0; i < marks.length; i++) { - bookmarks.add(ItemOverview.fromJson(marks[i])); - } - } else { - filterdBookmarks.clear(); - for (var i = 0; i < marks.length; i++) { - filterdBookmarks.add(ItemOverview.fromJson(marks[i])); - } - } + if (service.isSuccess) { + final marks = service.result['contents']; + bookmarks.clear(); + for (var i = 0; i < marks.length; i++) { + bookmarks.add(OverviewData.fromJson(marks[i])); + } appState = AppState.idle; return; } appState = AppState.failed; } - void unMark(int id) { + void onMarkChanged(int id, bool value) { + if (value) return; + final type = bookmarks.firstWhere((element) => element.id == id).type; + switch (type) { + case 'radars': + UserProvider.changeRadarMark(id, value); + break; + case 'news': + UserProvider.changeNewsMark(id, value); + break; + default: + } bookmarks.removeWhere((element) => element.id == id); - filterdBookmarks.removeWhere((element) => element.id == id); - notifyListeners(); - final service = RequestService(RequestHelper.markRadar(id)); - service.delete(); - } - - void onCommentsChanged(int id, int value) { - bookmarks.firstWhere((radar) => radar.id == id).comments = value; - filterdBookmarks.firstWhere((radar) => radar.id == id).comments = value; notifyListeners(); } } diff --git a/lib/pages/home/settings/bookmarks/bookmarks.dart b/lib/pages/home/settings/bookmarks/bookmarks.dart index 8bfd02a..6d1474a 100644 --- a/lib/pages/home/settings/bookmarks/bookmarks.dart +++ b/lib/pages/home/settings/bookmarks/bookmarks.dart @@ -1,13 +1,18 @@ import 'dart:async'; +import 'package:didvan/config/design_config.dart'; +import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/view/app_bar_data.dart'; import 'package:didvan/pages/home/settings/bookmarks/bookmark_state.dart'; +import 'package:didvan/pages/home/widgets/menu_item.dart'; +import 'package:didvan/routes/routes.dart'; +import 'package:didvan/widgets/animated_visibility.dart'; import 'package:didvan/widgets/didvan/card.dart'; import 'package:didvan/widgets/didvan/divider.dart'; import 'package:didvan/widgets/didvan/scaffold.dart'; -import 'package:didvan/pages/home/widgets/multitype_item.dart'; +import 'package:didvan/pages/home/widgets/multitype_overview.dart'; import 'package:didvan/pages/home/widgets/search_field.dart'; -import 'package:didvan/widgets/shimmer_placeholder.dart'; +import 'package:didvan/widgets/item_title.dart'; import 'package:didvan/widgets/state_handlers/empty_list.dart'; import 'package:didvan/widgets/state_handlers/empty_result.dart'; import 'package:didvan/widgets/state_handlers/sliver_state_handler.dart'; @@ -48,60 +53,62 @@ class _BookmarksState extends State { focusNode: _focuseNode, ), const SizedBox(height: 16), - // AnimatedVisibility( - // duration: DesignConfig.lowAnimationDuration, - // isVisible: !state.searching, - // child: DidvanCard( - // child: Column( - // children: [ - // MenuItem( - // onTap: () => _onCategorySelected('radar'), - // title: 'تحلیل‌های رادار', - // icon: DidvanIcons.radar_regular, - // iconSize: 24, - // ), - // const DidvanDivider(), - // MenuItem( - // onTap: () => _onCategorySelected('news'), - // title: 'اخبار', - // icon: DidvanIcons.news_regular, - // iconSize: 24, - // ), - // const DidvanDivider(), - // MenuItem( - // onTap: () => _onCategorySelected('video'), - // title: 'ویدئو‌ها', - // icon: DidvanIcons.video_regular, - // iconSize: 24, - // ), - // const DidvanDivider(), - // MenuItem( - // onTap: () => _onCategorySelected('podcast'), - // title: 'پادکست‌ها', - // icon: DidvanIcons.podcast_regular, - // iconSize: 24, - // ), - // ], - // ), - // ), - // ), - // Align( - // alignment: Alignment.centerRight, - // child: AnimatedVisibility( - // duration: DesignConfig.lowAnimationDuration, - // isVisible: !state.searching, - // child: const ItemTitle(title: 'آخرین نشان نشده‌ها'), - // ), - // ), + AnimatedVisibility( + duration: DesignConfig.lowAnimationDuration, + isVisible: !state.searching, + child: DidvanCard( + child: Column( + children: [ + MenuItem( + onTap: () => _onCategorySelected('radars'), + title: 'تحلیل‌های رادار', + icon: DidvanIcons.radar_regular, + iconSize: 24, + ), + const DidvanDivider(), + MenuItem( + onTap: () => _onCategorySelected('news'), + title: 'اخبار', + icon: DidvanIcons.news_regular, + iconSize: 24, + ), + const DidvanDivider(), + MenuItem( + onTap: () => _onCategorySelected('videos'), + title: 'ویدئو‌ها', + icon: DidvanIcons.video_regular, + iconSize: 24, + ), + const DidvanDivider(), + MenuItem( + onTap: () => _onCategorySelected('podcasts'), + title: 'پادکست‌ها', + icon: DidvanIcons.podcast_regular, + iconSize: 24, + ), + ], + ), + ), + ), + Align( + alignment: Alignment.centerRight, + child: AnimatedVisibility( + duration: DesignConfig.lowAnimationDuration, + isVisible: !state.searching, + child: const ItemTitle(title: 'آخرین نشان شده‌ها'), + ), + ), ], slivers: [ SliverStateHandler( state: state, - // centerEmptyState: state.searching, - builder: (context, state, index) => MultitypeItem( + centerEmptyState: state.searching, + builder: (context, state, index) => MultitypeOverview( item: state.bookmarks[index], + onMarkChanged: state.onMarkChanged, + hasUnmarkConfirmation: true, ), - placeholder: const _NewsOverviewPlaceholder(), + placeholder: MultitypeOverview.placeholder, itemPadding: const EdgeInsets.only(bottom: 8), emptyState: state.searching ? EmptyResult(onNewSearch: _focuseNode.requestFocus) @@ -114,12 +121,11 @@ class _BookmarksState extends State { ); } - // void _onCategorySelected(String? type) { - // FocusScope.of(context).unfocus(); - // final state = context.read(); - // state.groupName = type; - // Navigator.of(context).pushNamed(Routes.filteredBookmarks, arguments: state); - // } + void _onCategorySelected(String type) { + if (type != 'radars' && type != 'news') return; + FocusScope.of(context).unfocus(); + Navigator.of(context).pushNamed(Routes.filteredBookmarks, arguments: type); + } void _onChanged(String value) { final state = context.read(); @@ -133,56 +139,3 @@ class _BookmarksState extends State { }); } } - -class _NewsOverviewPlaceholder extends StatelessWidget { - const _NewsOverviewPlaceholder({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return DidvanCard( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const ShimmerPlaceholder(height: 64, width: 64), - const SizedBox(width: 8), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: const [ - ShimmerPlaceholder(height: 18, width: 200), - SizedBox(height: 8), - ShimmerPlaceholder(height: 18, width: 100), - ], - ), - ], - ), - const SizedBox(height: 12), - const ShimmerPlaceholder( - height: 16, - width: double.infinity, - ), - const SizedBox(height: 8), - const ShimmerPlaceholder( - height: 16, - width: double.infinity, - ), - const SizedBox(height: 8), - const ShimmerPlaceholder( - height: 16, - width: 100, - ), - const DidvanDivider(verticalPadding: 8), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: const [ - ShimmerPlaceholder(height: 12, width: 150), - ShimmerPlaceholder(height: 24, width: 24), - ], - ), - ], - ), - ); - } -} diff --git a/lib/pages/home/settings/bookmarks/filtered_bookmark/filtered_bookmark.dart b/lib/pages/home/settings/bookmarks/filtered_bookmark/filtered_bookmark.dart index e250b17..ac5bee1 100644 --- a/lib/pages/home/settings/bookmarks/filtered_bookmark/filtered_bookmark.dart +++ b/lib/pages/home/settings/bookmarks/filtered_bookmark/filtered_bookmark.dart @@ -1,10 +1,10 @@ +import 'package:didvan/models/news_overview.dart'; +import 'package:didvan/models/radar_overview.dart'; import 'package:didvan/models/view/app_bar_data.dart'; +import 'package:didvan/pages/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart'; +import 'package:didvan/pages/home/widgets/news_overview.dart'; import 'package:didvan/pages/home/widgets/radar_overview.dart'; -import 'package:didvan/pages/home/settings/bookmarks/bookmark_state.dart'; -import 'package:didvan/widgets/didvan/card.dart'; -import 'package:didvan/widgets/didvan/divider.dart'; import 'package:didvan/widgets/didvan/scaffold.dart'; -import 'package:didvan/widgets/shimmer_placeholder.dart'; import 'package:didvan/widgets/state_handlers/empty_list.dart'; import 'package:didvan/widgets/state_handlers/sliver_state_handler.dart'; import 'package:flutter/material.dart'; @@ -20,19 +20,22 @@ class FilteredBookmarks extends StatefulWidget { class _FilteredBookmarksState extends State { @override void initState() { - Future.delayed(Duration.zero, context.read().getBookmarks); + Future.delayed( + Duration.zero, + context.read().getBookmarks, + ); super.initState(); } String get _appBarTitle { - switch (context.read().groupName) { - case 'radar': + switch (context.read().type) { + case 'radars': return 'تحلیل‌های رادار'; case 'news': return 'اخبار'; - case 'video': + case 'videos': return 'ویدئو‌ها'; - case 'podcast': + case 'podcasts': return 'پادکست‌ها'; default: return 'پادکست‌ها'; @@ -44,93 +47,40 @@ class _FilteredBookmarksState extends State { return DidvanScaffold( appBarData: AppBarData(title: _appBarTitle), slivers: [ - Consumer( + Consumer( builder: (context, state, child) => - SliverStateHandler( + SliverStateHandler( state: state, - enableEmptyState: state.filterdBookmarks.isEmpty, + enableEmptyState: state.bookmarks.isEmpty, itemPadding: const EdgeInsets.only(bottom: 8), - placeholder: const _RadarOverviewPlaceholder(), + placeholder: RadarOverview.placeholder, emptyState: const EmptyList(), - builder: (context, state, index) => RadarOverview( - radar: state.filterdBookmarks[index], - onMarkChanged: (id, value) {}, - onCommentsChanged: (id, count) => - state.onCommentsChanged(id, count), - ), - childCount: state.filterdBookmarks.length, - onRetry: state.getBookmarks, + builder: (context, state, index) { + if (state.type == 'radars') { + return RadarOverview( + radar: state.bookmarks[index] as RadarOverviewData, + onMarkChanged: _onBookmarkChanged, + onCommentsChanged: state.onCommentsChanged, + hasUnmarkConfirmation: true, + ); + } + return NewsOverview( + news: state.bookmarks[index] as NewsOverviewData, + onMarkChanged: _onBookmarkChanged, + hasUnmarkConfirmation: true, + ); + }, + childCount: state.bookmarks.length, + onRetry: () => state.getBookmarks(), ), ), ], ); } -} -class _RadarOverviewPlaceholder extends StatelessWidget { - const _RadarOverviewPlaceholder({ - Key? key, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return DidvanCard( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const ShimmerPlaceholder( - width: 200, - height: 16, - ), - const SizedBox(height: 8), - const AspectRatio(aspectRatio: 16 / 9, child: ShimmerPlaceholder()), - const SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: const [ - ShimmerPlaceholder( - height: 12, - width: 70, - ), - ShimmerPlaceholder( - height: 12, - width: 70, - ), - ], - ), - const SizedBox(height: 8), - const ShimmerPlaceholder( - height: 16, - ), - const SizedBox(height: 8), - const ShimmerPlaceholder( - height: 16, - ), - const SizedBox(height: 8), - const ShimmerPlaceholder( - height: 16, - ), - const DidvanDivider(), - Row( - children: const [ - ShimmerPlaceholder( - height: 32, - width: 32, - ), - Spacer(), - ShimmerPlaceholder( - height: 32, - width: 32, - ), - SizedBox(width: 16), - ShimmerPlaceholder( - height: 32, - width: 32, - ), - ], - ), - ], - ), - ); + Future _onBookmarkChanged(int id, bool value) async { + if (value) return; + final state = context.read(); + state.onMarkChanged(id, false); } } diff --git a/lib/pages/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart b/lib/pages/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart new file mode 100644 index 0000000..ff5d20b --- /dev/null +++ b/lib/pages/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart @@ -0,0 +1,65 @@ +import 'package:didvan/models/enums.dart'; +import 'package:didvan/models/item_overview.dart'; +import 'package:didvan/models/news_overview.dart'; +import 'package:didvan/models/radar_overview.dart'; +import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/providers/user_provider.dart'; +import 'package:didvan/services/network/request.dart'; +import 'package:didvan/services/network/request_helper.dart'; + +class FilteredBookmarksState extends CoreProvier { + String search = ''; + String lastSearch = ''; + final List bookmarks = []; + final String type; + + FilteredBookmarksState(this.type); + + bool get searching => search != ''; + + Future getBookmarks() async { + if (search != '') { + lastSearch = search; + } + appState = AppState.busy; + final service = RequestService( + RequestHelper.bookmarks(type: type == 'radars' ? 'radar' : type)); + await service.httpGet(); + + if (service.isSuccess) { + final marks = service.result[type]; + bookmarks.clear(); + for (var i = 0; i < marks.length; i++) { + if (type == 'radars') { + bookmarks.add(RadarOverviewData.fromJson(marks[i])); + } + if (type == 'news') { + bookmarks.add(NewsOverviewData.fromJson(marks[i])); + } + } + appState = AppState.idle; + return; + } + appState = AppState.failed; + } + + void onMarkChanged(int id, bool value) { + switch (type) { + case 'radars': + UserProvider.changeRadarMark(id, value); + break; + case 'news': + UserProvider.changeNewsMark(id, value); + break; + default: + } + bookmarks.removeWhere((element) => element.id == id); + notifyListeners(); + } + + void onCommentsChanged(int id, int value) { + (bookmarks.firstWhere((radar) => radar.id == id) as RadarOverviewData) + .comments = value; + notifyListeners(); + } +} diff --git a/lib/pages/home/widgets/bookmark_button.dart b/lib/pages/home/widgets/bookmark_button.dart index b1b0716..eb4aa46 100644 --- a/lib/pages/home/widgets/bookmark_button.dart +++ b/lib/pages/home/widgets/bookmark_button.dart @@ -1,16 +1,21 @@ import 'package:didvan/constants/app_icons.dart'; +import 'package:didvan/models/view/action_sheet_data.dart'; +import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/widgets/didvan/icon_button.dart'; +import 'package:didvan/widgets/didvan/text.dart'; import 'package:flutter/material.dart'; class BookmarkButton extends StatefulWidget { final bool value; final void Function(bool value) onMarkChanged; final bool bigGestureSize; + final bool askForConfirmation; const BookmarkButton({ Key? key, required this.value, this.bigGestureSize = false, required this.onMarkChanged, + this.askForConfirmation = false, }) : super(key: key); @override @@ -37,11 +42,27 @@ class _BookmarkButtonState extends State { return DidvanIconButton( gestureSize: widget.bigGestureSize ? 32 : 24, icon: _value ? DidvanIcons.bookmark_solid : DidvanIcons.bookmark_regular, - onPressed: () { - setState(() { - _value = !_value; - }); - widget.onMarkChanged(_value); + onPressed: () async { + bool confirm = false; + if (widget.askForConfirmation) { + await ActionSheetUtils.openDialog( + data: ActionSheetData( + content: const DidvanText( + 'آیا می‌خواهید این محتوا از نشان‌ شده‌ها حذف شود؟', + ), + titleIcon: DidvanIcons.bookmark_regular, + titleColor: Theme.of(context).colorScheme.secondary, + title: 'تایید عملیات', + onConfirmed: () => confirm = true, + ), + ); + } + if (!widget.askForConfirmation || confirm) { + setState(() { + _value = !_value; + }); + widget.onMarkChanged(_value); + } }, ); } diff --git a/lib/pages/home/widgets/floating_navigation_bar.dart b/lib/pages/home/widgets/floating_navigation_bar.dart index 58ef5ee..fea2fbc 100644 --- a/lib/pages/home/widgets/floating_navigation_bar.dart +++ b/lib/pages/home/widgets/floating_navigation_bar.dart @@ -16,6 +16,7 @@ import 'package:flutter/material.dart'; class FloatingNavigationBar extends StatefulWidget { final ScrollController scrollController; + final bool hasUnmarkConfirmation; final void Function(int count) onCommentsChanged; final bool isRadar; final bool marked; @@ -36,6 +37,7 @@ class FloatingNavigationBar extends StatefulWidget { required this.id, required this.title, this.categories, + this.hasUnmarkConfirmation = false, }) : super(key: key); @override @@ -107,8 +109,14 @@ class _FloatingNavigationBarState extends State { const Spacer(), if (widget.isRadar) BookmarkButton( + askForConfirmation: widget.hasUnmarkConfirmation, value: widget.marked, - onMarkChanged: widget.onMarkChanged, + onMarkChanged: (value) { + widget.onMarkChanged(value); + if (widget.hasUnmarkConfirmation && !value) { + Navigator.of(context).pop(); + } + }, bigGestureSize: true, ), SizedBox( @@ -140,8 +148,14 @@ class _FloatingNavigationBarState extends State { if (!widget.isRadar) const SizedBox(width: 12), if (!widget.isRadar) BookmarkButton( + askForConfirmation: widget.hasUnmarkConfirmation, value: widget.marked, - onMarkChanged: widget.onMarkChanged, + onMarkChanged: (value) { + widget.onMarkChanged(value); + if (widget.hasUnmarkConfirmation && !value) { + Navigator.of(context).pop(); + } + }, bigGestureSize: true, ), if (widget.isRadar) diff --git a/lib/pages/home/widgets/multitype_item.dart b/lib/pages/home/widgets/multitype_overview.dart similarity index 58% rename from lib/pages/home/widgets/multitype_item.dart rename to lib/pages/home/widgets/multitype_overview.dart index 4bdab92..ef0a944 100644 --- a/lib/pages/home/widgets/multitype_item.dart +++ b/lib/pages/home/widgets/multitype_overview.dart @@ -1,19 +1,42 @@ import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/item_overview.dart'; +import 'package:didvan/models/requests/news.dart'; +import 'package:didvan/models/requests/radar.dart'; +import 'package:didvan/routes/routes.dart'; import 'package:didvan/widgets/didvan/card.dart'; import 'package:didvan/widgets/didvan/text.dart'; +import 'package:didvan/widgets/shimmer_placeholder.dart'; import 'package:didvan/widgets/skeleton_image.dart'; import 'package:flutter/material.dart'; import 'package:persian_number_utility/persian_number_utility.dart'; -class MultitypeItem extends StatelessWidget { - final ItemOverview item; - const MultitypeItem({Key? key, required this.item}) : super(key: key); +class MultitypeOverview extends StatelessWidget { + final OverviewData item; + final bool hasUnmarkConfirmation; + final void Function(int id, bool value) onMarkChanged; + + const MultitypeOverview({ + Key? key, + required this.item, + required this.onMarkChanged, + this.hasUnmarkConfirmation = false, + }) : super(key: key); @override Widget build(BuildContext context) { return DidvanCard( + onTap: () => Navigator.of(context).pushNamed( + item.type == 'radar' ? Routes.radarDetails : Routes.newsDetails, + arguments: { + 'onMarkChanged': onMarkChanged, + 'id': item.id, + 'args': item.type == 'radar' + ? const RadarRequestArgs(page: 0) + : const NewsRequestArgs(page: 0), + 'hasUnmarkConfirmation': hasUnmarkConfirmation, + }, + ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -72,4 +95,27 @@ class MultitypeItem extends StatelessWidget { ), ); } + + static Widget get placeholder => DidvanCard( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const ShimmerPlaceholder(height: 80, width: 80), + const SizedBox(width: 8), + SizedBox( + height: 80, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: const [ + ShimmerPlaceholder(height: 18, width: 200), + SizedBox(height: 8), + ShimmerPlaceholder(height: 18, width: 100), + Spacer(), + ShimmerPlaceholder(height: 14, width: 80), + ], + ), + ), + ], + ), + ); } diff --git a/lib/pages/home/widgets/news_overview.dart b/lib/pages/home/widgets/news_overview.dart index 8f43f6b..0c625de 100644 --- a/lib/pages/home/widgets/news_overview.dart +++ b/lib/pages/home/widgets/news_overview.dart @@ -6,6 +6,7 @@ import 'package:didvan/pages/home/widgets/bookmark_button.dart'; import 'package:didvan/widgets/didvan/card.dart'; import 'package:didvan/widgets/didvan/divider.dart'; import 'package:didvan/widgets/didvan/text.dart'; +import 'package:didvan/widgets/shimmer_placeholder.dart'; import 'package:didvan/widgets/skeleton_image.dart'; import 'package:flutter/material.dart'; @@ -13,11 +14,13 @@ class NewsOverview extends StatelessWidget { final NewsOverviewData news; final NewsRequestArgs? newsRequestArgs; final void Function(int id, bool value) onMarkChanged; + final bool hasUnmarkConfirmation; const NewsOverview({ Key? key, required this.news, required this.onMarkChanged, this.newsRequestArgs, + this.hasUnmarkConfirmation = false, }) : super(key: key); @override @@ -29,6 +32,7 @@ class NewsOverview extends StatelessWidget { 'onMarkChanged': onMarkChanged, 'id': news.id, 'args': newsRequestArgs, + 'hasUnmarkConfirmation': hasUnmarkConfirmation, }, ), child: Column( @@ -77,6 +81,7 @@ class NewsOverview extends StatelessWidget { BookmarkButton( value: news.marked, onMarkChanged: (value) => onMarkChanged(news.id, value), + askForConfirmation: hasUnmarkConfirmation, ), ], ), @@ -84,4 +89,50 @@ class NewsOverview extends StatelessWidget { ), ); } + + static Widget get placeholder => DidvanCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const ShimmerPlaceholder(height: 64, width: 64), + const SizedBox(width: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: const [ + ShimmerPlaceholder(height: 18, width: 200), + SizedBox(height: 8), + ShimmerPlaceholder(height: 18, width: 100), + ], + ), + ], + ), + const SizedBox(height: 12), + const ShimmerPlaceholder( + height: 16, + width: double.infinity, + ), + const SizedBox(height: 8), + const ShimmerPlaceholder( + height: 16, + width: double.infinity, + ), + const SizedBox(height: 8), + const ShimmerPlaceholder( + height: 16, + width: 100, + ), + const DidvanDivider(verticalPadding: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: const [ + ShimmerPlaceholder(height: 12, width: 150), + ShimmerPlaceholder(height: 24, width: 24), + ], + ), + ], + ), + ); } diff --git a/lib/pages/home/widgets/radar_overview.dart b/lib/pages/home/widgets/radar_overview.dart index 2d7946f..35be2a4 100644 --- a/lib/pages/home/widgets/radar_overview.dart +++ b/lib/pages/home/widgets/radar_overview.dart @@ -9,6 +9,7 @@ import 'package:didvan/widgets/didvan/card.dart'; import 'package:didvan/widgets/didvan/divider.dart'; import 'package:didvan/widgets/didvan/icon_button.dart'; import 'package:didvan/widgets/didvan/text.dart'; +import 'package:didvan/widgets/shimmer_placeholder.dart'; import 'package:didvan/widgets/skeleton_image.dart'; import 'package:flutter/material.dart'; @@ -16,6 +17,7 @@ class RadarOverview extends StatelessWidget { final RadarOverviewData radar; final void Function(int id, int count) onCommentsChanged; final void Function(int id, bool value) onMarkChanged; + final bool hasUnmarkConfirmation; final RadarRequestArgs? radarRequestArgs; const RadarOverview({ Key? key, @@ -23,6 +25,7 @@ class RadarOverview extends StatelessWidget { required this.onCommentsChanged, required this.onMarkChanged, this.radarRequestArgs, + this.hasUnmarkConfirmation = false, }) : super(key: key); @override @@ -35,6 +38,7 @@ class RadarOverview extends StatelessWidget { 'onCommentsChanged': onCommentsChanged, 'id': radar.id, 'args': radarRequestArgs, + 'hasUnmarkConfirmation': hasUnmarkConfirmation, }, ), child: Column( @@ -101,6 +105,7 @@ class RadarOverview extends StatelessWidget { BookmarkButton( value: radar.marked, onMarkChanged: (value) => onMarkChanged(radar.id, value), + askForConfirmation: hasUnmarkConfirmation, ), const Spacer(), if (radar.comments != 0) DidvanText(radar.comments.toString()), @@ -129,4 +134,63 @@ class RadarOverview extends StatelessWidget { ), ); } + + static Widget get placeholder => DidvanCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const ShimmerPlaceholder( + width: 200, + height: 16, + ), + const SizedBox(height: 8), + const AspectRatio(aspectRatio: 16 / 9, child: ShimmerPlaceholder()), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: const [ + ShimmerPlaceholder( + height: 12, + width: 70, + ), + ShimmerPlaceholder( + height: 12, + width: 70, + ), + ], + ), + const SizedBox(height: 8), + const ShimmerPlaceholder( + height: 16, + ), + const SizedBox(height: 8), + const ShimmerPlaceholder( + height: 16, + ), + const SizedBox(height: 8), + const ShimmerPlaceholder( + height: 16, + ), + const DidvanDivider(), + Row( + children: const [ + ShimmerPlaceholder( + height: 32, + width: 32, + ), + Spacer(), + ShimmerPlaceholder( + height: 32, + width: 32, + ), + SizedBox(width: 16), + ShimmerPlaceholder( + height: 32, + width: 32, + ), + ], + ), + ], + ), + ); } diff --git a/lib/routes/route_generator.dart b/lib/routes/route_generator.dart index 85164f2..15452b7 100644 --- a/lib/routes/route_generator.dart +++ b/lib/routes/route_generator.dart @@ -16,6 +16,7 @@ import 'package:didvan/pages/home/settings/about_us/about_us.dart'; import 'package:didvan/pages/home/settings/bookmarks/bookmarks.dart'; import 'package:didvan/pages/home/settings/bookmarks/bookmark_state.dart'; import 'package:didvan/pages/home/settings/bookmarks/filtered_bookmark/filtered_bookmark.dart'; +import 'package:didvan/pages/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart'; import 'package:didvan/pages/home/settings/direct_list/direct_list.dart'; import 'package:didvan/pages/home/settings/direct_list/direct_list_state.dart'; import 'package:didvan/pages/home/settings/general_settings/settings.dart'; @@ -129,8 +130,10 @@ class RouteGenerator { ); case Routes.filteredBookmarks: return _createRoute( - ChangeNotifierProvider.value( - value: settings.arguments as BookmarksState, + ChangeNotifierProvider( + create: (context) => FilteredBookmarksState( + settings.arguments as String, + ), child: const FilteredBookmarks(), ), ); diff --git a/lib/services/network/request_helper.dart b/lib/services/network/request_helper.dart index 6826af8..b9f2876 100644 --- a/lib/services/network/request_helper.dart +++ b/lib/services/network/request_helper.dart @@ -14,7 +14,7 @@ class RequestHelper { static const String updateUserProfile = _baseUserUrl + '/profile/photo'; static const String checkUsername = _baseUserUrl + '/CheckUsername'; static const String updateProfile = _baseUserUrl + '/profile/edit'; - static String bookmarks(String? type) => + static String bookmarks({String? type}) => _baseUserUrl + '/marked/${type ?? ''}'; static const String directTypes = baseUrl + '/direct/types'; diff --git a/lib/utils/action_sheet.dart b/lib/utils/action_sheet.dart index 18b4930..f121af0 100644 --- a/lib/utils/action_sheet.dart +++ b/lib/utils/action_sheet.dart @@ -156,6 +156,82 @@ class ActionSheetUtils { ); } + static Future openDialog({required ActionSheetData data}) async { + await showDialog( + context: context, + builder: (context) => Dialog( + shape: const RoundedRectangleBorder( + borderRadius: DesignConfig.mediumBorderRadius, + ), + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + GestureDetector( + onTap: () => Navigator.of(context).pop(), + child: Icon( + data.titleIcon, + size: 20, + color: data.titleColor, + ), + ), + const SizedBox( + width: 8, + ), + Expanded( + child: DidvanText( + data.title, + style: Theme.of(context).textTheme.headline3, + color: data.titleColor, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + const SizedBox( + height: 12, + ), + data.content, + const SizedBox( + height: 12, + ), + Row( + children: [ + if (data.hasDismissButton) + Expanded( + child: DidvanButton( + onPressed: data.onDismissed ?? () => pop(), + title: data.dismissTitle ?? 'بازگشت', + style: ButtonStyleMode.flat, + ), + ), + if (data.hasDismissButton) + const SizedBox( + width: 20, + ), + Expanded( + child: DidvanButton( + onPressed: () { + pop(); + data.onConfirmed?.call(); + }, + title: data.confrimTitle ?? 'تایید', + ), + ), + ], + ), + ], + ), + ), + ), + ); + } + static void pop() { DesignConfig.updateSystemUiOverlayStyle(); Navigator.of(context).pop(); diff --git a/lib/widgets/state_handlers/state_handler.dart b/lib/widgets/state_handlers/state_handler.dart index bb26748..107db8e 100644 --- a/lib/widgets/state_handlers/state_handler.dart +++ b/lib/widgets/state_handlers/state_handler.dart @@ -43,7 +43,7 @@ class StateHandler extends StatelessWidget { color: Theme.of(context).colorScheme.primary, ); case AppState.failed: - return EmptyConnection(onRetry: onRetry); + return Center(child: EmptyConnection(onRetry: onRetry)); default: return Container(); }