From 249d318ff89d76b673345140ae8269e0bb57cb18 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Sat, 19 Mar 2022 17:22:37 +0330 Subject: [PATCH 01/54] back to dev! --- lib/views/home/studio/studio.dart | 178 +++++++++++++++++++++++++++--- 1 file changed, 165 insertions(+), 13 deletions(-) diff --git a/lib/views/home/studio/studio.dart b/lib/views/home/studio/studio.dart index 5f5f4ca..1ffca83 100644 --- a/lib/views/home/studio/studio.dart +++ b/lib/views/home/studio/studio.dart @@ -1,26 +1,178 @@ -import 'package:didvan/config/theme_data.dart'; -import 'package:didvan/constants/assets.dart'; +import 'package:didvan/constants/app_icons.dart'; +import 'package:didvan/models/requests/studio.dart'; +import 'package:didvan/models/view/action_sheet_data.dart'; +import 'package:didvan/utils/action_sheet.dart'; +import 'package:didvan/views/home/studio/studio_state.dart'; +import 'package:didvan/views/home/studio/widgets/slider.dart'; +import 'package:didvan/views/home/studio/widgets/tab_bar.dart'; import 'package:didvan/views/home/widgets/logo_app_bar.dart'; -import 'package:didvan/views/widgets/state_handlers/empty_state.dart'; +import 'package:didvan/views/home/widgets/overview/podcast.dart'; +import 'package:didvan/views/home/widgets/overview/video.dart'; +import 'package:didvan/views/home/widgets/search_field.dart'; +import 'package:didvan/views/widgets/didvan/divider.dart'; +import 'package:didvan/views/widgets/didvan/icon_button.dart'; +import 'package:didvan/views/widgets/didvan/radial_button.dart'; +import 'package:didvan/views/widgets/item_title.dart'; +import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; -class Studio extends StatelessWidget { +class Studio extends StatefulWidget { const Studio({Key? key}) : super(key: key); + @override + State createState() => _StudioState(); +} + +class _StudioState extends State { + final _focusNode = FocusNode(); + + @override + void initState() { + context.read().init(); + super.initState(); + } + @override Widget build(BuildContext context) { - return Column( - children: [ - const LogoAppBar(), - Expanded( - child: EmptyState( - asset: Assets.emptyStudio, - title: 'استودیو آینده', - subtitle: 'به زودی...', - titleColor: Theme.of(context).colorScheme.title, + return CustomScrollView( + slivers: [ + SliverToBoxAdapter( + child: Row( + children: [ + const Expanded(child: LogoAppBar(type: 'studio')), + Padding( + padding: + EdgeInsets.only(top: MediaQuery.of(context).padding.top), + child: DidvanIconButton( + icon: DidvanIcons.bookmark_regular, + onPressed: () {}, + ), + ), + ], + ), + ), + const SliverToBoxAdapter( + child: StudioTabBar(), + ), + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: SearchField( + title: 'جستجو در استودیو', + onChanged: (value) {}, + focusNode: _focusNode, + ), + ), + ), + const SliverToBoxAdapter( + child: StudioSlider(), + ), + const SliverPadding( + padding: EdgeInsets.symmetric(horizontal: 16), + sliver: SliverToBoxAdapter( + child: DidvanDivider( + verticalPadding: 0, + ), + ), + ), + SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 16), + sliver: SliverToBoxAdapter( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const ItemTitle(title: 'تازه‌ترین‌ها'), + DidvanIconButton( + gestureSize: 36, + icon: DidvanIcons.sort_regular, + onPressed: _showSortDialog, + ), + ], + ), + ), + ), + Consumer( + builder: (context, state, child) => SliverStateHandler( + state: state, + itemPadding: const EdgeInsets.only( + bottom: 8, + left: 16, + right: 16, + ), + placeholder: state.videosSelected + ? VideoOverview.placeHolder + : PodcastOverview.placeholder, + builder: (context, state, index) => state.videosSelected + ? VideoOverview( + onMarkChanged: state.changeMark, + hasUnmarkConfirmation: false, + video: state.studios[index], + onCommentsChanged: state.onCommentsChanged, + studioRequestArgs: StudioRequestArgs( + page: state.page, + order: state.order, + search: state.search, + type: state.type, + ), + ) + : PodcastOverview( + podcast: state.studios[index], + onMarkChanged: state.changeMark, + studioRequestArgs: StudioRequestArgs( + page: state.page, + order: state.order, + search: state.search, + type: state.type, + ), + ), + childCount: state.studios.length, + onRetry: () => state.getStudioOverviews(page: 1), ), ), ], ); } + + void _showSortDialog() { + final state = context.read(); + ActionSheetUtils.showBottomSheet( + data: ActionSheetData( + content: StatefulBuilder( + builder: (context, setState) => Column( + children: [ + DidvanRadialButton( + title: 'جدیدترین‌ها', + onSelected: () => setState( + () => state.selectedSortTypeIndex = 0, + ), + value: state.selectedSortTypeIndex == 0, + ), + const SizedBox(height: 24), + DidvanRadialButton( + title: 'پربازدیدترین‌ها', + onSelected: () => setState( + () => state.selectedSortTypeIndex = 1, + ), + value: state.selectedSortTypeIndex == 1, + ), + const SizedBox(height: 24), + DidvanRadialButton( + title: 'پربحث‌ترین‌ها', + onSelected: () => setState( + () => state.selectedSortTypeIndex = 2, + ), + value: state.selectedSortTypeIndex == 2, + ), + ], + ), + ), + title: 'مرتب‌‌سازی', + titleIcon: DidvanIcons.sort_regular, + hasDismissButton: false, + confrimTitle: 'مرتب سازی', + onConfirmed: () => state.getStudioOverviews(page: 1), + ), + ); + } } From 0738072d5d6e21fa6b6eb8561660bf25a4755637 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Sun, 20 Mar 2022 13:29:35 +0330 Subject: [PATCH 02/54] test.api !!! --- lib/services/network/request_helper.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/services/network/request_helper.dart b/lib/services/network/request_helper.dart index bfddb9a..e35f070 100644 --- a/lib/services/network/request_helper.dart +++ b/lib/services/network/request_helper.dart @@ -3,7 +3,7 @@ import 'package:didvan/models/requests/radar.dart'; import 'package:didvan/models/requests/studio.dart'; class RequestHelper { - static const String baseUrl = 'https://api.didvan.app'; + static const String baseUrl = 'https://test.api.didvan.app'; static const String _baseUserUrl = baseUrl + '/user'; static const String _baseRadarUrl = baseUrl + '/radar'; static const String _baseNewsUrl = baseUrl + '/news'; From 5dd6c4205fab36874c2e3fc5fda4a26b629439b3 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Wed, 23 Mar 2022 13:58:58 +0430 Subject: [PATCH 03/54] D1APP-99 studio (beta 2) --- lib/main.dart | 4 + lib/models/overview_data.dart | 2 +- lib/models/slider_data.dart | 27 ++ lib/providers/user_provider.dart | 6 +- lib/routes/route_generator.dart | 22 +- lib/services/network/request_helper.dart | 28 +- lib/views/home/comments/comments.dart | 23 +- lib/views/home/comments/comments_state.dart | 26 +- .../home/settings/bookmarks/bookmarks.dart | 10 +- .../filtered_bookmark/filtered_bookmark.dart | 29 +- .../filtered_bookmarks_state.dart | 14 +- lib/views/home/studio/studio.dart | 52 +++- ...etails.dart => studio_details.mobile.dart} | 122 ++++---- .../studio_details/studio_details.web.dart | 210 ++++++++++++++ .../studio_details/studio_details_state.dart | 35 ++- .../widgets/studio_details.dart | 97 ------- .../widgets/studio_details_widget.dart | 272 ++++++++++++++++++ lib/views/home/studio/studio_state.dart | 57 +++- lib/views/home/studio/widgets/slider.dart | 80 +++++- .../widgets/audio/audio_player_widget.dart | 39 ++- lib/views/home/widgets/bnb.dart | 89 +++--- .../home/widgets/floating_navigation_bar.dart | 2 +- .../home/widgets/overview/multitype.dart | 69 ++++- lib/views/home/widgets/overview/podcast.dart | 3 + lib/views/home/widgets/overview/radar.dart | 2 +- lib/views/home/widgets/overview/video.dart | 7 +- lib/views/widgets/didvan/scaffold.dart | 14 +- 27 files changed, 1015 insertions(+), 326 deletions(-) create mode 100644 lib/models/slider_data.dart rename lib/views/home/studio/studio_details/{studio_details.dart => studio_details.mobile.dart} (61%) create mode 100644 lib/views/home/studio/studio_details/studio_details.web.dart delete mode 100644 lib/views/home/studio/studio_details/widgets/studio_details.dart create mode 100644 lib/views/home/studio/studio_details/widgets/studio_details_widget.dart diff --git a/lib/main.dart b/lib/main.dart index 3ed3728..d663c2d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ import 'package:didvan/config/theme_data.dart'; import 'package:didvan/providers/theme_provider.dart'; import 'package:didvan/providers/user_provider.dart'; import 'package:didvan/routes/route_generator.dart'; +import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:provider/provider.dart'; @@ -26,6 +27,9 @@ class Didvan extends StatelessWidget { ChangeNotifierProvider( create: (context) => ThemeProvider(), ), + ChangeNotifierProvider( + create: (context) => StudioDetailsState(), + ), ], child: Consumer( builder: (context, themeProvider, child) => MaterialApp( diff --git a/lib/models/overview_data.dart b/lib/models/overview_data.dart index b72995b..6a20c71 100644 --- a/lib/models/overview_data.dart +++ b/lib/models/overview_data.dart @@ -45,7 +45,7 @@ class OverviewData { createdAt: json['createdAt'], duration: json['duration'], type: json['type'] ?? '', - marked: json['marked'] ?? false, + marked: json['marked'] ?? true, media: json['media'], categories: json['categories'] != null ? List.from( diff --git a/lib/models/slider_data.dart b/lib/models/slider_data.dart new file mode 100644 index 0000000..a99c52d --- /dev/null +++ b/lib/models/slider_data.dart @@ -0,0 +1,27 @@ +class SliderData { + final int id; + final String title; + final String image; + final String media; + + const SliderData({ + required this.id, + required this.title, + required this.image, + required this.media, + }); + + factory SliderData.fromJson(Map json) => SliderData( + id: json['id'], + title: json['title'], + image: json['image'], + media: json['media'], + ); + + Map toJson() => { + 'id': id, + 'title': title, + 'image': image, + 'media': media, + }; +} diff --git a/lib/providers/user_provider.dart b/lib/providers/user_provider.dart index 19c5192..570bb06 100644 --- a/lib/providers/user_provider.dart +++ b/lib/providers/user_provider.dart @@ -132,7 +132,7 @@ class UserProvider extends CoreProvier { final MapEntry? lastChange = _radarMarkQueue.lastWhereOrNull((item) => item.key == id); if (lastChange == null) return; - final service = RequestService(RequestHelper.markRadar(id)); + final service = RequestService(RequestHelper.mark(id, 'radar')); if (lastChange.value) { await service.post(); } else { @@ -148,7 +148,7 @@ class UserProvider extends CoreProvier { final MapEntry? lastChange = _studioMarkQueue.lastWhereOrNull((item) => item.key == id); if (lastChange == null) return; - final service = RequestService(RequestHelper.markStudio(id)); + final service = RequestService(RequestHelper.mark(id, 'studio')); if (lastChange.value) { await service.post(); } else { @@ -164,7 +164,7 @@ class UserProvider extends CoreProvier { final MapEntry? lastChange = _newsMarkQueue.lastWhereOrNull((item) => item.key == id); if (lastChange == null) return; - final service = RequestService(RequestHelper.markNews(id)); + final service = RequestService(RequestHelper.mark(id, 'news')); if (lastChange.value) { await service.post(); } else { diff --git a/lib/routes/route_generator.dart b/lib/routes/route_generator.dart index 7514e7c..a4f2222 100644 --- a/lib/routes/route_generator.dart +++ b/lib/routes/route_generator.dart @@ -25,8 +25,9 @@ import 'package:didvan/views/home/settings/direct_list/direct_list_state.dart'; import 'package:didvan/views/home/settings/general_settings/settings.dart'; import 'package:didvan/views/home/settings/general_settings/settings_state.dart'; import 'package:didvan/views/home/settings/profile/profile.dart'; -import 'package:didvan/views/home/studio/studio_details/studio_details.dart'; -import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; +import 'package:didvan/views/home/studio/studio_details/studio_details.mobile.dart' + if (dart.library.io) 'package:didvan/views/home/studio/studio_details/studio_details.mobile.dart' + if (dart.library.html) 'package:didvan/views/home/studio/studio_details/studio_details.web.dart'; import 'package:didvan/views/home/studio/studio_state.dart'; import 'package:didvan/views/splash/splash.dart'; import 'package:didvan/routes/routes.dart'; @@ -64,9 +65,6 @@ class RouteGenerator { ChangeNotifierProvider( create: (context) => StudioState(), ), - ChangeNotifierProvider( - create: (context) => StudioDetailsState(), - ), ], child: const Home(), ), @@ -106,11 +104,8 @@ class RouteGenerator { ); case Routes.studioDetails: return _createRoute( - ChangeNotifierProvider.value( - value: (settings.arguments as Map)['state'], - child: StudioDetails( - pageData: settings.arguments as Map, - ), + StudioDetails( + pageData: settings.arguments as Map, ), ); case Routes.directList: @@ -150,14 +145,15 @@ class RouteGenerator { child: Hashtag(tag: settings.arguments as Tag), ), ); - case Routes.filteredBookmarks: return _createRoute( ChangeNotifierProvider( create: (context) => FilteredBookmarksState( - settings.arguments as String, + (settings.arguments as Map)['type'], ), - child: const FilteredBookmarks(), + child: FilteredBookmarks( + onDeleted: + (settings.arguments as Map)['onDeleted']), ), ); default: diff --git a/lib/services/network/request_helper.dart b/lib/services/network/request_helper.dart index e35f070..84e2a0f 100644 --- a/lib/services/network/request_helper.dart +++ b/lib/services/network/request_helper.dart @@ -54,11 +54,6 @@ class RequestHelper { MapEntry('tags', _urlListConcatGenerator(ids)), ]); - static String markRadar(int id) => _baseRadarUrl + '/$id/mark'; - static String radarComments(int id) => _baseRadarUrl + '/$id/comments'; - static String addRadarComment(int id) => _baseRadarUrl + '/$id/comments/add'; - static String feedbackRadarComment(int radarId, int id) => - _baseRadarUrl + '/$radarId/comments/$id/feedback'; static String radarDetails(int id, RadarRequestArgs args) => _baseRadarUrl + '/$id' + @@ -79,11 +74,6 @@ class RequestHelper { MapEntry('categories', _urlListConcatGenerator(args.categories)), ]); - static String markNews(int id) => _baseNewsUrl + '/$id/mark'; - static String newsComments(int id) => _baseNewsUrl + '/$id/comments'; - static String addNewsComment(int id) => _baseNewsUrl + '/$id/comments/add'; - static String feedbackNewsComment(int radarId, int id) => - _baseNewsUrl + '/$radarId/comments/$id/feedback'; static String newsDetails(int id, NewsRequestArgs args) => _baseNewsUrl + '/$id' + @@ -102,12 +92,10 @@ class RequestHelper { MapEntry('search', args.search), ]); - static String markStudio(int id) => _baseStudioUrl + '/$id/mark'; - static String studioComments(int id) => _baseStudioUrl + '/$id/comments'; - static String addStudioComment(int id) => - _baseStudioUrl + '/$id/comments/add'; - static String feedbackStudioComment(int studioId, int id) => - _baseStudioUrl + '/$studioId/comments/$id/feedback'; + static String sudioSlider(String type) => + _baseStudioUrl + + '/slider' + + _urlConcatGenerator([MapEntry('type', type)]); static String studioDetails(int id, StudioRequestArgs args) => _baseStudioUrl + '/$id' + @@ -126,6 +114,14 @@ class RequestHelper { MapEntry('search', args.search), ]); + static String mark(int id, String type) => baseUrl + '/$type/$id/mark'; + static String comments(int id, String type) => + baseUrl + '/$type/$id/comments'; + static String feedback(int id, int commentId, String type) => + baseUrl + '/$type/$id/comments/$commentId/feedback'; + static String addComment(int id, String type) => + baseUrl + '/$type/$id/comments/add'; + static String _urlConcatGenerator(List> additions) { String result = ''; additions.removeWhere( diff --git a/lib/views/home/comments/comments.dart b/lib/views/home/comments/comments.dart index 0bf0c72..baf7d3e 100644 --- a/lib/views/home/comments/comments.dart +++ b/lib/views/home/comments/comments.dart @@ -1,6 +1,7 @@ import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; +import 'package:didvan/constants/assets.dart'; import 'package:didvan/models/view/app_bar_data.dart'; import 'package:didvan/views/home/comments/comments_state.dart'; import 'package:didvan/views/home/comments/widgets/comment_item.dart'; @@ -9,6 +10,7 @@ import 'package:didvan/views/widgets/didvan/icon_button.dart'; import 'package:didvan/views/widgets/didvan/scaffold.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/shimmer_placeholder.dart'; +import 'package:didvan/views/widgets/state_handlers/empty_state.dart'; import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -32,7 +34,7 @@ class _CommentsState extends State { void initState() { final state = context.read(); state.itemId = widget.pageData['id']; - state.isRadar = widget.pageData['isRadar']; + state.type = widget.pageData['type']; state.onCommentsChanged = widget.pageData['onCommentsChanged']; Future.delayed( Duration.zero, @@ -41,6 +43,8 @@ class _CommentsState extends State { super.initState(); } + bool get _isPage => widget.pageData['isPage'] != false; + @override Widget build(BuildContext context) { final bottomViewInset = MediaQuery.of(context).viewInsets.bottom; @@ -56,11 +60,13 @@ class _CommentsState extends State { children: [ DidvanScaffold( backgroundColor: Theme.of(context).colorScheme.surface, - appBarData: AppBarData( - hasBack: true, - title: 'نظرات', - subtitle: widget.pageData['title'], - ), + appBarData: _isPage + ? AppBarData( + hasBack: true, + title: 'نظرات', + subtitle: widget.pageData['title'], + ) + : null, padding: const EdgeInsets.only(left: 16, right: 16, bottom: 92), slivers: [ Consumer( @@ -71,6 +77,11 @@ class _CommentsState extends State { itemPadding: const EdgeInsets.symmetric(vertical: 16), childCount: state.comments.length, placeholder: const _CommentPlaceholder(), + enableEmptyState: state.comments.isEmpty, + emptyState: EmptyState( + asset: Assets.emptyChat, + title: 'اولین نظر را بنویسید...', + ), builder: (context, state, index) => Comment( focusNode: _focusNode, comment: state.comments[index], diff --git a/lib/views/home/comments/comments_state.dart b/lib/views/home/comments/comments_state.dart index ead8409..cb52186 100644 --- a/lib/views/home/comments/comments_state.dart +++ b/lib/views/home/comments/comments_state.dart @@ -17,19 +17,17 @@ class CommentsState extends CoreProvier { bool showReplyBox = false; late void Function(int count) onCommentsChanged; int _count = 0; + late String type; final List comments = []; final Map> _feedbackQueue = {}; - bool isRadar = true; int itemId = 0; Future getComments() async { appState = AppState.busy; final service = RequestService( - isRadar - ? RequestHelper.radarComments(itemId) - : RequestHelper.newsComments(itemId), + RequestHelper.comments(itemId, type), ); await service.httpGet(); if (service.isSuccess) { @@ -52,13 +50,12 @@ class CommentsState extends CoreProvier { Future.delayed(const Duration(milliseconds: 500), () async { if (!_feedbackQueue.containsKey(id)) return; final service = RequestService( - isRadar - ? RequestHelper.feedbackRadarComment(itemId, id) - : RequestHelper.feedbackNewsComment(itemId, id), - body: { - 'like': _feedbackQueue[id]!.key, - 'dislike': _feedbackQueue[id]!.value, - }); + RequestHelper.feedback(itemId, id, type), + body: { + 'like': _feedbackQueue[id]!.key, + 'dislike': _feedbackQueue[id]!.value, + }, + ); await service.put(); _feedbackQueue.remove(id); }); @@ -119,10 +116,9 @@ class CommentsState extends CoreProvier { update(); body.addAll({'text': text}); final service = RequestService( - isRadar - ? RequestHelper.addRadarComment(itemId) - : RequestHelper.addNewsComment(itemId), - body: body); + RequestHelper.addComment(itemId, type), + body: body, + ); await service.post(); if (service.isSuccess) { diff --git a/lib/views/home/settings/bookmarks/bookmarks.dart b/lib/views/home/settings/bookmarks/bookmarks.dart index 520d880..81ddeab 100644 --- a/lib/views/home/settings/bookmarks/bookmarks.dart +++ b/lib/views/home/settings/bookmarks/bookmarks.dart @@ -131,7 +131,15 @@ class _BookmarksState extends State { void _onCategorySelected(String type) { FocusScope.of(context).unfocus(); - Navigator.of(context).pushNamed(Routes.filteredBookmarks, arguments: type); + Navigator.of(context).pushNamed(Routes.filteredBookmarks, arguments: { + 'type': type, + 'onDeleted': (int id) { + final state = context.read(); + state.bookmarks + .removeWhere((element) => element.id == id && element.type == type); + state.update(); + }, + }); } void _onChanged(String value) { diff --git a/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmark.dart b/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmark.dart index 2291e1a..bc553e4 100644 --- a/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmark.dart +++ b/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmark.dart @@ -1,7 +1,10 @@ +import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/models/view/app_bar_data.dart'; import 'package:didvan/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart'; import 'package:didvan/views/home/widgets/overview/news.dart'; +import 'package:didvan/views/home/widgets/overview/podcast.dart'; import 'package:didvan/views/home/widgets/overview/radar.dart'; +import 'package:didvan/views/home/widgets/overview/video.dart'; import 'package:didvan/views/widgets/didvan/scaffold.dart'; import 'package:didvan/views/widgets/state_handlers/empty_list.dart'; import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart'; @@ -9,7 +12,8 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class FilteredBookmarks extends StatefulWidget { - const FilteredBookmarks({Key? key}) : super(key: key); + final void Function(int id)? onDeleted; + const FilteredBookmarks({Key? key, this.onDeleted}) : super(key: key); @override _FilteredBookmarksState createState() => _FilteredBookmarksState(); @@ -67,8 +71,26 @@ class _FilteredBookmarksState extends State { hasUnmarkConfirmation: true, ); } - return NewsOverview( - news: state.bookmarks[index], + if (state.type == 'news') { + return NewsOverview( + news: state.bookmarks[index], + onMarkChanged: _onBookmarkChanged, + hasUnmarkConfirmation: true, + ); + } + if (state.type == 'podcast') { + return PodcastOverview( + studioRequestArgs: + const StudioRequestArgs(page: 0, type: 'podcast'), + podcast: state.bookmarks[index], + onMarkChanged: _onBookmarkChanged, + hasUnmarkConfirmation: true, + ); + } + return VideoOverview( + studioRequestArgs: + const StudioRequestArgs(page: 0, type: 'video'), + video: state.bookmarks[index], onMarkChanged: _onBookmarkChanged, hasUnmarkConfirmation: true, ); @@ -85,5 +107,6 @@ class _FilteredBookmarksState extends State { if (value) return; final state = context.read(); state.onMarkChanged(id, false); + widget.onDeleted?.call(id); } } diff --git a/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart b/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart index 7aa0463..9cfc4d1 100644 --- a/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart +++ b/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart @@ -56,14 +56,12 @@ class FilteredBookmarksState extends CoreProvier { } void onMarkChanged(int id, bool value) { - switch (type) { - case 'radar': - UserProvider.changeRadarMark(id, value); - break; - case 'news': - UserProvider.changeNewsMark(id, value); - break; - default: + if (type == 'radar') { + UserProvider.changeRadarMark(id, value); + } else if (type == 'news') { + UserProvider.changeNewsMark(id, value); + } else { + UserProvider.changeStudioMark(id, value); } bookmarks.removeWhere((element) => element.id == id); notifyListeners(); diff --git a/lib/views/home/studio/studio.dart b/lib/views/home/studio/studio.dart index 1ffca83..79744c4 100644 --- a/lib/views/home/studio/studio.dart +++ b/lib/views/home/studio/studio.dart @@ -1,6 +1,10 @@ +import 'dart:async'; + +import 'package:didvan/config/design_config.dart'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/models/view/action_sheet_data.dart'; +import 'package:didvan/routes/routes.dart'; import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/views/home/studio/studio_state.dart'; import 'package:didvan/views/home/studio/widgets/slider.dart'; @@ -9,6 +13,7 @@ import 'package:didvan/views/home/widgets/logo_app_bar.dart'; import 'package:didvan/views/home/widgets/overview/podcast.dart'; import 'package:didvan/views/home/widgets/overview/video.dart'; import 'package:didvan/views/home/widgets/search_field.dart'; +import 'package:didvan/views/widgets/animated_visibility.dart'; import 'package:didvan/views/widgets/didvan/divider.dart'; import 'package:didvan/views/widgets/didvan/icon_button.dart'; import 'package:didvan/views/widgets/didvan/radial_button.dart'; @@ -26,6 +31,7 @@ class Studio extends StatefulWidget { class _StudioState extends State { final _focusNode = FocusNode(); + Timer? _timer; @override void initState() { @@ -46,7 +52,10 @@ class _StudioState extends State { EdgeInsets.only(top: MediaQuery.of(context).padding.top), child: DidvanIconButton( icon: DidvanIcons.bookmark_regular, - onPressed: () {}, + onPressed: () => Navigator.of(context).pushNamed( + Routes.filteredBookmarks, + arguments: context.read().type, + ), ), ), ], @@ -59,14 +68,20 @@ class _StudioState extends State { child: Padding( padding: const EdgeInsets.all(16.0), child: SearchField( - title: 'جستجو در استودیو', - onChanged: (value) {}, + title: 'استودیو', + onChanged: _onChanged, focusNode: _focusNode, ), ), ), - const SliverToBoxAdapter( - child: StudioSlider(), + SliverToBoxAdapter( + child: Consumer( + builder: (context, state, child) => AnimatedVisibility( + isVisible: !state.searching, + duration: DesignConfig.lowAnimationDuration, + child: const StudioSlider(), + ), + ), ), const SliverPadding( padding: EdgeInsets.symmetric(horizontal: 16), @@ -82,7 +97,13 @@ class _StudioState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const ItemTitle(title: 'تازه‌ترین‌ها'), + Consumer( + builder: (context, state, child) => AnimatedVisibility( + isVisible: !state.searching, + duration: DesignConfig.lowAnimationDuration, + child: ItemTitle(title: state.orderString), + ), + ), DidvanIconButton( gestureSize: 36, icon: DidvanIcons.sort_regular, @@ -108,7 +129,6 @@ class _StudioState extends State { onMarkChanged: state.changeMark, hasUnmarkConfirmation: false, video: state.studios[index], - onCommentsChanged: state.onCommentsChanged, studioRequestArgs: StudioRequestArgs( page: state.page, order: state.order, @@ -127,13 +147,25 @@ class _StudioState extends State { ), ), childCount: state.studios.length, - onRetry: () => state.getStudioOverviews(page: 1), + onRetry: () => state.getStudios(page: 1), ), ), ], ); } + void _onChanged(String value) { + final state = context.read(); + if (value.length < 4 && value.isNotEmpty || state.lastSearch == value) { + return; + } + _timer?.cancel(); + _timer = Timer(const Duration(seconds: 1), () { + state.search = value; + state.getStudios(page: 1); + }); + } + void _showSortDialog() { final state = context.read(); ActionSheetUtils.showBottomSheet( @@ -142,7 +174,7 @@ class _StudioState extends State { builder: (context, setState) => Column( children: [ DidvanRadialButton( - title: 'جدیدترین‌ها', + title: 'تازه‌ترین‌ها', onSelected: () => setState( () => state.selectedSortTypeIndex = 0, ), @@ -171,7 +203,7 @@ class _StudioState extends State { titleIcon: DidvanIcons.sort_regular, hasDismissButton: false, confrimTitle: 'مرتب سازی', - onConfirmed: () => state.getStudioOverviews(page: 1), + onConfirmed: () => state.getStudios(page: 1), ), ); } diff --git a/lib/views/home/studio/studio_details/studio_details.dart b/lib/views/home/studio/studio_details/studio_details.mobile.dart similarity index 61% rename from lib/views/home/studio/studio_details/studio_details.dart rename to lib/views/home/studio/studio_details/studio_details.mobile.dart index 20ce528..4756477 100644 --- a/lib/views/home/studio/studio_details/studio_details.dart +++ b/lib/views/home/studio/studio_details/studio_details.mobile.dart @@ -1,13 +1,17 @@ import 'dart:io'; import 'package:didvan/config/design_config.dart'; +import 'package:didvan/config/theme_data.dart'; import 'package:didvan/models/view/app_bar_data.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; +import 'package:didvan/views/home/studio/studio_details/widgets/studio_details_widget.dart'; import 'package:didvan/views/widgets/didvan/scaffold.dart'; import 'package:didvan/views/widgets/state_handlers/state_handler.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; + import 'package:webview_flutter/webview_flutter.dart'; class StudioDetails extends StatefulWidget { @@ -20,6 +24,7 @@ class StudioDetails extends StatefulWidget { } class _StudioDetailsState extends State { + final _scrollController = ScrollController(); bool _isFullScreen = false; bool _isInit = true; @@ -29,14 +34,12 @@ class _StudioDetailsState extends State { @override void initState() { final state = context.read(); - Future.delayed( Duration.zero, () => state.getStudioDetails(widget.pageData['id']), ); - state.args = widget.pageData['args']; - if (Platform.isAndroid) WebView.platform = AndroidWebView(); + if (!kIsWeb && Platform.isAndroid) WebView.platform = AndroidWebView(); super.initState(); } @@ -77,39 +80,44 @@ class _StudioDetailsState extends State { _scaleInPortrait = _dwInPortrait / 576; _isInit = false; } + return Consumer( builder: (context, state, child) => StateHandler( state: state, onRetry: () => state.getStudioDetails(state.currentStudio.id), - builder: (context, state) => state.studios.isEmpty - ? const SizedBox() - : WillPopScope( - onWillPop: () async { - if (_isFullScreen) { - await _changeFullSceen(false); - return false; - } - return true; - }, - child: DidvanScaffold( - backgroundColor: Theme.of(context).colorScheme.surface, - padding: EdgeInsets.zero, - appBarData: _isFullScreen - ? null - : AppBarData( - isSmall: true, - title: state.currentStudio.title, - ), - children: [ - SizedBox( - width: ds.width, - height: _isFullScreen ? ds.height : ds.width * 9 / 16, - child: Stack( - children: [ - WebView( - allowsInlineMediaPlayback: true, - initialUrl: Uri.dataFromString( - ''' + builder: (context, state) { + if (state.studios.isEmpty) { + return const SizedBox(); + } + return WillPopScope( + onWillPop: () async { + if (_isFullScreen) { + await _changeFullSceen(false); + return false; + } + return true; + }, + child: DidvanScaffold( + scrollController: _scrollController, + backgroundColor: Theme.of(context).colorScheme.surface, + padding: EdgeInsets.zero, + appBarData: _isFullScreen + ? null + : AppBarData( + isSmall: true, + title: state.currentStudio.title, + ), + children: [ + SizedBox( + width: ds.width, + height: _isFullScreen ? ds.height : ds.width * 9 / 16, + child: Stack( + children: [ + WebView( + backgroundColor: Theme.of(context).colorScheme.black, + allowsInlineMediaPlayback: true, + initialUrl: Uri.dataFromString( + ''' { ''', - mimeType: 'text/html', - ).toString(), - javascriptMode: JavascriptMode.unrestricted, - ), - Positioned( - right: 42, - bottom: 8, - child: GestureDetector( - onTap: () => _changeFullSceen(!_isFullScreen), - child: Container( - color: Colors.transparent, - width: 24, - height: 30, - ), - ), - ), - ], + mimeType: 'text/html', + ).toString(), + javascriptMode: JavascriptMode.unrestricted, ), - ), - ], + Positioned( + right: 42, + bottom: 8, + child: GestureDetector( + onTap: () => _changeFullSceen(!_isFullScreen), + child: Container( + color: Colors.transparent, + width: 24, + height: 30, + ), + ), + ), + ], + ), ), - ), + const SizedBox(height: 20), + StudioDetailsWidget( + onCommentsTabSelected: () => _scrollController.animateTo( + _scrollController.position.maxScrollExtent, + duration: DesignConfig.lowAnimationDuration, + curve: Curves.easeIn, + ), + studio: state.currentStudio, + ), + ], + ), + ); + }, ), ); } diff --git a/lib/views/home/studio/studio_details/studio_details.web.dart b/lib/views/home/studio/studio_details/studio_details.web.dart new file mode 100644 index 0000000..c186998 --- /dev/null +++ b/lib/views/home/studio/studio_details/studio_details.web.dart @@ -0,0 +1,210 @@ +import 'dart:io'; +import 'dart:ui' as ui; + +import 'package:didvan/config/design_config.dart'; +import 'package:didvan/config/theme_data.dart'; +import 'package:didvan/models/view/app_bar_data.dart'; +import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; +import 'package:didvan/views/home/studio/studio_details/widgets/studio_details_widget.dart'; +import 'package:didvan/views/widgets/didvan/scaffold.dart'; +import 'package:didvan/views/widgets/state_handlers/state_handler.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:provider/provider.dart'; +import 'package:universal_html/html.dart' as html; +import 'package:webview_flutter/webview_flutter.dart'; + +class StudioDetails extends StatefulWidget { + final Map pageData; + + const StudioDetails({Key? key, required this.pageData}) : super(key: key); + + @override + State createState() => _StudioDetailsState(); +} + +class _StudioDetailsState extends State { + final _scrollController = ScrollController(); + + bool _isFullScreen = false; + bool _isInit = true; + + double _dwInPortrait = 0; + double _scaleInPortrait = 1; + + @override + void initState() { + final state = context.read(); + Future.delayed( + Duration.zero, + () => state.getStudioDetails(widget.pageData['id']), + ); + state.args = widget.pageData['args']; + if (!kIsWeb && Platform.isAndroid) WebView.platform = AndroidWebView(); + super.initState(); + } + + Future _changeFullSceen(bool value) async { + if (value) { + await SystemChrome.setEnabledSystemUIMode( + SystemUiMode.manual, + overlays: [], + ); + SystemChrome.setSystemUIOverlayStyle( + const SystemUiOverlayStyle( + systemNavigationBarColor: Colors.black, + ), + ); + await SystemChrome.setPreferredOrientations( + [DeviceOrientation.landscapeLeft], + ); + } else { + await SystemChrome.setEnabledSystemUIMode( + SystemUiMode.manual, + overlays: [SystemUiOverlay.bottom, SystemUiOverlay.top], + ); + await SystemChrome.setPreferredOrientations( + [DeviceOrientation.portraitUp], + ); + DesignConfig.updateSystemUiOverlayStyle(); + } + setState(() { + _isFullScreen = value; + }); + } + + @override + Widget build(BuildContext context) { + final ds = MediaQuery.of(context).size; + if (_isInit) { + _dwInPortrait = MediaQuery.of(context).size.width; + _scaleInPortrait = _dwInPortrait / 576; + _isInit = false; + } + + return Consumer( + builder: (context, state, child) => StateHandler( + state: state, + onRetry: () => state.getStudioDetails(state.currentStudio.id), + builder: (context, state) { + if (state.studios.isEmpty) { + return const SizedBox(); + } + if (kIsWeb) { + // ignore: undefined_prefixed_name + ui.platformViewRegistry.registerViewFactory( + "video", + (int viewId) => html.IFrameElement() + ..allowFullscreen = true + ..src = Uri.dataFromString( + '' + + state.currentStudio.media, + mimeType: 'text/html', + ).toString() + ..style.border = 'none', + ); + } + return WillPopScope( + onWillPop: () async { + if (_isFullScreen) { + await _changeFullSceen(false); + return false; + } + return true; + }, + child: DidvanScaffold( + scrollController: _scrollController, + padding: EdgeInsets.zero, + appBarData: _isFullScreen + ? null + : AppBarData( + isSmall: true, + title: state.currentStudio.title, + ), + children: [ + if (kIsWeb) + const AspectRatio( + aspectRatio: 16 / 9, + child: HtmlElementView(viewType: 'video'), + ), + if (!kIsWeb) + SizedBox( + width: ds.width, + height: _isFullScreen ? ds.height : ds.width * 9 / 16, + child: Stack( + children: [ + WebView( + backgroundColor: Theme.of(context).colorScheme.black, + allowsInlineMediaPlayback: true, + initialUrl: Uri.dataFromString( + ''' + + + + + + + ${state.currentStudio.media} + + + ''', + mimeType: 'text/html', + ).toString(), + javascriptMode: JavascriptMode.unrestricted, + ), + if (!kIsWeb) + Positioned( + right: 42, + bottom: 8, + child: GestureDetector( + onTap: () => _changeFullSceen(!_isFullScreen), + child: Container( + color: Colors.transparent, + width: 24, + height: 30, + ), + ), + ), + ], + ), + ), + const SizedBox(height: 20), + StudioDetailsWidget( + onCommentsTabSelected: () => _scrollController.animateTo( + _scrollController.position.maxScrollExtent, + duration: DesignConfig.lowAnimationDuration, + curve: Curves.easeIn, + ), + studio: state.currentStudio, + ), + ], + ), + ); + }, + ), + ); + } +} diff --git a/lib/views/home/studio/studio_details/studio_details_state.dart b/lib/views/home/studio/studio_details/studio_details_state.dart index 0a6205b..7ef097c 100644 --- a/lib/views/home/studio/studio_details/studio_details_state.dart +++ b/lib/views/home/studio/studio_details/studio_details_state.dart @@ -17,6 +17,7 @@ class StudioDetailsState extends CoreProvier { int _selectedDetailsIndex = 0; bool isFetchingNewItem = false; final List relatedQueue = []; + bool currentTypeIsVideo = true; int _currentIndex = 0; int get currentIndex => _currentIndex; @@ -24,6 +25,9 @@ class StudioDetailsState extends CoreProvier { int get selectedDetailsIndex => _selectedDetailsIndex; set selectedDetailsIndex(int value) { _selectedDetailsIndex = value; + if (value == 2) { + getRelatedContents(); + } notifyListeners(); } @@ -35,12 +39,16 @@ class StudioDetailsState extends CoreProvier { } } - Future getStudioDetails(int id, - {bool? isForward, StudioRequestArgs? args}) async { + Future getStudioDetails( + int id, { + bool? isForward, + StudioRequestArgs? args, + }) async { if (args != null) { this.args = args; } if (isForward == null) { + _selectedDetailsIndex = 0; appState = AppState.busy; } else { isFetchingNewItem = true; @@ -49,22 +57,16 @@ class StudioDetailsState extends CoreProvier { final service = RequestService(RequestHelper.studioDetails(id, this.args)); await service.httpGet(); if (service.isSuccess) { + studios.clear(); final result = service.result; final studio = StudioDetailsData.fromJson(result['studio']); + await _handlePodcastPlayback(studio); if (this.args.page == 0) { studios.add(studio); initialIndex = 0; appState = AppState.idle; return; } - if (this.args.type == 'podcast') { - MediaService.currentPodcast = studio; - MediaService.podcastPlaylistArgs = args; - await MediaService.handleAudioPlayback( - audioSource: studio.media, - isVoiceMessage: false, - ); - } StudioDetailsData? prevStudio; if (result['prevStudio'].isNotEmpty) { @@ -108,13 +110,24 @@ class StudioDetailsState extends CoreProvier { } } + Future _handlePodcastPlayback(StudioDetailsData studio) async { + if (args.type == 'podcast') { + MediaService.currentPodcast = studio; + MediaService.podcastPlaylistArgs = args; + await MediaService.handleAudioPlayback( + audioSource: studio.media, + isVoiceMessage: false, + ); + } + } + Future getRelatedContents() async { if (currentStudio.relatedContents.isNotEmpty) return; relatedQueue.add(currentStudio.id); final service = RequestService(RequestHelper.tag( ids: currentStudio.tags.map((tag) => tag.id).toList(), itemId: currentStudio.id, - type: 'studio', + type: currentStudio.media.contains('iframe') ? 'video' : 'podcast', )); await service.httpGet(); if (service.isSuccess) { diff --git a/lib/views/home/studio/studio_details/widgets/studio_details.dart b/lib/views/home/studio/studio_details/widgets/studio_details.dart deleted file mode 100644 index 059b4cc..0000000 --- a/lib/views/home/studio/studio_details/widgets/studio_details.dart +++ /dev/null @@ -1,97 +0,0 @@ -import 'package:didvan/config/theme_data.dart'; -import 'package:didvan/constants/app_icons.dart'; -import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; -import 'package:didvan/views/widgets/didvan/text.dart'; -import 'package:didvan/views/widgets/state_handlers/state_handler.dart'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; - -class StudioDetailsWidget extends StatelessWidget { - const StudioDetailsWidget({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Consumer( - builder: (context, state, child) => StateHandler( - onRetry: () {}, - state: state, - builder: (context, state) => Container( - color: Theme.of(context).colorScheme.surface, - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - _TabItem( - icon: DidvanIcons.description_solid, - title: 'توضیحات', - onTap: () => state.selectedDetailsIndex = 0, - isSelected: state.selectedDetailsIndex == 0, - ), - _TabItem( - icon: DidvanIcons.chats_solid, - title: 'نظرات', - onTap: () => state.selectedDetailsIndex = 1, - isSelected: state.selectedDetailsIndex == 1, - ), - _TabItem( - icon: DidvanIcons.puzzle_solid, - title: 'مطالب مرتبط', - onTap: () => state.selectedDetailsIndex = 2, - isSelected: state.selectedDetailsIndex == 2, - ), - ], - ), - const SizedBox(height: 16), - ], - ), - ), - ), - ); - } -} - -class _TabItem extends StatelessWidget { - final IconData icon; - final String title; - final VoidCallback onTap; - final bool isSelected; - const _TabItem({ - Key? key, - required this.icon, - required this.title, - required this.onTap, - required this.isSelected, - }) : super(key: key); - - Color? _color(context) => - isSelected ? Theme.of(context).colorScheme.focusedBorder : null; - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: onTap, - child: Container( - color: Colors.transparent, - child: Column( - children: [ - Icon( - icon, - color: _color(context), - ), - Container( - width: 64, - height: 1, - color: _color(context), - ), - DidvanText( - title, - color: _color(context), - style: Theme.of(context).textTheme.caption, - ) - ], - ), - ), - ); - } -} diff --git a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart new file mode 100644 index 0000000..1844c5d --- /dev/null +++ b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart @@ -0,0 +1,272 @@ +import 'package:didvan/config/theme_data.dart'; +import 'package:didvan/constants/app_icons.dart'; +import 'package:didvan/models/studio_details_data.dart'; +import 'package:didvan/views/home/comments/comments.dart'; +import 'package:didvan/views/home/comments/comments_state.dart'; +import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; +import 'package:didvan/views/home/widgets/overview/multitype.dart'; +import 'package:didvan/views/home/widgets/tag_item.dart'; +import 'package:didvan/views/widgets/didvan/text.dart'; +import 'package:didvan/views/widgets/skeleton_image.dart'; +import 'package:didvan/views/widgets/state_handlers/state_handler.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class StudioDetailsWidget extends StatelessWidget { + final StudioDetailsData studio; + final VoidCallback onCommentsTabSelected; + const StudioDetailsWidget({ + Key? key, + required this.studio, + required this.onCommentsTabSelected, + }) : super(key: key); + + bool get _isVideo => studio.media.contains('ifram'); + + @override + Widget build(BuildContext context) { + final ds = MediaQuery.of(context).size; + return Consumer( + builder: (context, state, child) => Container( + color: Theme.of(context).colorScheme.surface, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + const SizedBox(), + _TabItem( + icon: DidvanIcons.description_solid, + title: 'توضیحات', + onTap: () => state.selectedDetailsIndex = 0, + isSelected: state.selectedDetailsIndex == 0, + isVideo: _isVideo, + ), + _TabItem( + icon: DidvanIcons.chats_solid, + title: 'نظرات', + onTap: () { + state.selectedDetailsIndex = 1; + onCommentsTabSelected(); + }, + isSelected: state.selectedDetailsIndex == 1, + isVideo: _isVideo, + ), + _TabItem( + icon: DidvanIcons.puzzle_solid, + title: 'مطالب مرتبط', + onTap: () => state.selectedDetailsIndex = 2, + isSelected: state.selectedDetailsIndex == 2, + isVideo: _isVideo, + ), + const SizedBox(), + ], + ), + const SizedBox(height: 24), + StateHandler( + onRetry: () {}, + state: state, + builder: (context, state) { + if (state.selectedDetailsIndex == 0) { + return SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + DidvanText(state.currentStudio.description), + if (studio.tags.isNotEmpty) const SizedBox(height: 20), + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + for (var i = 0; i < studio.tags.length; i++) + TagItem(tag: studio.tags[i]), + ], + ), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const SizedBox(), + if (state.studios.length != state.currentIndex + 1) + _StudioPreview( + isNext: true, + studio: state.studios[state.currentIndex + 1]!, + ), + if (state.currentIndex != 0) + _StudioPreview( + isNext: false, + studio: state.studios[state.currentIndex - 1]!, + ), + const SizedBox(), + ], + ) + ], + ), + ); + } + if (state.selectedDetailsIndex == 1) { + return ChangeNotifierProvider( + create: (context) => CommentsState(), + child: SizedBox( + height: ds.height - 180, + child: Comments( + pageData: { + 'id': studio.id, + 'type': 'studio', + 'title': studio.title, + 'onCommentsChanged': state.onCommentsChanged, + 'isPage': false, + }, + ), + ), + ); + } + return Column( + children: [ + if (studio.relatedContents.isEmpty) + for (var i = 0; i < 3; i++) + Padding( + padding: const EdgeInsets.only( + bottom: 8, + left: 16, + right: 16, + ), + child: MultitypeOverview.placeholder, + ), + for (var i = 0; i < studio.relatedContents.length; i++) + Padding( + padding: const EdgeInsets.only( + bottom: 8, + left: 16, + right: 16, + ), + child: MultitypeOverview( + item: studio.relatedContents[i], + onMarkChanged: (id, value) {}, + ), + ), + ], + ); + }, + ), + ], + ), + ), + ); + } +} + +class _TabItem extends StatelessWidget { + final IconData icon; + final String title; + final VoidCallback onTap; + final bool isSelected; + final bool isVideo; + const _TabItem({ + Key? key, + required this.icon, + required this.title, + required this.onTap, + required this.isSelected, + required this.isVideo, + }) : super(key: key); + + Color? _color(context) { + if (isSelected) { + if (isVideo) { + return Theme.of(context).colorScheme.secondary; + } + return Theme.of(context).colorScheme.focusedBorder; + } + return Theme.of(context).colorScheme.border; + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: Container( + color: Colors.transparent, + child: Column( + children: [ + Icon( + icon, + color: _color(context), + ), + if (isSelected) const SizedBox(height: 8), + if (isSelected) + Container( + width: 64, + height: 1, + color: _color(context), + ), + if (isSelected) + DidvanText( + title, + color: _color(context), + style: Theme.of(context).textTheme.caption, + ) + ], + ), + ), + ); + } +} + +class _StudioPreview extends StatelessWidget { + final bool isNext; + final StudioDetailsData studio; + const _StudioPreview({Key? key, required this.isNext, required this.studio}) + : super(key: key); + + String get _previewTitle { + if (studio.media.contains('iframe')) { + return 'ویدئو ${isNext ? 'بعدی' : 'قبلی'} '; + } + return 'پادکست ${isNext ? 'بعدی' : 'قبلی'} '; + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + final state = context.read(); + state.getStudioDetails(studio.id, args: state.args); + }, + child: Container( + width: 88, + color: Colors.transparent, + child: Column( + children: [ + SkeletonImage( + imageUrl: studio.image, + aspectRatio: 1 / 1, + ), + const SizedBox(height: 8), + Icon( + isNext + ? DidvanIcons.angle_right_regular + : DidvanIcons.angle_left_regular, + ), + const SizedBox(height: 8), + DidvanText( + _previewTitle, + style: Theme.of(context).textTheme.caption, + textAlign: TextAlign.center, + ), + const SizedBox(height: 8), + DidvanText( + studio.title, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.overline, + color: Theme.of(context).colorScheme.caption, + ), + ], + ), + ), + ); + } +} diff --git a/lib/views/home/studio/studio_state.dart b/lib/views/home/studio/studio_state.dart index 18b7dc2..115534a 100644 --- a/lib/views/home/studio/studio_state.dart +++ b/lib/views/home/studio/studio_state.dart @@ -1,6 +1,7 @@ import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/requests/studio.dart'; +import 'package:didvan/models/slider_data.dart'; import 'package:didvan/providers/core_provider.dart'; import 'package:didvan/providers/user_provider.dart'; import 'package:didvan/services/network/request.dart'; @@ -8,9 +9,10 @@ import 'package:didvan/services/network/request_helper.dart'; class StudioState extends CoreProvier { final List studios = []; + final List sliders = []; - String? search; - String? lastSearch; + String search = ''; + String lastSearch = ''; int page = 1; int lastPage = 1; @@ -20,21 +22,13 @@ class StudioState extends CoreProvier { bool get videosSelected => _videosSelected; + bool get searching => search.isNotEmpty; + set videosSelected(bool value) { if (_videosSelected == value) return; _videosSelected = value; - studios.clear(); - getStudioOverviews(page: page); - } - - void init() { - search = ''; - lastSearch = ''; - _videosSelected = true; - selectedSortTypeIndex = 0; - Future.delayed(Duration.zero, () { - getStudioOverviews(page: 1); - }); + _getSliders(); + getStudios(page: page); } String get order { @@ -43,13 +37,46 @@ class StudioState extends CoreProvier { return 'comment'; } + String get orderString { + if (selectedSortTypeIndex == 0) return 'تازه‌ترین‌ها'; + if (selectedSortTypeIndex == 1) return 'پربازدیدترین‌ها'; + return 'پربحث‌نرین‌ها'; + } + String get type { if (videosSelected) return 'video'; return 'podcast'; } - Future getStudioOverviews({required int page}) async { + void init() { + search = ''; + lastSearch = ''; + _videosSelected = true; + selectedSortTypeIndex = 0; + Future.delayed(Duration.zero, () { + _getSliders(); + getStudios(page: 1); + }); + } + + Future _getSliders() async { + final service = RequestService( + RequestHelper.sudioSlider(type), + ); + await service.httpGet(); + if (service.isSuccess) { + sliders.clear(); + final sliderItems = service.result['studios']; + for (var i = 0; i < sliderItems.length; i++) { + sliders.add(SliderData.fromJson(sliderItems[i])); + } + } + notifyListeners(); + } + + Future getStudios({required int page}) async { this.page = page; + lastSearch = search; if (page == 1) { appState = AppState.busy; } diff --git a/lib/views/home/studio/widgets/slider.dart b/lib/views/home/studio/widgets/slider.dart index d312b2b..39e5bc4 100644 --- a/lib/views/home/studio/widgets/slider.dart +++ b/lib/views/home/studio/widgets/slider.dart @@ -1,28 +1,92 @@ import 'package:carousel_slider/carousel_slider.dart'; +import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; +import 'package:didvan/models/enums.dart'; +import 'package:didvan/views/home/studio/studio_state.dart'; +import 'package:didvan/views/widgets/didvan/text.dart'; +import 'package:didvan/views/widgets/shimmer_placeholder.dart'; +import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; -class StudioSlider extends StatelessWidget { +class StudioSlider extends StatefulWidget { const StudioSlider({Key? key}) : super(key: key); + @override + State createState() => _StudioSliderState(); +} + +class _StudioSliderState extends State { + int selectedIndex = 0; + @override Widget build(BuildContext context) { + final state = context.watch(); return Column( children: [ CarouselSlider( items: [ - Image.network('https://wallpapercave.com/wp/wp10731650.jpg'), - Image.network('https://wallpapercave.com/wp/wp10731650.jpg'), - Image.network('https://wallpapercave.com/wp/wp10731650.jpg'), - Image.network('https://wallpapercave.com/wp/wp10731650.jpg'), + for (var i = 0; i < state.sliders.length; i++) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: state.appState == AppState.busy + ? const ShimmerPlaceholder() + : Stack( + children: [ + SkeletonImage( + borderRadius: DesignConfig.mediumBorderRadius, + imageUrl: state.sliders[i].image, + width: double.infinity, + ), + Positioned( + bottom: 0, + left: 0, + right: 0, + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 4, + horizontal: 8, + ), + decoration: BoxDecoration( + color: (state.videosSelected + ? Theme.of(context) + .colorScheme + .secondaryDisabled + : Theme.of(context).colorScheme.focused) + .withOpacity(0.9), + borderRadius: const BorderRadius.vertical( + bottom: Radius.circular(10), + ), + ), + child: DidvanText( + state.sliders[i].title, + color: Theme.of(context).colorScheme.title, + style: Theme.of(context).textTheme.caption, + ), + ), + ), + ], + ), + ), ], options: CarouselOptions( + onPageChanged: (index, reason) => setState( + () => selectedIndex = index, + ), viewportFraction: 0.94, aspectRatio: 16 / 9, autoPlay: true, ), ), - Row(), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + for (var i = 0; i < state.sliders.length; i++) + _SliderIndicator(isCurrentIndex: selectedIndex == i), + ], + ), + const SizedBox(height: 16), ], ); } @@ -35,9 +99,11 @@ class _SliderIndicator extends StatelessWidget { @override Widget build(BuildContext context) { - return Container( + return AnimatedContainer( + duration: DesignConfig.lowAnimationDuration, height: 8, width: 8, + margin: const EdgeInsets.only(left: 4), decoration: BoxDecoration( border: Border.all( color: Theme.of(context).colorScheme.focusedBorder, diff --git a/lib/views/home/widgets/audio/audio_player_widget.dart b/lib/views/home/widgets/audio/audio_player_widget.dart index d369b10..dbdc64f 100644 --- a/lib/views/home/widgets/audio/audio_player_widget.dart +++ b/lib/views/home/widgets/audio/audio_player_widget.dart @@ -1,8 +1,11 @@ +import 'dart:math'; + import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/studio_details_data.dart'; import 'package:didvan/services/media/media.dart'; +import 'package:didvan/views/home/studio/studio_state.dart'; import 'package:didvan/views/home/widgets/audio/audio_slider.dart'; import 'package:didvan/views/home/widgets/bookmark_button.dart'; import 'package:didvan/views/widgets/didvan/icon_button.dart'; @@ -10,6 +13,7 @@ import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/ink_wrapper.dart'; import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; class AudioPlayerWidget extends StatelessWidget { final StudioDetailsData podcast; @@ -77,8 +81,13 @@ class AudioPlayerWidget extends StatelessWidget { const DidvanText('30', isEnglishFont: true), ], ), - _PlayPouseAnimatedIcon( - audioSource: podcast.media, + StreamBuilder( + stream: MediaService.audioPlayer.playingStream, + builder: (context, snapshot) { + return _PlayPouseAnimatedIcon( + audioSource: podcast.media, + ); + }, ), Column( children: [ @@ -88,8 +97,10 @@ class AudioPlayerWidget extends StatelessWidget { onPressed: () { MediaService.audioPlayer.seek( Duration( - seconds: - MediaService.audioPlayer.position.inSeconds - 10, + seconds: max( + 0, + MediaService.audioPlayer.position.inSeconds - 10, + ), ), ); }, @@ -100,7 +111,8 @@ class AudioPlayerWidget extends StatelessWidget { BookmarkButton( gestureSize: 48, value: podcast.marked, - onMarkChanged: (value) {}, + onMarkChanged: (value) => + context.read().changeMark(podcast.id, value), ), ], ), @@ -123,6 +135,12 @@ class __PlayPouseAnimatedIconState extends State<_PlayPouseAnimatedIcon> with SingleTickerProviderStateMixin { late final AnimationController _animationController; + @override + void didUpdateWidget(covariant _PlayPouseAnimatedIcon oldWidget) { + _handleAnimation(); + super.didUpdateWidget(oldWidget); + } + @override void initState() { super.initState(); @@ -130,8 +148,13 @@ class __PlayPouseAnimatedIconState extends State<_PlayPouseAnimatedIcon> vsync: this, duration: DesignConfig.lowAnimationDuration, ); + } + + void _handleAnimation() { if (MediaService.audioPlayer.playing) { _animationController.forward(); + } else { + _animationController.reverse(); } } @@ -144,11 +167,7 @@ class __PlayPouseAnimatedIconState extends State<_PlayPouseAnimatedIcon> audioSource: widget.audioSource, isVoiceMessage: false, ); - if (MediaService.audioPlayer.playing) { - _animationController.forward(); - } else { - _animationController.reverse(); - } + _handleAnimation(); }, child: Container( padding: const EdgeInsets.all(8), diff --git a/lib/views/home/widgets/bnb.dart b/lib/views/home/widgets/bnb.dart index a7651be..2a9d722 100644 --- a/lib/views/home/widgets/bnb.dart +++ b/lib/views/home/widgets/bnb.dart @@ -1,9 +1,11 @@ import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; +import 'package:didvan/models/enums.dart'; import 'package:didvan/services/media/media.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; -import 'package:didvan/views/home/studio/studio_details/widgets/studio_details.dart'; +import 'package:didvan/views/home/studio/studio_details/widgets/studio_details_widget.dart'; +import 'package:didvan/views/home/studio/studio_state.dart'; import 'package:didvan/views/home/widgets/audio/audio_player_widget.dart'; import 'package:didvan/views/home/widgets/audio/audio_slider.dart'; import 'package:didvan/views/widgets/didvan/icon_button.dart'; @@ -179,46 +181,65 @@ class DidvanBNB extends StatelessWidget { final sheetKey = GlobalKey(); bool isExpanded = false; final detailsState = context.read(); + final state = context.read(); showModalBottomSheet( backgroundColor: Colors.transparent, context: context, isScrollControlled: true, - builder: (context) => ChangeNotifierProvider.value( - value: detailsState, - child: ExpandableBottomSheet( - key: sheetKey, - background: const SizedBox(), - persistentHeader: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - AudioPlayerWidget( - podcast: MediaService.currentPodcast!, - ), - Container( - width: MediaQuery.of(context).size.width, + builder: (context) => ChangeNotifierProvider.value( + value: state, + child: Consumer( + builder: (context, state, child) => ExpandableBottomSheet( + key: sheetKey, + background: Align( + alignment: Alignment.bottomCenter, + child: Container( + height: MediaQuery.of(context).size.height * 0.7, color: Theme.of(context).colorScheme.surface, - child: Column( - children: [ - DidvanIconButton( - size: 32, - icon: DidvanIcons.angle_down_regular, - onPressed: () { - if (!isExpanded) { - sheetKey.currentState?.expand(); - isExpanded = true; - return; - } - isExpanded = false; - sheetKey.currentState?.contract(); - }, - ), - const SizedBox(height: 16), - ], - ), ), - ], + ), + persistentHeader: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + AudioPlayerWidget( + podcast: MediaService.currentPodcast!, + ), + Container( + width: MediaQuery.of(context).size.width, + color: Theme.of(context).colorScheme.surface, + child: Column( + children: [ + DidvanIconButton( + size: 32, + icon: DidvanIcons.angle_down_regular, + onPressed: () { + if (!isExpanded) { + sheetKey.currentState?.expand(); + isExpanded = true; + return; + } + isExpanded = false; + sheetKey.currentState?.contract(); + }, + ), + const SizedBox(height: 16), + ], + ), + ), + ], + ), + expandableContent: state.appState == AppState.busy + ? const SizedBox() + : StudioDetailsWidget( + studio: detailsState.currentStudio, + onCommentsTabSelected: () { + Future.delayed( + const Duration(milliseconds: 100), + sheetKey.currentState?.expand, + ); + }, + ), ), - expandableContent: const StudioDetailsWidget(), ), ), ); diff --git a/lib/views/home/widgets/floating_navigation_bar.dart b/lib/views/home/widgets/floating_navigation_bar.dart index 28a88e4..dd453a9 100644 --- a/lib/views/home/widgets/floating_navigation_bar.dart +++ b/lib/views/home/widgets/floating_navigation_bar.dart @@ -130,7 +130,7 @@ class _FloatingNavigationBarState extends State { Routes.comments, arguments: { 'id': widget.item.id, - 'isRadar': widget.isRadar, + 'type': widget.isRadar ? 'radar' : 'news', 'title': widget.item.title, 'onCommentsChanged': widget.onCommentsChanged, }, diff --git a/lib/views/home/widgets/overview/multitype.dart b/lib/views/home/widgets/overview/multitype.dart index 0076a05..6e5a5aa 100644 --- a/lib/views/home/widgets/overview/multitype.dart +++ b/lib/views/home/widgets/overview/multitype.dart @@ -3,13 +3,16 @@ import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/requests/news.dart'; import 'package:didvan/models/requests/radar.dart'; +import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/routes/routes.dart'; +import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; import 'package:didvan/views/widgets/didvan/card.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/shimmer_placeholder.dart'; import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:flutter/material.dart'; import 'package:persian_number_utility/persian_number_utility.dart'; +import 'package:provider/provider.dart'; class MultitypeOverview extends StatelessWidget { final OverviewData item; @@ -23,20 +26,60 @@ class MultitypeOverview extends StatelessWidget { this.hasUnmarkConfirmation = false, }) : super(key: key); + get _targetPageArgs { + if (item.type == 'radar') { + return const RadarRequestArgs(page: 0); + } + if (item.type == 'news') { + return const NewsRequestArgs(page: 0); + } + return StudioRequestArgs(page: 0, type: item.type); + } + + String get _targetPageRouteName { + if (item.type == 'radar') { + return Routes.radarDetails; + } + if (item.type == 'news') { + return Routes.newsDetails; + } + return Routes.studioDetails; + } + + IconData get _icon { + if (item.type == 'radar') { + return DidvanIcons.radar_light; + } + if (item.type == 'news') { + return DidvanIcons.news_light; + } + if (item.type == 'video') { + return DidvanIcons.video_light; + } + return DidvanIcons.podcast_light; + } + @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, - }, - ), + onTap: () { + if (item.type == 'podcast') { + context.read().getStudioDetails( + item.id, + args: StudioRequestArgs(page: 0, type: item.type), + ); + return; + } + Navigator.of(context).pushNamed( + _targetPageRouteName, + arguments: { + 'onMarkChanged': onMarkChanged, + 'id': item.id, + 'args': _targetPageArgs, + 'hasUnmarkConfirmation': hasUnmarkConfirmation, + }, + ); + }, child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -52,9 +95,7 @@ class MultitypeOverview extends StatelessWidget { ), ), child: Icon( - item.type == 'radar' - ? DidvanIcons.radar_light - : DidvanIcons.news_light, + _icon, color: Theme.of(context).colorScheme.white, size: 18, ), diff --git a/lib/views/home/widgets/overview/podcast.dart b/lib/views/home/widgets/overview/podcast.dart index 1a243df..5476de4 100644 --- a/lib/views/home/widgets/overview/podcast.dart +++ b/lib/views/home/widgets/overview/podcast.dart @@ -19,11 +19,13 @@ class PodcastOverview extends StatelessWidget { final OverviewData podcast; final void Function(int id, bool value) onMarkChanged; final StudioRequestArgs? studioRequestArgs; + final bool hasUnmarkConfirmation; const PodcastOverview({ Key? key, required this.podcast, required this.onMarkChanged, this.studioRequestArgs, + this.hasUnmarkConfirmation = false, }) : super(key: key); @override @@ -82,6 +84,7 @@ class PodcastOverview extends StatelessWidget { ), const SizedBox(width: 16), BookmarkButton( + askForConfirmation: hasUnmarkConfirmation, gestureSize: 24, value: podcast.marked, onMarkChanged: (value) => onMarkChanged(podcast.id, value), diff --git a/lib/views/home/widgets/overview/radar.dart b/lib/views/home/widgets/overview/radar.dart index cd88ff3..d74f422 100644 --- a/lib/views/home/widgets/overview/radar.dart +++ b/lib/views/home/widgets/overview/radar.dart @@ -117,7 +117,7 @@ class RadarOverview extends StatelessWidget { onPressed: () => Navigator.of(context).pushNamed( Routes.comments, arguments: { - 'isRadar': true, + 'type': 'radar', 'title': radar.title, 'id': radar.id, 'onCommentsChanged': (count) => diff --git a/lib/views/home/widgets/overview/video.dart b/lib/views/home/widgets/overview/video.dart index 3150b34..2deaf2b 100644 --- a/lib/views/home/widgets/overview/video.dart +++ b/lib/views/home/widgets/overview/video.dart @@ -4,7 +4,6 @@ import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/utils/date_time.dart'; -import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; import 'package:didvan/views/home/widgets/bookmark_button.dart'; import 'package:didvan/views/home/widgets/duration_widget.dart'; import 'package:didvan/views/widgets/didvan/card.dart'; @@ -14,18 +13,15 @@ import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/shimmer_placeholder.dart'; import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; class VideoOverview extends StatelessWidget { final OverviewData video; - final void Function(int id, int count) onCommentsChanged; final void Function(int id, bool value) onMarkChanged; final bool hasUnmarkConfirmation; final StudioRequestArgs? studioRequestArgs; const VideoOverview({ Key? key, required this.video, - required this.onCommentsChanged, required this.onMarkChanged, required this.hasUnmarkConfirmation, this.studioRequestArgs, @@ -38,12 +34,10 @@ class VideoOverview extends StatelessWidget { Routes.studioDetails, arguments: { 'onMarkChanged': onMarkChanged, - 'onCommentsChanged': onCommentsChanged, 'id': video.id, 'args': studioRequestArgs, 'hasUnmarkConfirmation': hasUnmarkConfirmation, 'isVideo': true, - 'state': context.read(), }, ), child: Row( @@ -115,6 +109,7 @@ class VideoOverview extends StatelessWidget { gestureSize: 24, value: video.marked, onMarkChanged: (value) => onMarkChanged(video.id, value), + askForConfirmation: hasUnmarkConfirmation, ), ], ), diff --git a/lib/views/widgets/didvan/scaffold.dart b/lib/views/widgets/didvan/scaffold.dart index 0dabcf2..bca06ac 100644 --- a/lib/views/widgets/didvan/scaffold.dart +++ b/lib/views/widgets/didvan/scaffold.dart @@ -9,6 +9,7 @@ class DidvanScaffold extends StatefulWidget { final EdgeInsets padding; final Color? backgroundColor; final bool reverse; + final ScrollController? scrollController; const DidvanScaffold({ Key? key, @@ -18,6 +19,7 @@ class DidvanScaffold extends StatefulWidget { this.padding = const EdgeInsets.symmetric(horizontal: 16), this.backgroundColor, this.reverse = false, + this.scrollController, }) : super(key: key); @override @@ -25,7 +27,13 @@ class DidvanScaffold extends StatefulWidget { } class _DidvanScaffoldState extends State { - final _scrollController = ScrollController(); + late final ScrollController _scrollController; + + @override + void initState() { + _scrollController = widget.scrollController ?? ScrollController(); + super.initState(); + } @override Widget build(BuildContext context) { @@ -33,7 +41,9 @@ class _DidvanScaffoldState extends State { return Scaffold( backgroundColor: widget.backgroundColor, body: Padding( - padding: EdgeInsets.only(top: statusBarHeight), + padding: widget.appBarData == null + ? EdgeInsets.zero + : EdgeInsets.only(top: statusBarHeight), child: Stack( children: [ CustomScrollView( From 00e00b0ec51aa1c25102e908d82eb4baac6adb87 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Wed, 23 Mar 2022 14:01:15 +0430 Subject: [PATCH 04/54] fandogh latest version config --- .fandogh/fandogh.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.fandogh/fandogh.yaml b/.fandogh/fandogh.yaml index ebd7abc..7f73831 100644 --- a/.fandogh/fandogh.yaml +++ b/.fandogh/fandogh.yaml @@ -3,7 +3,7 @@ name: app-dev spec: allow_http: false disable_default_domains: true - image: app-dev:1.1.4 + image: app-dev:1.2.0 image_pull_policy: IfNotPresent path: / replicas: 1 From d869f543388747e13310363afd107c92791d9409 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Fri, 25 Mar 2022 19:23:37 +0430 Subject: [PATCH 05/54] D1APP-99 studio --- android/app/src/main/AndroidManifest.xml | 6 +- lib/services/network/request.dart | 9 ++ lib/views/home/home.dart | 2 +- lib/views/home/studio/studio.dart | 5 +- .../studio_details/studio_details.mobile.dart | 23 ++-- .../studio_details/studio_details.web.dart | 49 +++---- .../studio_details/studio_details_state.dart | 130 +++++++----------- .../widgets/studio_details_widget.dart | 16 ++- lib/views/home/studio/studio_state.dart | 7 + lib/views/home/studio/widgets/slider.dart | 110 +++++++++------ .../widgets/audio/audio_player_widget.dart | 125 ++++++++++++++++- .../home/widgets/audio/audio_slider.dart | 1 + lib/views/home/widgets/bookmark_button.dart | 3 + lib/views/home/widgets/overview/podcast.dart | 14 +- lib/views/home/widgets/overview/video.dart | 15 +- .../{home/widgets => widgets/didvan}/bnb.dart | 2 +- pubspec.lock | 37 ++++- pubspec.yaml | 1 + 18 files changed, 374 insertions(+), 181 deletions(-) rename lib/views/{home/widgets => widgets/didvan}/bnb.dart (99%) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 34d2e7f..85b3ee9 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -3,10 +3,14 @@ + + android:usesCleartextTraffic="true" + android:requestLegacyExternalStorage="true"> + + download() async { + Permission.manageExternalStorage.request(); + final response = await http.get(Uri.parse(url)); + final file = await File('/storage/emulated/0/Download/file.mp3').create(); + await file.writeAsBytes(response.bodyBytes); + } + void _handleResponse(http.Response? response) { statusCode = response?.statusCode; if (_handleError(response)) { diff --git a/lib/views/home/home.dart b/lib/views/home/home.dart index 0e1d5a8..b9c203e 100644 --- a/lib/views/home/home.dart +++ b/lib/views/home/home.dart @@ -5,7 +5,7 @@ import 'package:didvan/views/home/radar/radar.dart'; import 'package:didvan/views/home/settings/settings.dart'; import 'package:didvan/views/home/statistics/statistics.dart'; import 'package:didvan/views/home/studio/studio.dart'; -import 'package:didvan/views/home/widgets/bnb.dart'; +import 'package:didvan/views/widgets/didvan/bnb.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; diff --git a/lib/views/home/studio/studio.dart b/lib/views/home/studio/studio.dart index 79744c4..51d7b8e 100644 --- a/lib/views/home/studio/studio.dart +++ b/lib/views/home/studio/studio.dart @@ -54,7 +54,10 @@ class _StudioState extends State { icon: DidvanIcons.bookmark_regular, onPressed: () => Navigator.of(context).pushNamed( Routes.filteredBookmarks, - arguments: context.read().type, + arguments: { + 'type': context.read().type, + 'onDeleted': (_) {} + }, ), ), ), diff --git a/lib/views/home/studio/studio_details/studio_details.mobile.dart b/lib/views/home/studio/studio_details/studio_details.mobile.dart index 4756477..05e0ba6 100644 --- a/lib/views/home/studio/studio_details/studio_details.mobile.dart +++ b/lib/views/home/studio/studio_details/studio_details.mobile.dart @@ -84,9 +84,11 @@ class _StudioDetailsState extends State { return Consumer( builder: (context, state, child) => StateHandler( state: state, - onRetry: () => state.getStudioDetails(state.currentStudio.id), + onRetry: () => state.getStudioDetails(state.studio.id), builder: (context, state) { - if (state.studios.isEmpty) { + if (state.prevStudio == null && + state.nextStudio == null && + state.args.page != 0) { return const SizedBox(); } return WillPopScope( @@ -105,7 +107,7 @@ class _StudioDetailsState extends State { ? null : AppBarData( isSmall: true, - title: state.currentStudio.title, + title: state.studio.title, ), children: [ SizedBox( @@ -146,7 +148,7 @@ class _StudioDetailsState extends State { - ${state.currentStudio.media} + ${state.studio.media} ''', @@ -171,12 +173,15 @@ class _StudioDetailsState extends State { ), const SizedBox(height: 20), StudioDetailsWidget( - onCommentsTabSelected: () => _scrollController.animateTo( - _scrollController.position.maxScrollExtent, - duration: DesignConfig.lowAnimationDuration, - curve: Curves.easeIn, + onCommentsTabSelected: () => Future.delayed( + const Duration(milliseconds: 100), + () => _scrollController.animateTo( + _scrollController.position.maxScrollExtent, + duration: DesignConfig.lowAnimationDuration, + curve: Curves.easeIn, + ), ), - studio: state.currentStudio, + studio: state.studio, ), ], ), diff --git a/lib/views/home/studio/studio_details/studio_details.web.dart b/lib/views/home/studio/studio_details/studio_details.web.dart index c186998..7993fcf 100644 --- a/lib/views/home/studio/studio_details/studio_details.web.dart +++ b/lib/views/home/studio/studio_details/studio_details.web.dart @@ -86,25 +86,25 @@ class _StudioDetailsState extends State { return Consumer( builder: (context, state, child) => StateHandler( state: state, - onRetry: () => state.getStudioDetails(state.currentStudio.id), + onRetry: () => state.getStudioDetails(state.studio.id), builder: (context, state) { - if (state.studios.isEmpty) { + if (state.prevStudio == null && + state.nextStudio == null && + state.args.page != 0) { return const SizedBox(); } - if (kIsWeb) { - // ignore: undefined_prefixed_name - ui.platformViewRegistry.registerViewFactory( - "video", - (int viewId) => html.IFrameElement() - ..allowFullscreen = true - ..src = Uri.dataFromString( - '' + - state.currentStudio.media, - mimeType: 'text/html', - ).toString() - ..style.border = 'none', - ); - } + // ignore: undefined_prefixed_name + ui.platformViewRegistry.registerViewFactory( + "video", + (int viewId) => html.IFrameElement() + ..allowFullscreen = true + ..src = Uri.dataFromString( + '' + + state.studio.media, + mimeType: 'text/html', + ).toString() + ..style.border = 'none', + ); return WillPopScope( onWillPop: () async { if (_isFullScreen) { @@ -120,7 +120,7 @@ class _StudioDetailsState extends State { ? null : AppBarData( isSmall: true, - title: state.currentStudio.title, + title: state.studio.title, ), children: [ if (kIsWeb) @@ -167,7 +167,7 @@ class _StudioDetailsState extends State { - ${state.currentStudio.media} + ${state.studio.media} ''', @@ -193,12 +193,15 @@ class _StudioDetailsState extends State { ), const SizedBox(height: 20), StudioDetailsWidget( - onCommentsTabSelected: () => _scrollController.animateTo( - _scrollController.position.maxScrollExtent, - duration: DesignConfig.lowAnimationDuration, - curve: Curves.easeIn, + onCommentsTabSelected: () => Future.delayed( + const Duration(milliseconds: 100), + () => _scrollController.animateTo( + _scrollController.position.maxScrollExtent, + duration: DesignConfig.lowAnimationDuration, + curve: Curves.easeIn, + ), ), - studio: state.currentStudio, + studio: state.studio, ), ], ), diff --git a/lib/views/home/studio/studio_details/studio_details_state.dart b/lib/views/home/studio/studio_details/studio_details_state.dart index 7ef097c..f37d4cd 100644 --- a/lib/views/home/studio/studio_details/studio_details_state.dart +++ b/lib/views/home/studio/studio_details/studio_details_state.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:math'; import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; @@ -11,16 +10,17 @@ import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; class StudioDetailsState extends CoreProvier { - final List studios = []; + late StudioDetailsData studio; + StudioDetailsData? nextStudio; + StudioDetailsData? prevStudio; late int initialIndex; late StudioRequestArgs args; - int _selectedDetailsIndex = 0; - bool isFetchingNewItem = false; final List relatedQueue = []; - bool currentTypeIsVideo = true; + bool _positionListenerActivated = false; - int _currentIndex = 0; - int get currentIndex => _currentIndex; + int _selectedDetailsIndex = 0; + Timer? timer; + int timerValue = 10; int get selectedDetailsIndex => _selectedDetailsIndex; set selectedDetailsIndex(int value) { @@ -31,83 +31,49 @@ class StudioDetailsState extends CoreProvier { notifyListeners(); } - StudioDetailsData get currentStudio { - try { - return studios[_currentIndex]!; - } catch (e) { - return studios[_currentIndex + 1]!; - } - } - Future getStudioDetails( int id, { - bool? isForward, StudioRequestArgs? args, + bool? isForward, }) async { if (args != null) { this.args = args; } - if (isForward == null) { - _selectedDetailsIndex = 0; - appState = AppState.busy; - } else { - isFetchingNewItem = true; - notifyListeners(); + if (MediaService.currentPodcast?.id == id && this.args.type == 'podcast') { + return; } + _selectedDetailsIndex = 0; + if (isForward != null) { + if (isForward) { + prevStudio = studio; + studio = nextStudio!; + nextStudio = null; + } else { + nextStudio = studio; + studio = prevStudio!; + prevStudio = null; + } + _handlePodcastPlayback(studio); + } + appState = AppState.busy; final service = RequestService(RequestHelper.studioDetails(id, this.args)); await service.httpGet(); if (service.isSuccess) { - studios.clear(); final result = service.result; - final studio = StudioDetailsData.fromJson(result['studio']); - await _handlePodcastPlayback(studio); - if (this.args.page == 0) { - studios.add(studio); - initialIndex = 0; - appState = AppState.idle; - return; - } - - StudioDetailsData? prevStudio; - if (result['prevStudio'].isNotEmpty) { - prevStudio = StudioDetailsData.fromJson(result['prevStudio']); - } - - StudioDetailsData? nextStudio; - if (result['nextStudio'].isNotEmpty) { + studio = StudioDetailsData.fromJson(result['studio']); + if (result['nextStudio'].isNotEmpty && this.args.page != 0) { nextStudio = StudioDetailsData.fromJson(result['nextStudio']); } - - if (isForward == null) { - studios - .addAll(List.generate(max(studio.order - 2, 0), (index) => null)); - if (prevStudio != null) { - studios.add(prevStudio); - } - studios.add(studio); - if (nextStudio != null) { - studios.add(nextStudio); - } - _currentIndex = initialIndex = studio.order - 1; - } else if (isForward) { - if (!exists(nextStudio) && nextStudio != null) { - studios.add(nextStudio); - } - _currentIndex++; - } else if (!isForward) { - if (!exists(prevStudio) && prevStudio != null) { - studios[_currentIndex - 2] = prevStudio; - } - _currentIndex--; + if (result['prevStudio'].isNotEmpty && this.args.page != 0) { + prevStudio = StudioDetailsData.fromJson(result['prevStudio']); + } + if (isForward == null) { + await _handlePodcastPlayback(studio); } - isFetchingNewItem = false; appState = AppState.idle; return; } - //why? total page state shouldn't die! - if (isForward == null) { - appState = AppState.failed; - } + appState = AppState.failed; } Future _handlePodcastPlayback(StudioDetailsData studio) async { @@ -118,37 +84,39 @@ class StudioDetailsState extends CoreProvier { audioSource: studio.media, isVoiceMessage: false, ); + if (nextStudio != null && !_positionListenerActivated) { + _positionListenerActivated = true; + MediaService.audioPlayer.positionStream.listen((event) { + final duration = MediaService.audioPlayer.duration ?? + Duration(seconds: studio.duration); + if (event.compareTo(duration) > 0 && nextStudio != null) { + getStudioDetails(nextStudio!.id, isForward: true); + } + }); + } } } Future getRelatedContents() async { - if (currentStudio.relatedContents.isNotEmpty) return; - relatedQueue.add(currentStudio.id); + if (studio.relatedContents.isNotEmpty) return; + relatedQueue.add(studio.id); final service = RequestService(RequestHelper.tag( - ids: currentStudio.tags.map((tag) => tag.id).toList(), - itemId: currentStudio.id, - type: currentStudio.media.contains('iframe') ? 'video' : 'podcast', + ids: studio.tags.map((tag) => tag.id).toList(), + itemId: studio.id, + type: studio.media.contains('iframe') ? 'video' : 'podcast', )); await service.httpGet(); if (service.isSuccess) { final relateds = service.result['contents']; for (var i = 0; i < relateds.length; i++) { - studios - .where((element) => element != null) - .firstWhere((element) => element!.id == currentStudio.id)! - .relatedContents - .add(OverviewData.fromJson(relateds[i])); + studio.relatedContents.add(OverviewData.fromJson(relateds[i])); } notifyListeners(); } } - bool exists(StudioDetailsData? studio) => - studios.any((r) => studio != null && r != null && r.id == studio.id); - void onCommentsChanged(int count) { - studios.firstWhere((studio) => studio?.id == currentStudio.id)!.comments = - count; + studio.comments = count; notifyListeners(); } } diff --git a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart index 1844c5d..fe5fec4 100644 --- a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart +++ b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart @@ -75,7 +75,7 @@ class StudioDetailsWidget extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - DidvanText(state.currentStudio.description), + DidvanText(state.studio.description), if (studio.tags.isNotEmpty) const SizedBox(height: 20), Wrap( spacing: 8, @@ -90,15 +90,15 @@ class StudioDetailsWidget extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const SizedBox(), - if (state.studios.length != state.currentIndex + 1) + if (state.nextStudio != null) _StudioPreview( isNext: true, - studio: state.studios[state.currentIndex + 1]!, + studio: state.nextStudio!, ), - if (state.currentIndex != 0) + if (state.prevStudio != null) _StudioPreview( isNext: false, - studio: state.studios[state.currentIndex - 1]!, + studio: state.prevStudio!, ), const SizedBox(), ], @@ -234,7 +234,11 @@ class _StudioPreview extends StatelessWidget { return GestureDetector( onTap: () { final state = context.read(); - state.getStudioDetails(studio.id, args: state.args); + state.getStudioDetails( + isNext ? state.nextStudio!.id : state.prevStudio!.id, + args: state.args, + isForward: isNext, + ); }, child: Container( width: 88, diff --git a/lib/views/home/studio/studio_state.dart b/lib/views/home/studio/studio_state.dart index 115534a..f07effa 100644 --- a/lib/views/home/studio/studio_state.dart +++ b/lib/views/home/studio/studio_state.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/requests/studio.dart'; @@ -117,4 +119,9 @@ class StudioState extends CoreProvier { studios.firstWhere((radar) => radar.id == id).comments = count; notifyListeners(); } + + void download(String url) { + final service = RequestService(url); + service.download(); + } } diff --git a/lib/views/home/studio/widgets/slider.dart b/lib/views/home/studio/widgets/slider.dart index 39e5bc4..c7c41f2 100644 --- a/lib/views/home/studio/widgets/slider.dart +++ b/lib/views/home/studio/widgets/slider.dart @@ -2,6 +2,9 @@ import 'package:carousel_slider/carousel_slider.dart'; import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/models/enums.dart'; +import 'package:didvan/models/requests/studio.dart'; +import 'package:didvan/routes/routes.dart'; +import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; import 'package:didvan/views/home/studio/studio_state.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/shimmer_placeholder.dart'; @@ -26,48 +29,75 @@ class _StudioSliderState extends State { children: [ CarouselSlider( items: [ - for (var i = 0; i < state.sliders.length; i++) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: state.appState == AppState.busy - ? const ShimmerPlaceholder() - : Stack( - children: [ - SkeletonImage( - borderRadius: DesignConfig.mediumBorderRadius, - imageUrl: state.sliders[i].image, - width: double.infinity, - ), - Positioned( - bottom: 0, - left: 0, - right: 0, - child: Container( - padding: const EdgeInsets.symmetric( - vertical: 4, - horizontal: 8, - ), - decoration: BoxDecoration( - color: (state.videosSelected - ? Theme.of(context) - .colorScheme - .secondaryDisabled - : Theme.of(context).colorScheme.focused) - .withOpacity(0.9), - borderRadius: const BorderRadius.vertical( - bottom: Radius.circular(10), - ), - ), - child: DidvanText( - state.sliders[i].title, - color: Theme.of(context).colorScheme.title, - style: Theme.of(context).textTheme.caption, + if (state.appState == AppState.busy) + const Padding( + padding: EdgeInsets.symmetric(horizontal: 4), + child: ShimmerPlaceholder(), + ), + if (state.appState == AppState.idle) + for (var i = 0; i < state.sliders.length; i++) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: GestureDetector( + onTap: () { + if (state.videosSelected) { + Navigator.of(context) + .pushNamed(Routes.studioDetails, arguments: { + 'onMarkChanged': state.changeMark, + 'id': state.sliders[i].id, + 'args': + const StudioRequestArgs(page: 0, type: 'video'), + 'hasUnmarkConfirmation': false, + 'isVideo': true, + }); + return; + } + context.read().getStudioDetails( + state.sliders[i].id, + args: const StudioRequestArgs( + page: 0, + type: 'podcast', + ), + ); + }, + child: Stack( + children: [ + SkeletonImage( + borderRadius: DesignConfig.mediumBorderRadius, + imageUrl: state.sliders[i].image, + width: double.infinity, + ), + Positioned( + bottom: 0, + left: 0, + right: 0, + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 4, + horizontal: 8, + ), + decoration: BoxDecoration( + color: (state.videosSelected + ? Theme.of(context) + .colorScheme + .secondaryDisabled + : Theme.of(context).colorScheme.focused) + .withOpacity(0.9), + borderRadius: const BorderRadius.vertical( + bottom: Radius.circular(10), ), ), + child: DidvanText( + state.sliders[i].title, + color: Theme.of(context).colorScheme.title, + style: Theme.of(context).textTheme.caption, + ), ), - ], - ), - ), + ), + ], + ), + ), + ), ], options: CarouselOptions( onPageChanged: (index, reason) => setState( @@ -75,7 +105,7 @@ class _StudioSliderState extends State { ), viewportFraction: 0.94, aspectRatio: 16 / 9, - autoPlay: true, + autoPlay: state.appState == AppState.idle, ), ), const SizedBox(height: 16), diff --git a/lib/views/home/widgets/audio/audio_player_widget.dart b/lib/views/home/widgets/audio/audio_player_widget.dart index dbdc64f..edc9675 100644 --- a/lib/views/home/widgets/audio/audio_player_widget.dart +++ b/lib/views/home/widgets/audio/audio_player_widget.dart @@ -1,16 +1,21 @@ +import 'dart:async'; import 'dart:math'; import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/studio_details_data.dart'; +import 'package:didvan/models/view/action_sheet_data.dart'; import 'package:didvan/services/media/media.dart'; +import 'package:didvan/utils/action_sheet.dart'; +import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; import 'package:didvan/views/home/studio/studio_state.dart'; import 'package:didvan/views/home/widgets/audio/audio_slider.dart'; import 'package:didvan/views/home/widgets/bookmark_button.dart'; import 'package:didvan/views/widgets/didvan/icon_button.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/ink_wrapper.dart'; +import 'package:didvan/views/widgets/item_title.dart'; import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -21,6 +26,7 @@ class AudioPlayerWidget extends StatelessWidget { @override Widget build(BuildContext context) { + final state = context.read(); return Container( decoration: BoxDecoration( borderRadius: const BorderRadius.vertical(top: Radius.circular(8)), @@ -60,13 +66,34 @@ class AudioPlayerWidget extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - DidvanIconButton( - icon: DidvanIcons.sleep_timer_regular, - onPressed: () {}, + const SizedBox(), + StatefulBuilder( + builder: (context, setState) => Column( + children: [ + DidvanIconButton( + icon: state.timer == null + ? DidvanIcons.sleep_timer_regular + : DidvanIcons.sleep_enabled_regular, + color: Theme.of(context).colorScheme.title, + onPressed: () => _showSleepTimer( + state, + () => setState(() {}), + ), + ), + if (state.timer != null) + DidvanText( + state.timerValue.toString() + '\'', + isEnglishFont: true, + style: Theme.of(context).textTheme.overline, + color: Theme.of(context).colorScheme.caption, + ), + ], + ), ), Column( children: [ DidvanIconButton( + color: Theme.of(context).colorScheme.title, size: 32, icon: DidvanIcons.media_forward_solid, onPressed: () { @@ -78,7 +105,11 @@ class AudioPlayerWidget extends StatelessWidget { ); }, ), - const DidvanText('30', isEnglishFont: true), + DidvanText( + '30', + isEnglishFont: true, + color: Theme.of(context).colorScheme.title, + ), ], ), StreamBuilder( @@ -94,6 +125,7 @@ class AudioPlayerWidget extends StatelessWidget { DidvanIconButton( size: 32, icon: DidvanIcons.media_backward_solid, + color: Theme.of(context).colorScheme.title, onPressed: () { MediaService.audioPlayer.seek( Duration( @@ -105,21 +137,104 @@ class AudioPlayerWidget extends StatelessWidget { ); }, ), - const DidvanText('10', isEnglishFont: true), + DidvanText( + '10', + isEnglishFont: true, + color: Theme.of(context).colorScheme.title, + ), ], ), BookmarkButton( gestureSize: 48, + color: Theme.of(context).colorScheme.title, value: podcast.marked, onMarkChanged: (value) => context.read().changeMark(podcast.id, value), ), + const SizedBox(), ], ), ], ), ); } + + Future _showSleepTimer(StudioDetailsState state, update) async { + int timerValue = 10; + final controller = FixedExtentScrollController(); + Future.delayed( + const Duration(milliseconds: 100), + () => controller.animateTo( + 50 * (state.timerValue / 5 - 2), + duration: DesignConfig.lowAnimationDuration, + curve: Curves.easeIn, + ), + ); + await ActionSheetUtils.showBottomSheet( + data: ActionSheetData( + content: StatefulBuilder( + builder: (context, setState) => Column( + children: [ + const ItemTitle( + title: 'زمان خواب', + icon: DidvanIcons.sleep_timer_regular, + ), + const SizedBox(height: 24), + DidvanText( + timerValue.toString() + ' دقیقه', + style: Theme.of(context).textTheme.headline3, + ), + const SizedBox(height: 12), + const Icon(DidvanIcons.caret_down_solid), + const SizedBox(height: 8), + SizedBox( + height: 50, + child: RotatedBox( + quarterTurns: 3, + child: ListWheelScrollView( + controller: controller, + physics: const FixedExtentScrollPhysics(), + itemExtent: 48, + onSelectedItemChanged: (index) { + final minutes = (index + 2) * 5; + timerValue = minutes; + setState(() {}); + }, + children: [ + for (var i = 0; i < 9; i++) + Center( + child: Container( + color: Theme.of(context).colorScheme.text, + width: 50, + height: 3, + ), + ), + ], + ), + ), + ), + ], + ), + ), + onConfirmed: () { + state.timer = Timer( + Duration(minutes: timerValue), + MediaService.audioPlayer.stop, + ); + state.timerValue = timerValue; + update(); + }, + dismissTitle: 'لغو', + onDismissed: () { + state.timer?.cancel(); + state.timer = null; + state.timerValue = 10; + update(); + }, + ), + ); + controller.dispose(); + } } class _PlayPouseAnimatedIcon extends StatefulWidget { diff --git a/lib/views/home/widgets/audio/audio_slider.dart b/lib/views/home/widgets/audio/audio_slider.dart index 9c80349..093bcd2 100644 --- a/lib/views/home/widgets/audio/audio_slider.dart +++ b/lib/views/home/widgets/audio/audio_slider.dart @@ -45,6 +45,7 @@ class AudioSlider extends StatelessWidget { timeLabelTextStyle: TextStyle( fontSize: showTimer ? null : 0, height: showTimer ? 3 : 0, + color: Theme.of(context).colorScheme.text, fontFamily: DesignConfig.fontFamily.replaceAll( '-FA', '', diff --git a/lib/views/home/widgets/bookmark_button.dart b/lib/views/home/widgets/bookmark_button.dart index ca98b06..8f1d5aa 100644 --- a/lib/views/home/widgets/bookmark_button.dart +++ b/lib/views/home/widgets/bookmark_button.dart @@ -7,6 +7,7 @@ import 'package:flutter/material.dart'; class BookmarkButton extends StatefulWidget { final bool value; + final Color? color; final void Function(bool value) onMarkChanged; final bool askForConfirmation; final double gestureSize; @@ -16,6 +17,7 @@ class BookmarkButton extends StatefulWidget { required this.onMarkChanged, this.askForConfirmation = false, required this.gestureSize, + this.color, }) : super(key: key); @override @@ -41,6 +43,7 @@ class _BookmarkButtonState extends State { Widget build(BuildContext context) { return DidvanIconButton( gestureSize: widget.gestureSize, + color: widget.color, icon: _value ? DidvanIcons.bookmark_solid : DidvanIcons.bookmark_regular, onPressed: () async { bool confirm = false; diff --git a/lib/views/home/widgets/overview/podcast.dart b/lib/views/home/widgets/overview/podcast.dart index 5476de4..e674955 100644 --- a/lib/views/home/widgets/overview/podcast.dart +++ b/lib/views/home/widgets/overview/podcast.dart @@ -4,6 +4,7 @@ import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/utils/date_time.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; +import 'package:didvan/views/home/studio/studio_state.dart'; import 'package:didvan/views/home/widgets/bookmark_button.dart'; import 'package:didvan/views/home/widgets/duration_widget.dart'; import 'package:didvan/views/widgets/didvan/card.dart'; @@ -77,12 +78,13 @@ class PodcastOverview extends StatelessWidget { children: [ DurationWidget(duration: podcast.duration!), const Spacer(), - DidvanIconButton( - gestureSize: 28, - icon: DidvanIcons.download_regular, - onPressed: () {}, - ), - const SizedBox(width: 16), + // DidvanIconButton( + // gestureSize: 28, + // icon: DidvanIcons.download_regular, + // onPressed: () => + // context.read().download(podcast.media!), + // ), + // const SizedBox(width: 16), BookmarkButton( askForConfirmation: hasUnmarkConfirmation, gestureSize: 24, diff --git a/lib/views/home/widgets/overview/video.dart b/lib/views/home/widgets/overview/video.dart index 2deaf2b..ea8a81c 100644 --- a/lib/views/home/widgets/overview/video.dart +++ b/lib/views/home/widgets/overview/video.dart @@ -4,6 +4,7 @@ import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/utils/date_time.dart'; +import 'package:didvan/views/home/studio/studio_state.dart'; import 'package:didvan/views/home/widgets/bookmark_button.dart'; import 'package:didvan/views/home/widgets/duration_widget.dart'; import 'package:didvan/views/widgets/didvan/card.dart'; @@ -13,6 +14,7 @@ import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/shimmer_placeholder.dart'; import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; class VideoOverview extends StatelessWidget { final OverviewData video; @@ -99,12 +101,13 @@ class VideoOverview extends StatelessWidget { children: [ DurationWidget(duration: video.duration!), const Spacer(), - DidvanIconButton( - gestureSize: 28, - icon: DidvanIcons.download_regular, - onPressed: () {}, - ), - const SizedBox(width: 16), + // DidvanIconButton( + // gestureSize: 28, + // icon: DidvanIcons.download_regular, + // onPressed: () => + // context.read().download(video.media!), + // ), + // const SizedBox(width: 16), BookmarkButton( gestureSize: 24, value: video.marked, diff --git a/lib/views/home/widgets/bnb.dart b/lib/views/widgets/didvan/bnb.dart similarity index 99% rename from lib/views/home/widgets/bnb.dart rename to lib/views/widgets/didvan/bnb.dart index 2a9d722..c5f4ec5 100644 --- a/lib/views/home/widgets/bnb.dart +++ b/lib/views/widgets/didvan/bnb.dart @@ -231,7 +231,7 @@ class DidvanBNB extends StatelessWidget { expandableContent: state.appState == AppState.busy ? const SizedBox() : StudioDetailsWidget( - studio: detailsState.currentStudio, + studio: detailsState.studio, onCommentsTabSelected: () { Future.delayed( const Duration(milliseconds: 100), diff --git a/pubspec.lock b/pubspec.lock index ab49287..bb94c55 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -532,6 +532,41 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.11.1" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + url: "https://pub.dartlang.org" + source: hosted + version: "9.2.0" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + url: "https://pub.dartlang.org" + source: hosted + version: "9.0.2+1" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + url: "https://pub.dartlang.org" + source: hosted + version: "9.0.3" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "3.7.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0" persian_datetime_picker: dependency: "direct main" description: @@ -840,4 +875,4 @@ packages: version: "5.3.1" sdks: dart: ">=2.16.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" diff --git a/pubspec.yaml b/pubspec.yaml index 7bae68f..745c0a7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -64,6 +64,7 @@ dependencies: firebase_core: ^1.13.1 webview_flutter: ^3.0.1 expandable_bottom_sheet: ^1.1.1+1 + permission_handler: ^9.2.0 dev_dependencies: From 24fee2221aa1701d2baa657da10dd4eb4c9265bf Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Fri, 25 Mar 2022 22:25:25 +0430 Subject: [PATCH 06/54] code cleaning + bug fixes --- .../studio_details/studio_details.mobile.dart | 2 ++ .../studio_details/studio_details.web.dart | 1 + .../studio_details/studio_details_state.dart | 7 ++++++- .../widgets/studio_details_widget.dart | 19 ++++++++++++++++++- lib/views/home/widgets/overview/podcast.dart | 3 --- lib/views/home/widgets/overview/video.dart | 3 --- 6 files changed, 27 insertions(+), 8 deletions(-) diff --git a/lib/views/home/studio/studio_details/studio_details.mobile.dart b/lib/views/home/studio/studio_details/studio_details.mobile.dart index 05e0ba6..a8d51f1 100644 --- a/lib/views/home/studio/studio_details/studio_details.mobile.dart +++ b/lib/views/home/studio/studio_details/studio_details.mobile.dart @@ -100,6 +100,7 @@ class _StudioDetailsState extends State { return true; }, child: DidvanScaffold( + key: ValueKey(state.studio.id), scrollController: _scrollController, backgroundColor: Theme.of(context).colorScheme.surface, padding: EdgeInsets.zero, @@ -173,6 +174,7 @@ class _StudioDetailsState extends State { ), const SizedBox(height: 20), StudioDetailsWidget( + scrollController: _scrollController, onCommentsTabSelected: () => Future.delayed( const Duration(milliseconds: 100), () => _scrollController.animateTo( diff --git a/lib/views/home/studio/studio_details/studio_details.web.dart b/lib/views/home/studio/studio_details/studio_details.web.dart index 7993fcf..c3040ff 100644 --- a/lib/views/home/studio/studio_details/studio_details.web.dart +++ b/lib/views/home/studio/studio_details/studio_details.web.dart @@ -193,6 +193,7 @@ class _StudioDetailsState extends State { ), const SizedBox(height: 20), StudioDetailsWidget( + scrollController: _scrollController, onCommentsTabSelected: () => Future.delayed( const Duration(milliseconds: 100), () => _scrollController.animateTo( diff --git a/lib/views/home/studio/studio_details/studio_details_state.dart b/lib/views/home/studio/studio_details/studio_details_state.dart index f37d4cd..89bd1ff 100644 --- a/lib/views/home/studio/studio_details/studio_details_state.dart +++ b/lib/views/home/studio/studio_details/studio_details_state.dart @@ -53,11 +53,16 @@ class StudioDetailsState extends CoreProvier { studio = prevStudio!; prevStudio = null; } + notifyListeners(); _handlePodcastPlayback(studio); } - appState = AppState.busy; + if (isForward == null) { + appState = AppState.busy; + } final service = RequestService(RequestHelper.studioDetails(id, this.args)); await service.httpGet(); + nextStudio = null; + prevStudio = null; if (service.isSuccess) { final result = service.result; studio = StudioDetailsData.fromJson(result['studio']); diff --git a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart index fe5fec4..6c99823 100644 --- a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart +++ b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart @@ -1,3 +1,4 @@ +import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/studio_details_data.dart'; @@ -14,11 +15,13 @@ import 'package:provider/provider.dart'; class StudioDetailsWidget extends StatelessWidget { final StudioDetailsData studio; + final ScrollController? scrollController; final VoidCallback onCommentsTabSelected; const StudioDetailsWidget({ Key? key, required this.studio, required this.onCommentsTabSelected, + this.scrollController, }) : super(key: key); bool get _isVideo => studio.media.contains('ifram'); @@ -94,11 +97,13 @@ class StudioDetailsWidget extends StatelessWidget { _StudioPreview( isNext: true, studio: state.nextStudio!, + scrollController: scrollController, ), if (state.prevStudio != null) _StudioPreview( isNext: false, studio: state.prevStudio!, + scrollController: scrollController, ), const SizedBox(), ], @@ -219,7 +224,12 @@ class _TabItem extends StatelessWidget { class _StudioPreview extends StatelessWidget { final bool isNext; final StudioDetailsData studio; - const _StudioPreview({Key? key, required this.isNext, required this.studio}) + final ScrollController? scrollController; + const _StudioPreview( + {Key? key, + required this.isNext, + required this.studio, + this.scrollController}) : super(key: key); String get _previewTitle { @@ -239,6 +249,11 @@ class _StudioPreview extends StatelessWidget { args: state.args, isForward: isNext, ); + scrollController?.animateTo( + 0, + duration: DesignConfig.lowAnimationDuration, + curve: Curves.easeIn, + ); }, child: Container( width: 88, @@ -264,6 +279,8 @@ class _StudioPreview extends StatelessWidget { const SizedBox(height: 8), DidvanText( studio.title, + maxLines: 3, + overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, style: Theme.of(context).textTheme.overline, color: Theme.of(context).colorScheme.caption, diff --git a/lib/views/home/widgets/overview/podcast.dart b/lib/views/home/widgets/overview/podcast.dart index e674955..6aa2ed9 100644 --- a/lib/views/home/widgets/overview/podcast.dart +++ b/lib/views/home/widgets/overview/podcast.dart @@ -1,15 +1,12 @@ import 'package:didvan/config/theme_data.dart'; -import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/utils/date_time.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; -import 'package:didvan/views/home/studio/studio_state.dart'; import 'package:didvan/views/home/widgets/bookmark_button.dart'; import 'package:didvan/views/home/widgets/duration_widget.dart'; import 'package:didvan/views/widgets/didvan/card.dart'; import 'package:didvan/views/widgets/didvan/divider.dart'; -import 'package:didvan/views/widgets/didvan/icon_button.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/shimmer_placeholder.dart'; import 'package:didvan/views/widgets/skeleton_image.dart'; diff --git a/lib/views/home/widgets/overview/video.dart b/lib/views/home/widgets/overview/video.dart index ea8a81c..704b28f 100644 --- a/lib/views/home/widgets/overview/video.dart +++ b/lib/views/home/widgets/overview/video.dart @@ -4,17 +4,14 @@ import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/utils/date_time.dart'; -import 'package:didvan/views/home/studio/studio_state.dart'; import 'package:didvan/views/home/widgets/bookmark_button.dart'; import 'package:didvan/views/home/widgets/duration_widget.dart'; import 'package:didvan/views/widgets/didvan/card.dart'; import 'package:didvan/views/widgets/didvan/divider.dart'; -import 'package:didvan/views/widgets/didvan/icon_button.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/shimmer_placeholder.dart'; import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; class VideoOverview extends StatelessWidget { final OverviewData video; From d9890d6a9d047b3c9ea395c268fa77299df5d71f Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Fri, 25 Mar 2022 22:27:19 +0430 Subject: [PATCH 07/54] bug fixes --- .../home/studio/studio_details/studio_details_state.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/views/home/studio/studio_details/studio_details_state.dart b/lib/views/home/studio/studio_details/studio_details_state.dart index 89bd1ff..49f13d1 100644 --- a/lib/views/home/studio/studio_details/studio_details_state.dart +++ b/lib/views/home/studio/studio_details/studio_details_state.dart @@ -78,7 +78,9 @@ class StudioDetailsState extends CoreProvier { appState = AppState.idle; return; } - appState = AppState.failed; + if (isForward == null) { + appState = AppState.failed; + } } Future _handlePodcastPlayback(StudioDetailsData studio) async { From d906c07a42f62f2bdab05b8f8a6f315c5331e255 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Fri, 25 Mar 2022 22:36:52 +0430 Subject: [PATCH 08/54] hashtags pagination bug fixed --- lib/views/home/hashtag/hashtag_state.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/views/home/hashtag/hashtag_state.dart b/lib/views/home/hashtag/hashtag_state.dart index fc1e5e7..fbe6d78 100644 --- a/lib/views/home/hashtag/hashtag_state.dart +++ b/lib/views/home/hashtag/hashtag_state.dart @@ -25,6 +25,7 @@ class HashtagState extends CoreProvier { )); await service.httpGet(); if (service.isSuccess) { + lastPage = service.result['lastPage']; final contents = service.result['contents']; for (var i = 0; i < contents.length; i++) { items.add(OverviewData.fromJson(contents[i])); From 37aa23bb8d8a60ddd5e4f75ccbbcdec53d4ace26 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Fri, 25 Mar 2022 22:40:54 +0430 Subject: [PATCH 09/54] on wrong token login bug fixed --- lib/providers/user_provider.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/providers/user_provider.dart b/lib/providers/user_provider.dart index 570bb06..6748251 100644 --- a/lib/providers/user_provider.dart +++ b/lib/providers/user_provider.dart @@ -29,13 +29,13 @@ class UserProvider extends CoreProvier { isAuthenticated = true; final RequestService service = RequestService(RequestHelper.userInfo); await service.httpGet(); + if (service.statusCode == 401 || service.result['user'] == null) { + return false; + } if (service.isSuccess) { user = User.fromJson(service.result['user']); return true; } - if (service.statusCode == 401) { - return false; - } throw 'Getting user from API failed!'; } From 9260c65330bdeebb0ac3b26ab7247d5ff2b4deb1 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Sat, 26 Mar 2022 00:50:11 +0430 Subject: [PATCH 10/54] fandogh and pub new version --- .fandogh/fandogh.yaml | 2 +- pubspec.yaml | 44 +++++++------------------------------------ 2 files changed, 8 insertions(+), 38 deletions(-) diff --git a/.fandogh/fandogh.yaml b/.fandogh/fandogh.yaml index 7f73831..d7044ce 100644 --- a/.fandogh/fandogh.yaml +++ b/.fandogh/fandogh.yaml @@ -3,7 +3,7 @@ name: app-dev spec: allow_http: false disable_default_domains: true - image: app-dev:1.2.0 + image: app-dev:1.5.0 image_pull_policy: IfNotPresent path: / replicas: 1 diff --git a/pubspec.yaml b/pubspec.yaml index 745c0a7..0ad925a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.2.0+1 +version: 1.5.0+5 environment: sdk: ">=2.12.0 <3.0.0" @@ -91,42 +91,12 @@ flutter: # To add assets to your application, add an assets section, like this: assets: - - lib/assets/images/logos/logo-vertical-dark.svg - - lib/assets/images/logos/logo-vertical-light.svg - - lib/assets/images/logos/logo-horizontal-dark.svg - - lib/assets/images/logos/logo-horizontal-light.svg - - lib/assets/images/logos/studio-dark.svg - - lib/assets/images/logos/studio-light.svg - - lib/assets/images/categories/business-light.svg - - lib/assets/images/categories/economic-light.svg - - lib/assets/images/categories/enviromental-light.svg - - lib/assets/images/categories/political-light.svg - - lib/assets/images/categories/social-light.svg - - lib/assets/images/categories/tech-light.svg - - lib/assets/images/categories/business-dark.svg - - lib/assets/images/categories/economic-dark.svg - - lib/assets/images/categories/enviromental-dark.svg - - lib/assets/images/categories/political-dark.svg - - lib/assets/images/categories/social-dark.svg - - lib/assets/images/categories/tech-dark.svg - - lib/assets/images/themes/theme-light.svg - - lib/assets/images/themes/theme-dark.svg - - lib/assets/images/records/record-dark.svg - - lib/assets/images/records/record-light.svg - - lib/assets/images/empty_states/bookmark-light.svg - - lib/assets/images/empty_states/chart-light.svg - - lib/assets/images/empty_states/chat-light.svg - - lib/assets/images/empty_states/connection-light.svg - - lib/assets/images/empty_states/result-light.svg - - lib/assets/images/empty_states/studio-light.svg - - lib/assets/images/empty_states/bookmark-dark.svg - - lib/assets/images/empty_states/chart-dark.svg - - lib/assets/images/empty_states/chat-dark.svg - - lib/assets/images/empty_states/connection-dark.svg - - lib/assets/images/empty_states/result-dark.svg - - lib/assets/images/empty_states/studio-dark.svg - - lib/assets/animations/indicator-light.riv - - lib/assets/animations/indicator-dark.riv + - lib/assets/images/logos/ + - lib/assets/images/categories/ + - lib/assets/images/themes/ + - lib/assets/images/records/ + - lib/assets/images/empty_states/ + - lib/assets/animations/ - lib/assets/loading.gif From 0866856645d9ceedbc5d3ff1cc2585afe3e87730 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Sat, 26 Mar 2022 01:17:11 +0430 Subject: [PATCH 11/54] bug fixes --- ios/Podfile.lock | 6 ++++++ lib/views/home/studio/widgets/slider.dart | 1 + lib/views/home/widgets/overview/news.dart | 2 +- lib/views/home/widgets/overview/podcast.dart | 2 +- lib/views/home/widgets/overview/radar.dart | 2 +- lib/views/home/widgets/overview/video.dart | 2 +- pubspec.yaml | 2 +- release.sh | 13 +++++++++++++ 8 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 release.sh diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 89b36ba..98e5804 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -79,6 +79,8 @@ PODS: - nanopb/encode (2.30908.0) - path_provider_ios (0.0.1): - Flutter + - permission_handler_apple (9.0.2): + - Flutter - PromisesObjC (2.0.0) - record (0.0.1): - Flutter @@ -102,6 +104,7 @@ DEPENDENCIES: - image_picker (from `.symlinks/plugins/image_picker/ios`) - just_audio (from `.symlinks/plugins/just_audio/ios`) - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) + - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - record (from `.symlinks/plugins/record/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) @@ -142,6 +145,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/just_audio/ios" path_provider_ios: :path: ".symlinks/plugins/path_provider_ios/ios" + permission_handler_apple: + :path: ".symlinks/plugins/permission_handler_apple/ios" record: :path: ".symlinks/plugins/record/ios" sqflite: @@ -171,6 +176,7 @@ SPEC CHECKSUMS: just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 path_provider_ios: 7d7ce634493af4477d156294792024ec3485acd5 + permission_handler_apple: d21b38e1a4b2e041c63af9568f9165e114e507a6 PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58 record: 7ee2393532f8553bbb09fa19e95478323b7c0a99 sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 diff --git a/lib/views/home/studio/widgets/slider.dart b/lib/views/home/studio/widgets/slider.dart index c7c41f2..ea8cbdd 100644 --- a/lib/views/home/studio/widgets/slider.dart +++ b/lib/views/home/studio/widgets/slider.dart @@ -66,6 +66,7 @@ class _StudioSliderState extends State { borderRadius: DesignConfig.mediumBorderRadius, imageUrl: state.sliders[i].image, width: double.infinity, + height: double.infinity, ), Positioned( bottom: 0, diff --git a/lib/views/home/widgets/overview/news.dart b/lib/views/home/widgets/overview/news.dart index ac163c2..ee22add 100644 --- a/lib/views/home/widgets/overview/news.dart +++ b/lib/views/home/widgets/overview/news.dart @@ -79,7 +79,7 @@ class NewsOverview extends StatelessWidget { ], ), BookmarkButton( - gestureSize: 24, + gestureSize: 32, value: news.marked, onMarkChanged: (value) => onMarkChanged(news.id, value), askForConfirmation: hasUnmarkConfirmation, diff --git a/lib/views/home/widgets/overview/podcast.dart b/lib/views/home/widgets/overview/podcast.dart index 6aa2ed9..cea07fd 100644 --- a/lib/views/home/widgets/overview/podcast.dart +++ b/lib/views/home/widgets/overview/podcast.dart @@ -84,7 +84,7 @@ class PodcastOverview extends StatelessWidget { // const SizedBox(width: 16), BookmarkButton( askForConfirmation: hasUnmarkConfirmation, - gestureSize: 24, + gestureSize: 32, value: podcast.marked, onMarkChanged: (value) => onMarkChanged(podcast.id, value), ), diff --git a/lib/views/home/widgets/overview/radar.dart b/lib/views/home/widgets/overview/radar.dart index d74f422..fa79acd 100644 --- a/lib/views/home/widgets/overview/radar.dart +++ b/lib/views/home/widgets/overview/radar.dart @@ -103,7 +103,7 @@ class RadarOverview extends StatelessWidget { Row( children: [ BookmarkButton( - gestureSize: 24, + gestureSize: 32, value: radar.marked, onMarkChanged: (value) => onMarkChanged(radar.id, value), askForConfirmation: hasUnmarkConfirmation, diff --git a/lib/views/home/widgets/overview/video.dart b/lib/views/home/widgets/overview/video.dart index 704b28f..f88c842 100644 --- a/lib/views/home/widgets/overview/video.dart +++ b/lib/views/home/widgets/overview/video.dart @@ -106,7 +106,7 @@ class VideoOverview extends StatelessWidget { // ), // const SizedBox(width: 16), BookmarkButton( - gestureSize: 24, + gestureSize: 32, value: video.marked, onMarkChanged: (value) => onMarkChanged(video.id, value), askForConfirmation: hasUnmarkConfirmation, diff --git a/pubspec.yaml b/pubspec.yaml index 0ad925a..a75edb7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.5.0+5 +version: 1.5.0+6 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/release.sh b/release.sh new file mode 100644 index 0000000..9913c8f --- /dev/null +++ b/release.sh @@ -0,0 +1,13 @@ +flutter clean +flutter build apk +cp build/app/outputs/flutter-apk/app-release.apk /users/arytan/desktop +flutter build web --web-renderer canvaskit +cd build/web +fandogh login --username didvan --password 12799721 +fandogh image publish --version $1 +fandogh service apply -f ../../.fandogh/fandogh.yaml +cd ../.. +flutter build ipa +xcodebuild -exportArchive -exportOptionsPlist ios/runner/info.plist -archivePath build/ios/archive/runner.xcarchive -exportPath /users/arytan/desktop/App.ipa +cp /users/arytan/desktop/didvan.ipa/didvan.ipa /users/arytan/desktop +echo "Done!" \ No newline at end of file From 0bf8211c2e5c8e474ca45839de814a68ad1c0cf8 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Sat, 26 Mar 2022 01:45:18 +0430 Subject: [PATCH 12/54] firebase subscription to general topic --- lib/services/app_initalizer.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/services/app_initalizer.dart b/lib/services/app_initalizer.dart index 56d5f3f..7483968 100644 --- a/lib/services/app_initalizer.dart +++ b/lib/services/app_initalizer.dart @@ -74,6 +74,7 @@ class AppInitializer { Firebase.app(); } final FirebaseMessaging fcm = FirebaseMessaging.instance; + fcm.subscribeToTopic('general'); await fcm.requestPermission( alert: true, announcement: false, From 1d401b3ba0efa0c47f48a6d1026783bbf0850d37 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Tue, 29 Mar 2022 11:58:12 +0430 Subject: [PATCH 13/54] design updates + bug fixes --- lib/providers/core_provider.dart | 2 +- lib/providers/server_data_provider.dart | 6 +- lib/providers/user_provider.dart | 3 +- lib/routes/route_generator.dart | 24 ++- lib/views/home/comments/comments_state.dart | 1 - .../home/news/news_details/news_details.dart | 52 +++--- .../radar/radar_details/radar_details.dart | 66 ++++---- .../filtered_bookmarks_state.dart | 11 -- .../direct_list/direct_list_state.dart | 1 - .../studio_details/studio_details.mobile.dart | 156 +++++++++++------- .../studio_details/studio_details.web.dart | 128 +++++--------- .../studio_details/studio_details_state.dart | 2 +- .../widgets/details_tab_bar.dart | 134 +++++++++++++++ .../widgets/studio_details_widget.dart | 109 ++---------- lib/views/home/studio/widgets/slider.dart | 25 ++- lib/views/home/studio/widgets/tab_bar.dart | 28 ++-- lib/views/home/widgets/bookmark_button.dart | 6 +- .../home/widgets/floating_navigation_bar.dart | 6 + .../home/widgets/overview/multitype.dart | 18 +- lib/views/widgets/didvan/app_bar.dart | 3 +- lib/views/widgets/didvan/bnb.dart | 63 +++---- lib/views/widgets/didvan/scaffold.dart | 14 +- .../state_handlers/sliver_state_handler.dart | 4 +- 23 files changed, 472 insertions(+), 390 deletions(-) create mode 100644 lib/views/home/studio/studio_details/widgets/details_tab_bar.dart diff --git a/lib/providers/core_provider.dart b/lib/providers/core_provider.dart index 820387d..b9887ef 100644 --- a/lib/providers/core_provider.dart +++ b/lib/providers/core_provider.dart @@ -3,7 +3,7 @@ import 'package:didvan/utils/action_sheet.dart'; import 'package:flutter/cupertino.dart'; class CoreProvier with ChangeNotifier { - AppState _appState = AppState.idle; + AppState _appState = AppState.busy; set appState(AppState newState) { if (newState == AppState.isolatedBusy) { diff --git a/lib/providers/server_data_provider.dart b/lib/providers/server_data_provider.dart index 532bb5e..4ef71bb 100644 --- a/lib/providers/server_data_provider.dart +++ b/lib/providers/server_data_provider.dart @@ -1,5 +1,6 @@ import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; +import 'package:collection/collection.dart'; class ServerDataProvider { static final List directTypes = []; @@ -10,7 +11,10 @@ class ServerDataProvider { static int labelToTypeId(String label) => label.contains('پشتیبانی') ? 7 - : directTypes.firstWhere((element) => element.value.contains(label)).key; + : directTypes + .firstWhereOrNull((element) => element.value.contains(label)) + ?.key ?? + 7; static Future _getDirectTypes() async { final service = RequestService(RequestHelper.directTypes); diff --git a/lib/providers/user_provider.dart b/lib/providers/user_provider.dart index 6748251..ad4a4fb 100644 --- a/lib/providers/user_provider.dart +++ b/lib/providers/user_provider.dart @@ -29,7 +29,8 @@ class UserProvider extends CoreProvier { isAuthenticated = true; final RequestService service = RequestService(RequestHelper.userInfo); await service.httpGet(); - if (service.statusCode == 401 || service.result['user'] == null) { + if (service.statusCode == 401 || + (service.isSuccess && service.result['user'] == null)) { return false; } if (service.isSuccess) { diff --git a/lib/routes/route_generator.dart b/lib/routes/route_generator.dart index a4f2222..2716aaf 100644 --- a/lib/routes/route_generator.dart +++ b/lib/routes/route_generator.dart @@ -180,17 +180,23 @@ class RouteGenerator { final shortestSide = MediaQuery.of(context).size.shortestSide; final bool useMobileLayout = shortestSide < 600; if (kIsWeb && !useMobileLayout) { - return Container( - color: Theme.of(context).colorScheme.background, - alignment: Alignment.center, - child: AspectRatio(aspectRatio: 9 / 16, child: page), + return MediaQuery( + data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), + child: Container( + color: Theme.of(context).colorScheme.background, + alignment: Alignment.center, + child: AspectRatio(aspectRatio: 9 / 16, child: page), + ), ); } - return Container( - color: Theme.of(context).colorScheme.surface, - child: SafeArea( - child: page, - top: false, + return MediaQuery( + data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), + child: Container( + color: Theme.of(context).colorScheme.surface, + child: SafeArea( + child: page, + top: false, + ), ), ); }, diff --git a/lib/views/home/comments/comments_state.dart b/lib/views/home/comments/comments_state.dart index cb52186..5982f54 100644 --- a/lib/views/home/comments/comments_state.dart +++ b/lib/views/home/comments/comments_state.dart @@ -25,7 +25,6 @@ class CommentsState extends CoreProvier { int itemId = 0; Future getComments() async { - appState = AppState.busy; final service = RequestService( RequestHelper.comments(itemId, type), ); diff --git a/lib/views/home/news/news_details/news_details.dart b/lib/views/home/news/news_details/news_details.dart index 55d09d4..08d5a11 100644 --- a/lib/views/home/news/news_details/news_details.dart +++ b/lib/views/home/news/news_details/news_details.dart @@ -43,36 +43,34 @@ class _NewsDetailsState extends State { state: state, builder: (context, state) => Stack( children: [ - if (state.news.isNotEmpty) - IgnorePointer( - ignoring: state.isFetchingNewItem, - child: DidvanPageView( - isRadar: false, - initialIndex: state.initialIndex, - onPageChanged: _onPageChnaged, - scrollController: _scrollController, - items: state.news, - currentIndex: state.currentIndex, - ), + IgnorePointer( + ignoring: state.isFetchingNewItem, + child: DidvanPageView( + isRadar: false, + initialIndex: state.initialIndex, + onPageChanged: _onPageChnaged, + scrollController: _scrollController, + items: state.news, + currentIndex: state.currentIndex, ), - if (state.news.isNotEmpty) - Positioned( - bottom: 0, - left: 0, - right: 0, - child: FloatingNavigationBar( - hasUnmarkConfirmation: - widget.pageData['hasUnmarkConfirmation'], - scrollController: _scrollController, - item: state.currentNews, - onCommentsChanged: state.onCommentsChanged, - onMarkChanged: (value) => widget.pageData['onMarkChanged']( - state.currentNews.id, - value, - ), - isRadar: false, + ), + Positioned( + bottom: 0, + left: 0, + right: 0, + child: FloatingNavigationBar( + hasUnmarkConfirmation: + widget.pageData['hasUnmarkConfirmation'], + scrollController: _scrollController, + item: state.currentNews, + onCommentsChanged: state.onCommentsChanged, + onMarkChanged: (value) => widget.pageData['onMarkChanged']( + state.currentNews.id, + value, ), + isRadar: false, ), + ), ], ), ), diff --git a/lib/views/home/radar/radar_details/radar_details.dart b/lib/views/home/radar/radar_details/radar_details.dart index 1c9eb11..d88f98e 100644 --- a/lib/views/home/radar/radar_details/radar_details.dart +++ b/lib/views/home/radar/radar_details/radar_details.dart @@ -43,43 +43,41 @@ class _RadarDetailsState extends State { state: state, builder: (context, state) => Stack( children: [ - if (state.radars.isNotEmpty) - IgnorePointer( - ignoring: state.isFetchingNewItem, - child: DidvanPageView( - isRadar: true, - initialIndex: state.initialIndex, - onPageChanged: _onPageChanged, - scrollController: _scrollController, - items: state.radars, - currentIndex: state.currentIndex, - ), + IgnorePointer( + ignoring: state.isFetchingNewItem, + child: DidvanPageView( + isRadar: true, + initialIndex: state.initialIndex, + onPageChanged: _onPageChanged, + scrollController: _scrollController, + items: state.radars, + currentIndex: state.currentIndex, ), - if (state.radars.isNotEmpty) - Positioned( - bottom: 0, - left: 0, - right: 0, - child: FloatingNavigationBar( - hasUnmarkConfirmation: - widget.pageData['hasUnmarkConfirmation'], - isRadar: true, - scrollController: _scrollController, - onMarkChanged: (value) => - widget.pageData['onMarkChanged']?.call( + ), + Positioned( + bottom: 0, + left: 0, + right: 0, + child: FloatingNavigationBar( + hasUnmarkConfirmation: + widget.pageData['hasUnmarkConfirmation'], + isRadar: true, + scrollController: _scrollController, + onMarkChanged: (value) => + widget.pageData['onMarkChanged']?.call( + state.currentRadar.id, + value, + ), + item: state.currentRadar, + onCommentsChanged: (count) { + state.onCommentsChanged(count); + widget.pageData['onCommentsChanged']?.call( state.currentRadar.id, - value, - ), - item: state.currentRadar, - onCommentsChanged: (count) { - state.onCommentsChanged(count); - widget.pageData['onCommentsChanged']?.call( - state.currentRadar.id, - count, - ); - }, - ), + count, + ); + }, ), + ), ], ), ), diff --git a/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart b/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart index 9cfc4d1..1fb2353 100644 --- a/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart +++ b/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart @@ -6,8 +6,6 @@ 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; int page = 1; @@ -15,17 +13,8 @@ class FilteredBookmarksState extends CoreProvier { FilteredBookmarksState(this.type); - bool get searching => search != ''; - Future getBookmarks({required int page}) async { - if (search != '') { - lastSearch = search; - } - if (page == 1) { - bookmarks.clear(); - } this.page = page; - appState = AppState.busy; String typeString = ''; if (type == 'video' || type == 'podcast') { typeString = 'studios'; diff --git a/lib/views/home/settings/direct_list/direct_list_state.dart b/lib/views/home/settings/direct_list/direct_list_state.dart index 728305a..c6efe26 100644 --- a/lib/views/home/settings/direct_list/direct_list_state.dart +++ b/lib/views/home/settings/direct_list/direct_list_state.dart @@ -15,7 +15,6 @@ class DirectListState extends CoreProvier { } Future getDirectsList() async { - appState = AppState.busy; final RequestService service = RequestService(RequestHelper.directs); await service.httpGet(); if (service.isSuccess) { diff --git a/lib/views/home/studio/studio_details/studio_details.mobile.dart b/lib/views/home/studio/studio_details/studio_details.mobile.dart index a8d51f1..5c726fb 100644 --- a/lib/views/home/studio/studio_details/studio_details.mobile.dart +++ b/lib/views/home/studio/studio_details/studio_details.mobile.dart @@ -4,7 +4,9 @@ import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/models/view/app_bar_data.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; +import 'package:didvan/views/home/studio/studio_details/widgets/details_tab_bar.dart'; import 'package:didvan/views/home/studio/studio_details/widgets/studio_details_widget.dart'; +import 'package:didvan/views/home/widgets/bookmark_button.dart'; import 'package:didvan/views/widgets/didvan/scaffold.dart'; import 'package:didvan/views/widgets/state_handlers/state_handler.dart'; import 'package:flutter/foundation.dart'; @@ -39,7 +41,7 @@ class _StudioDetailsState extends State { () => state.getStudioDetails(widget.pageData['id']), ); state.args = widget.pageData['args']; - if (!kIsWeb && Platform.isAndroid) WebView.platform = AndroidWebView(); + if (Platform.isAndroid) WebView.platform = AndroidWebView(); super.initState(); } @@ -107,82 +109,108 @@ class _StudioDetailsState extends State { appBarData: _isFullScreen ? null : AppBarData( + trailing: BookmarkButton( + value: state.studio.marked, + onMarkChanged: (value) => widget + .pageData['onMarkChanged'](state.studio.id, value), + gestureSize: 48, + ), isSmall: true, title: state.studio.title, ), - children: [ - SizedBox( - width: ds.width, - height: _isFullScreen ? ds.height : ds.width * 9 / 16, - child: Stack( + showSliversFirst: true, + slivers: [ + SliverAppBar( + automaticallyImplyLeading: false, + pinned: true, + backgroundColor: Theme.of(context).colorScheme.surface, + toolbarHeight: + (_isFullScreen ? ds.height : ds.width * 9 / 16) + + 72 - + MediaQuery.of(context).padding.top, + elevation: 0, + flexibleSpace: Column( children: [ - WebView( - backgroundColor: Theme.of(context).colorScheme.black, - allowsInlineMediaPlayback: true, - initialUrl: Uri.dataFromString( - ''' - - - - - - - ${state.studio.media} - - - ''', - mimeType: 'text/html', - ).toString(), - javascriptMode: JavascriptMode.unrestricted, + SizedBox( + width: ds.width, + height: _isFullScreen ? ds.height : ds.width * 9 / 16, + child: Stack( + children: [ + WebView( + backgroundColor: + Theme.of(context).colorScheme.black, + allowsInlineMediaPlayback: true, + initialUrl: Uri.dataFromString( + ''' + + + + + + + ${state.studio.media} + + + ''', + mimeType: 'text/html', + ).toString(), + javascriptMode: JavascriptMode.unrestricted, + ), + Positioned( + right: 42, + bottom: 8, + child: GestureDetector( + onTap: () => _changeFullSceen(!_isFullScreen), + child: Container( + color: Colors.transparent, + width: 24, + height: 30, + ), + ), + ), + ], + ), ), - Positioned( - right: 42, - bottom: 8, - child: GestureDetector( - onTap: () => _changeFullSceen(!_isFullScreen), - child: Container( - color: Colors.transparent, - width: 24, - height: 30, + DetailsTabBar( + isVideo: true, + onCommentsTabSelected: () => Future.delayed( + const Duration(milliseconds: 100), + () => _scrollController.animateTo( + _scrollController.position.maxScrollExtent, + duration: DesignConfig.lowAnimationDuration, + curve: Curves.easeIn, ), ), ), ], ), ), - const SizedBox(height: 20), + ], + children: [ StudioDetailsWidget( scrollController: _scrollController, - onCommentsTabSelected: () => Future.delayed( - const Duration(milliseconds: 100), - () => _scrollController.animateTo( - _scrollController.position.maxScrollExtent, - duration: DesignConfig.lowAnimationDuration, - curve: Curves.easeIn, - ), - ), studio: state.studio, ), ], diff --git a/lib/views/home/studio/studio_details/studio_details.web.dart b/lib/views/home/studio/studio_details/studio_details.web.dart index c3040ff..65b1da5 100644 --- a/lib/views/home/studio/studio_details/studio_details.web.dart +++ b/lib/views/home/studio/studio_details/studio_details.web.dart @@ -2,18 +2,17 @@ import 'dart:io'; import 'dart:ui' as ui; import 'package:didvan/config/design_config.dart'; -import 'package:didvan/config/theme_data.dart'; import 'package:didvan/models/view/app_bar_data.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; +import 'package:didvan/views/home/studio/studio_details/widgets/details_tab_bar.dart'; import 'package:didvan/views/home/studio/studio_details/widgets/studio_details_widget.dart'; +import 'package:didvan/views/home/widgets/bookmark_button.dart'; import 'package:didvan/views/widgets/didvan/scaffold.dart'; import 'package:didvan/views/widgets/state_handlers/state_handler.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'package:universal_html/html.dart' as html; -import 'package:webview_flutter/webview_flutter.dart'; class StudioDetails extends StatefulWidget { final Map pageData; @@ -28,10 +27,6 @@ class _StudioDetailsState extends State { final _scrollController = ScrollController(); bool _isFullScreen = false; - bool _isInit = true; - - double _dwInPortrait = 0; - double _scaleInPortrait = 1; @override void initState() { @@ -41,7 +36,6 @@ class _StudioDetailsState extends State { () => state.getStudioDetails(widget.pageData['id']), ); state.args = widget.pageData['args']; - if (!kIsWeb && Platform.isAndroid) WebView.platform = AndroidWebView(); super.initState(); } @@ -77,12 +71,6 @@ class _StudioDetailsState extends State { @override Widget build(BuildContext context) { final ds = MediaQuery.of(context).size; - if (_isInit) { - _dwInPortrait = MediaQuery.of(context).size.width; - _scaleInPortrait = _dwInPortrait / 576; - _isInit = false; - } - return Consumer( builder: (context, state, child) => StateHandler( state: state, @@ -121,87 +109,47 @@ class _StudioDetailsState extends State { : AppBarData( isSmall: true, title: state.studio.title, + trailing: BookmarkButton( + value: state.studio.marked, + onMarkChanged: (value) => widget + .pageData['onMarkChanged'](state.studio.id, value), + gestureSize: 48, + ), ), - children: [ - if (kIsWeb) - const AspectRatio( - aspectRatio: 16 / 9, - child: HtmlElementView(viewType: 'video'), - ), - if (!kIsWeb) - SizedBox( - width: ds.width, - height: _isFullScreen ? ds.height : ds.width * 9 / 16, - child: Stack( - children: [ - WebView( - backgroundColor: Theme.of(context).colorScheme.black, - allowsInlineMediaPlayback: true, - initialUrl: Uri.dataFromString( - ''' - - - - - - - ${state.studio.media} - - - ''', - mimeType: 'text/html', - ).toString(), - javascriptMode: JavascriptMode.unrestricted, - ), - if (!kIsWeb) - Positioned( - right: 42, - bottom: 8, - child: GestureDetector( - onTap: () => _changeFullSceen(!_isFullScreen), - child: Container( - color: Colors.transparent, - width: 24, - height: 30, - ), - ), + showSliversFirst: true, + slivers: [ + SliverAppBar( + automaticallyImplyLeading: false, + pinned: true, + elevation: 0, + toolbarHeight: + (_isFullScreen ? ds.height : ds.width * 9 / 16) + + 72 - + MediaQuery.of(context).padding.top, + flexibleSpace: Column( + children: [ + const AspectRatio( + aspectRatio: 16 / 9, + child: HtmlElementView(viewType: 'video'), + ), + DetailsTabBar( + isVideo: true, + onCommentsTabSelected: () => Future.delayed( + const Duration(milliseconds: 100), + () => _scrollController.animateTo( + _scrollController.position.maxScrollExtent, + duration: DesignConfig.lowAnimationDuration, + curve: Curves.easeIn, ), - ], - ), + ), + ), + ], ), - const SizedBox(height: 20), + ), + ], + children: [ StudioDetailsWidget( scrollController: _scrollController, - onCommentsTabSelected: () => Future.delayed( - const Duration(milliseconds: 100), - () => _scrollController.animateTo( - _scrollController.position.maxScrollExtent, - duration: DesignConfig.lowAnimationDuration, - curve: Curves.easeIn, - ), - ), studio: state.studio, ), ], diff --git a/lib/views/home/studio/studio_details/studio_details_state.dart b/lib/views/home/studio/studio_details/studio_details_state.dart index 49f13d1..2f8d867 100644 --- a/lib/views/home/studio/studio_details/studio_details_state.dart +++ b/lib/views/home/studio/studio_details/studio_details_state.dart @@ -110,7 +110,7 @@ class StudioDetailsState extends CoreProvier { final service = RequestService(RequestHelper.tag( ids: studio.tags.map((tag) => tag.id).toList(), itemId: studio.id, - type: studio.media.contains('iframe') ? 'video' : 'podcast', + type: args.type, )); await service.httpGet(); if (service.isSuccess) { diff --git a/lib/views/home/studio/studio_details/widgets/details_tab_bar.dart b/lib/views/home/studio/studio_details/widgets/details_tab_bar.dart new file mode 100644 index 0000000..ce2a4a2 --- /dev/null +++ b/lib/views/home/studio/studio_details/widgets/details_tab_bar.dart @@ -0,0 +1,134 @@ +import 'package:didvan/config/design_config.dart'; +import 'package:didvan/config/theme_data.dart'; +import 'package:didvan/constants/app_icons.dart'; +import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; +import 'package:didvan/views/widgets/didvan/text.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class DetailsTabBar extends StatelessWidget { + final bool isVideo; + final VoidCallback onCommentsTabSelected; + + const DetailsTabBar({ + Key? key, + required this.isVideo, + required this.onCommentsTabSelected, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final state = context.read(); + return WillPopScope( + onWillPop: () async { + state.selectedDetailsIndex = 0; + return true; + }, + child: Container( + height: 72, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + boxShadow: [ + BoxShadow( + color: const Color(0XFF1B3C59).withOpacity(0.15), + offset: const Offset(0, 8), + blurRadius: 8, + spreadRadius: 0, + ) + ], + ), + child: FittedBox( + fit: BoxFit.scaleDown, + child: SizedBox( + width: MediaQuery.of(context).size.width, + child: Row( + children: [ + _TabItem( + icon: DidvanIcons.description_solid, + title: 'توضیحات', + onTap: () => state.selectedDetailsIndex = 0, + isSelected: state.selectedDetailsIndex == 0, + isVideo: isVideo, + ), + _TabItem( + icon: DidvanIcons.chats_solid, + title: 'نظرات', + onTap: () { + state.selectedDetailsIndex = 1; + onCommentsTabSelected(); + }, + isSelected: state.selectedDetailsIndex == 1, + isVideo: isVideo, + ), + _TabItem( + icon: DidvanIcons.puzzle_solid, + title: 'مطالب مرتبط', + onTap: () => state.selectedDetailsIndex = 2, + isSelected: state.selectedDetailsIndex == 2, + isVideo: isVideo, + ), + ], + ), + ), + ), + ), + ); + } +} + +class _TabItem extends StatelessWidget { + final IconData icon; + final String title; + final VoidCallback onTap; + final bool isSelected; + final bool isVideo; + const _TabItem({ + Key? key, + required this.icon, + required this.title, + required this.onTap, + required this.isSelected, + required this.isVideo, + }) : super(key: key); + + Color? _color(context) { + if (isSelected) { + if (isVideo) { + return Theme.of(context).colorScheme.secondary; + } + return Theme.of(context).colorScheme.focusedBorder; + } + return Theme.of(context).colorScheme.border; + } + + @override + Widget build(BuildContext context) { + return Expanded( + child: GestureDetector( + onTap: onTap, + child: Container( + color: Colors.transparent, + child: Column( + children: [ + Icon( + icon, + color: _color(context), + ), + AnimatedContainer( + duration: DesignConfig.lowAnimationDuration, + width: isSelected ? 64 : 0, + height: 1, + color: _color(context), + ), + DidvanText( + title, + color: _color(context), + style: Theme.of(context).textTheme.caption, + ) + ], + ), + ), + ), + ); + } +} diff --git a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart index 6c99823..6edbac4 100644 --- a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart +++ b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart @@ -5,6 +5,7 @@ import 'package:didvan/models/studio_details_data.dart'; import 'package:didvan/views/home/comments/comments.dart'; import 'package:didvan/views/home/comments/comments_state.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; +import 'package:didvan/views/home/studio/studio_details/widgets/details_tab_bar.dart'; import 'package:didvan/views/home/widgets/overview/multitype.dart'; import 'package:didvan/views/home/widgets/tag_item.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; @@ -16,15 +17,15 @@ import 'package:provider/provider.dart'; class StudioDetailsWidget extends StatelessWidget { final StudioDetailsData studio; final ScrollController? scrollController; - final VoidCallback onCommentsTabSelected; + final VoidCallback? onCommentsTabSelected; const StudioDetailsWidget({ Key? key, required this.studio, - required this.onCommentsTabSelected, + this.onCommentsTabSelected, this.scrollController, }) : super(key: key); - bool get _isVideo => studio.media.contains('ifram'); + bool get _isVideo => studio.media.contains('iframe'); @override Widget build(BuildContext context) { @@ -35,38 +36,12 @@ class StudioDetailsWidget extends StatelessWidget { child: Column( mainAxisSize: MainAxisSize.min, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - const SizedBox(), - _TabItem( - icon: DidvanIcons.description_solid, - title: 'توضیحات', - onTap: () => state.selectedDetailsIndex = 0, - isSelected: state.selectedDetailsIndex == 0, - isVideo: _isVideo, - ), - _TabItem( - icon: DidvanIcons.chats_solid, - title: 'نظرات', - onTap: () { - state.selectedDetailsIndex = 1; - onCommentsTabSelected(); - }, - isSelected: state.selectedDetailsIndex == 1, - isVideo: _isVideo, - ), - _TabItem( - icon: DidvanIcons.puzzle_solid, - title: 'مطالب مرتبط', - onTap: () => state.selectedDetailsIndex = 2, - isSelected: state.selectedDetailsIndex == 2, - isVideo: _isVideo, - ), - const SizedBox(), - ], - ), - const SizedBox(height: 24), + if (!_isVideo) + DetailsTabBar( + isVideo: _isVideo, + onCommentsTabSelected: onCommentsTabSelected ?? () {}, + ), + if (state.selectedDetailsIndex != 1) const SizedBox(height: 16), StateHandler( onRetry: () {}, state: state, @@ -116,7 +91,10 @@ class StudioDetailsWidget extends StatelessWidget { return ChangeNotifierProvider( create: (context) => CommentsState(), child: SizedBox( - height: ds.height - 180, + height: ds.height - + ds.width * 9 / 16 - + 128 - + MediaQuery.of(context).padding.top, child: Comments( pageData: { 'id': studio.id, @@ -164,63 +142,6 @@ class StudioDetailsWidget extends StatelessWidget { } } -class _TabItem extends StatelessWidget { - final IconData icon; - final String title; - final VoidCallback onTap; - final bool isSelected; - final bool isVideo; - const _TabItem({ - Key? key, - required this.icon, - required this.title, - required this.onTap, - required this.isSelected, - required this.isVideo, - }) : super(key: key); - - Color? _color(context) { - if (isSelected) { - if (isVideo) { - return Theme.of(context).colorScheme.secondary; - } - return Theme.of(context).colorScheme.focusedBorder; - } - return Theme.of(context).colorScheme.border; - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: onTap, - child: Container( - color: Colors.transparent, - child: Column( - children: [ - Icon( - icon, - color: _color(context), - ), - if (isSelected) const SizedBox(height: 8), - if (isSelected) - Container( - width: 64, - height: 1, - color: _color(context), - ), - if (isSelected) - DidvanText( - title, - color: _color(context), - style: Theme.of(context).textTheme.caption, - ) - ], - ), - ), - ); - } -} - class _StudioPreview extends StatelessWidget { final bool isNext; final StudioDetailsData studio; @@ -279,7 +200,7 @@ class _StudioPreview extends StatelessWidget { const SizedBox(height: 8), DidvanText( studio.title, - maxLines: 3, + maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, style: Theme.of(context).textTheme.overline, diff --git a/lib/views/home/studio/widgets/slider.dart b/lib/views/home/studio/widgets/slider.dart index ea8cbdd..c072168 100644 --- a/lib/views/home/studio/widgets/slider.dart +++ b/lib/views/home/studio/widgets/slider.dart @@ -114,7 +114,10 @@ class _StudioSliderState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ for (var i = 0; i < state.sliders.length; i++) - _SliderIndicator(isCurrentIndex: selectedIndex == i), + _SliderIndicator( + isCurrentIndex: selectedIndex == i, + isVideo: state.videosSelected, + ), ], ), const SizedBox(height: 16), @@ -125,8 +128,19 @@ class _StudioSliderState extends State { class _SliderIndicator extends StatelessWidget { final bool isCurrentIndex; - const _SliderIndicator({Key? key, required this.isCurrentIndex}) - : super(key: key); + final bool isVideo; + const _SliderIndicator({ + Key? key, + required this.isCurrentIndex, + required this.isVideo, + }) : super(key: key); + + Color _color(BuildContext context) { + if (isVideo) { + return Theme.of(context).colorScheme.secondary; + } + return Theme.of(context).colorScheme.focusedBorder; + } @override Widget build(BuildContext context) { @@ -137,11 +151,10 @@ class _SliderIndicator extends StatelessWidget { margin: const EdgeInsets.only(left: 4), decoration: BoxDecoration( border: Border.all( - color: Theme.of(context).colorScheme.focusedBorder, + color: _color(context), ), shape: BoxShape.circle, - color: - isCurrentIndex ? Theme.of(context).colorScheme.focusedBorder : null, + color: isCurrentIndex ? _color(context) : null, ), ); } diff --git a/lib/views/home/studio/widgets/tab_bar.dart b/lib/views/home/studio/widgets/tab_bar.dart index 3d915a8..b1ed37b 100644 --- a/lib/views/home/studio/widgets/tab_bar.dart +++ b/lib/views/home/studio/widgets/tab_bar.dart @@ -20,9 +20,7 @@ class StudioTabBar extends StatelessWidget { padding: const EdgeInsets.all(4), decoration: BoxDecoration( border: Border.all( - color: state.videosSelected - ? Theme.of(context).colorScheme.secondary - : Theme.of(context).colorScheme.primary, + color: Theme.of(context).colorScheme.border, ), borderRadius: DesignConfig.lowBorderRadius, ), @@ -88,19 +86,17 @@ class _StudioTypeButton extends StatelessWidget { size: 32, color: _color(context), ), - if (!isSelected) const SizedBox(height: 18), - if (isSelected) - Container( - width: 88, - height: 1, - color: _color(context), - ), - if (isSelected) - DidvanText( - title, - style: Theme.of(context).textTheme.overline, - color: _color(context), - ) + AnimatedContainer( + duration: DesignConfig.lowAnimationDuration, + width: isSelected ? 88 : 0, + height: 1, + color: _color(context), + ), + DidvanText( + title, + style: Theme.of(context).textTheme.overline, + color: _color(context), + ) ], ), ), diff --git a/lib/views/home/widgets/bookmark_button.dart b/lib/views/home/widgets/bookmark_button.dart index 8f1d5aa..2387ca0 100644 --- a/lib/views/home/widgets/bookmark_button.dart +++ b/lib/views/home/widgets/bookmark_button.dart @@ -1,3 +1,4 @@ +import 'package:didvan/config/design_config.dart'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/view/action_sheet_data.dart'; import 'package:didvan/utils/action_sheet.dart'; @@ -43,7 +44,10 @@ class _BookmarkButtonState extends State { Widget build(BuildContext context) { return DidvanIconButton( gestureSize: widget.gestureSize, - color: widget.color, + color: widget.color ?? + (DesignConfig.isDark || !_value + ? null + : Theme.of(context).colorScheme.primary), icon: _value ? DidvanIcons.bookmark_solid : DidvanIcons.bookmark_regular, onPressed: () async { bool confirm = false; diff --git a/lib/views/home/widgets/floating_navigation_bar.dart b/lib/views/home/widgets/floating_navigation_bar.dart index dd453a9..aac4297 100644 --- a/lib/views/home/widgets/floating_navigation_bar.dart +++ b/lib/views/home/widgets/floating_navigation_bar.dart @@ -104,6 +104,9 @@ class _FloatingNavigationBarState extends State { const Spacer(), if (widget.isRadar) BookmarkButton( + color: DesignConfig.isDark + ? Theme.of(context).colorScheme.focusedBorder + : Theme.of(context).colorScheme.focused, askForConfirmation: widget.hasUnmarkConfirmation, value: widget.item.marked, onMarkChanged: (value) { @@ -143,6 +146,9 @@ class _FloatingNavigationBarState extends State { if (!widget.isRadar) const SizedBox(width: 12), if (!widget.isRadar) BookmarkButton( + color: DesignConfig.isDark + ? Theme.of(context).colorScheme.focusedBorder + : Theme.of(context).colorScheme.focused, askForConfirmation: widget.hasUnmarkConfirmation, value: widget.item.marked, onMarkChanged: (value) { diff --git a/lib/views/home/widgets/overview/multitype.dart b/lib/views/home/widgets/overview/multitype.dart index 6e5a5aa..136d78e 100644 --- a/lib/views/home/widgets/overview/multitype.dart +++ b/lib/views/home/widgets/overview/multitype.dart @@ -5,6 +5,7 @@ import 'package:didvan/models/requests/news.dart'; import 'package:didvan/models/requests/radar.dart'; import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/routes/routes.dart'; +import 'package:didvan/utils/date_time.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; import 'package:didvan/views/widgets/didvan/card.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; @@ -127,7 +128,22 @@ class MultitypeOverview extends StatelessWidget { DateTime.parse(item.createdAt).toPersianDateStr(), style: Theme.of(context).textTheme.overline, ), - // DidvanText('text'), + const Spacer(), + if ((item.timeToRead ?? item.duration) != null) ...[ + const Icon( + DidvanIcons.timer_light, + size: 18, + ), + const SizedBox(width: 4), + DidvanText( + item.timeToRead != null + ? 'خواندن در ${item.timeToRead} دقیقه' + : DateTimeUtils.normalizeTimeDuration( + Duration(seconds: item.duration!), + ), + style: Theme.of(context).textTheme.overline, + ), + ] ], ), ], diff --git a/lib/views/widgets/didvan/app_bar.dart b/lib/views/widgets/didvan/app_bar.dart index f7f00e3..1f1e6ed 100644 --- a/lib/views/widgets/didvan/app_bar.dart +++ b/lib/views/widgets/didvan/app_bar.dart @@ -20,7 +20,8 @@ class DidvanAppBar extends StatelessWidget { return Container( height: appBarData.isSmall ? 56 : 72, width: MediaQuery.of(context).size.width, - padding: const EdgeInsets.only(right: 4, left: 20), + padding: + EdgeInsets.only(right: 4, left: appBarData.trailing == null ? 20 : 0), decoration: BoxDecoration( border: hasBorder ? Border( diff --git a/lib/views/widgets/didvan/bnb.dart b/lib/views/widgets/didvan/bnb.dart index c5f4ec5..78af00d 100644 --- a/lib/views/widgets/didvan/bnb.dart +++ b/lib/views/widgets/didvan/bnb.dart @@ -198,35 +198,42 @@ class DidvanBNB extends StatelessWidget { color: Theme.of(context).colorScheme.surface, ), ), - persistentHeader: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - AudioPlayerWidget( - podcast: MediaService.currentPodcast!, - ), - Container( - width: MediaQuery.of(context).size.width, - color: Theme.of(context).colorScheme.surface, - child: Column( - children: [ - DidvanIconButton( - size: 32, - icon: DidvanIcons.angle_down_regular, - onPressed: () { - if (!isExpanded) { - sheetKey.currentState?.expand(); - isExpanded = true; - return; - } - isExpanded = false; - sheetKey.currentState?.contract(); - }, - ), - const SizedBox(height: 16), - ], + persistentHeader: GestureDetector( + onVerticalDragUpdate: (details) { + if (details.delta.dy > 10) { + Navigator.of(context).pop(); + } + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + AudioPlayerWidget( + podcast: MediaService.currentPodcast!, ), - ), - ], + Container( + width: MediaQuery.of(context).size.width, + color: Theme.of(context).colorScheme.surface, + child: Column( + children: [ + DidvanIconButton( + size: 32, + icon: DidvanIcons.angle_down_regular, + onPressed: () { + if (!isExpanded) { + sheetKey.currentState?.expand(); + isExpanded = true; + return; + } + isExpanded = false; + sheetKey.currentState?.contract(); + }, + ), + const SizedBox(height: 16), + ], + ), + ), + ], + ), ), expandableContent: state.appState == AppState.busy ? const SizedBox() diff --git a/lib/views/widgets/didvan/scaffold.dart b/lib/views/widgets/didvan/scaffold.dart index bca06ac..8e31440 100644 --- a/lib/views/widgets/didvan/scaffold.dart +++ b/lib/views/widgets/didvan/scaffold.dart @@ -10,6 +10,7 @@ class DidvanScaffold extends StatefulWidget { final Color? backgroundColor; final bool reverse; final ScrollController? scrollController; + final bool showSliversFirst; const DidvanScaffold({ Key? key, @@ -20,6 +21,7 @@ class DidvanScaffold extends StatefulWidget { this.backgroundColor, this.reverse = false, this.scrollController, + this.showSliversFirst = false, }) : super(key: key); @override @@ -60,7 +62,7 @@ class _DidvanScaffoldState extends State { pinned: true, flexibleSpace: DidvanAppBar(appBarData: widget.appBarData!), ), - if (widget.children != null) + if (widget.children != null && !widget.showSliversFirst) SliverPadding( padding: widget.padding, sliver: SliverList( @@ -76,6 +78,16 @@ class _DidvanScaffoldState extends State { padding: widget.padding, sliver: widget.slivers![i], ), + if (widget.children != null && widget.showSliversFirst) + SliverPadding( + padding: widget.padding, + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) => widget.children![index], + childCount: widget.children!.length, + ), + ), + ), if (widget.reverse) SliverToBoxAdapter( child: SizedBox( diff --git a/lib/views/widgets/state_handlers/sliver_state_handler.dart b/lib/views/widgets/state_handlers/sliver_state_handler.dart index cb762f5..f27ed14 100644 --- a/lib/views/widgets/state_handlers/sliver_state_handler.dart +++ b/lib/views/widgets/state_handlers/sliver_state_handler.dart @@ -40,7 +40,9 @@ class SliverStateHandler extends SliverList { if (enableEmptyState && state.appState == AppState.idle) { return Padding( padding: EdgeInsets.only( - top: centerEmptyState ? 120 : 20, + top: centerEmptyState + ? MediaQuery.of(context).size.height / 4 + : 20, bottom: 20, ), child: emptyState, From 658d74b9ed067c7d8ab717a35270ca64c6bd78fa Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Tue, 29 Mar 2022 12:07:56 +0430 Subject: [PATCH 14/54] asc and desc added to studio api --- lib/models/requests/studio.dart | 2 ++ lib/services/network/request_helper.dart | 2 ++ lib/views/home/studio/studio.dart | 14 ++++++++++++-- lib/views/home/studio/studio_state.dart | 8 +++++--- 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/models/requests/studio.dart b/lib/models/requests/studio.dart index 74feeaa..221ea0a 100644 --- a/lib/models/requests/studio.dart +++ b/lib/models/requests/studio.dart @@ -3,9 +3,11 @@ class StudioRequestArgs { final String? search; final String? order; final String? type; + final bool? asc; const StudioRequestArgs({ required this.page, + this.asc, this.search, this.order, this.type, diff --git a/lib/services/network/request_helper.dart b/lib/services/network/request_helper.dart index 84e2a0f..ead496c 100644 --- a/lib/services/network/request_helper.dart +++ b/lib/services/network/request_helper.dart @@ -104,6 +104,7 @@ class RequestHelper { MapEntry('type', args.type), MapEntry('order', args.order), MapEntry('search', args.search), + MapEntry('asc', args.asc), ]); static String studioOverviews({required StudioRequestArgs args}) => _baseStudioUrl + @@ -112,6 +113,7 @@ class RequestHelper { MapEntry('type', args.type), MapEntry('order', args.order), MapEntry('search', args.search), + MapEntry('asc', args.asc), ]); static String mark(int id, String type) => baseUrl + '/$type/$id/mark'; diff --git a/lib/views/home/studio/studio.dart b/lib/views/home/studio/studio.dart index 51d7b8e..e23a38a 100644 --- a/lib/views/home/studio/studio.dart +++ b/lib/views/home/studio/studio.dart @@ -137,6 +137,7 @@ class _StudioState extends State { order: state.order, search: state.search, type: state.type, + asc: state.selectedSortTypeIndex == 1, ), ) : PodcastOverview( @@ -147,6 +148,7 @@ class _StudioState extends State { order: state.order, search: state.search, type: state.type, + asc: state.selectedSortTypeIndex == 1, ), ), childCount: state.studios.length, @@ -185,7 +187,7 @@ class _StudioState extends State { ), const SizedBox(height: 24), DidvanRadialButton( - title: 'پربازدیدترین‌ها', + title: 'قدیمی‌ترین‌ها', onSelected: () => setState( () => state.selectedSortTypeIndex = 1, ), @@ -193,12 +195,20 @@ class _StudioState extends State { ), const SizedBox(height: 24), DidvanRadialButton( - title: 'پربحث‌ترین‌ها', + title: 'پربازدیدترین‌ها', onSelected: () => setState( () => state.selectedSortTypeIndex = 2, ), value: state.selectedSortTypeIndex == 2, ), + const SizedBox(height: 24), + DidvanRadialButton( + title: 'پربحث‌ترین‌ها', + onSelected: () => setState( + () => state.selectedSortTypeIndex = 3, + ), + value: state.selectedSortTypeIndex == 3, + ), ], ), ), diff --git a/lib/views/home/studio/studio_state.dart b/lib/views/home/studio/studio_state.dart index f07effa..e6d9c66 100644 --- a/lib/views/home/studio/studio_state.dart +++ b/lib/views/home/studio/studio_state.dart @@ -34,14 +34,15 @@ class StudioState extends CoreProvier { } String get order { - if (selectedSortTypeIndex == 0) return 'date'; - if (selectedSortTypeIndex == 1) return 'view'; + if (selectedSortTypeIndex == 0 || selectedSortTypeIndex == 1) return 'date'; + if (selectedSortTypeIndex == 2) return 'view'; return 'comment'; } String get orderString { if (selectedSortTypeIndex == 0) return 'تازه‌ترین‌ها'; - if (selectedSortTypeIndex == 1) return 'پربازدیدترین‌ها'; + if (selectedSortTypeIndex == 1) return 'قدیمی‌ترین‌ها'; + if (selectedSortTypeIndex == 2) return 'پربازدیدترین‌ها'; return 'پربحث‌نرین‌ها'; } @@ -89,6 +90,7 @@ class StudioState extends CoreProvier { type: type, search: search, order: order, + asc: selectedSortTypeIndex == 1, ), ), ); From 9369dffc037c42d1ac4cc2086ae568510756eba6 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Tue, 29 Mar 2022 15:14:50 +0430 Subject: [PATCH 15/54] offline podcasts + code cleaning + bug fixes --- lib/services/media/media.dart | 15 +++--- lib/services/network/request.dart | 13 +++-- lib/services/storage/storage.dart | 18 +++++++ lib/views/home/comments/comments.dart | 2 + lib/views/home/direct/direct_state.dart | 3 ++ .../home/direct/widgets/audio_widget.dart | 48 +++++++++++++++++-- lib/views/home/direct/widgets/message.dart | 1 + .../home/direct/widgets/message_box.dart | 5 +- .../studio_details/studio_details.mobile.dart | 1 - .../studio_details/studio_details.web.dart | 1 - .../studio_details/studio_details_state.dart | 30 +++++++++++- .../widgets/studio_details_widget.dart | 4 +- lib/views/home/studio/studio_state.dart | 31 ++++++++++-- .../widgets/audio/audio_player_widget.dart | 12 ++++- .../home/widgets/audio/audio_slider.dart | 2 +- lib/views/home/widgets/overview/podcast.dart | 32 ++++++++++--- .../widgets/player_controller_button.dart | 34 ------------- lib/views/widgets/didvan/bnb.dart | 6 ++- lib/views/widgets/didvan/scaffold.dart | 3 ++ 19 files changed, 187 insertions(+), 74 deletions(-) delete mode 100644 lib/views/home/widgets/player_controller_button.dart diff --git a/lib/services/media/media.dart b/lib/services/media/media.dart index 353b309..a6595fd 100644 --- a/lib/services/media/media.dart +++ b/lib/services/media/media.dart @@ -23,15 +23,13 @@ class MediaService { static Future handleAudioPlayback({ required dynamic audioSource, + required int id, + bool? isNetworkAudio, bool isVoiceMessage = true, }) async { - bool isNetworkAudio = audioSource.runtimeType == String; String tag; - if (isNetworkAudio) { - tag = audioSource; - } else { - tag = audioSource.path; - } + tag = '${isVoiceMessage ? 'message' : 'podcast'}-$id'; + isNetworkAudio ??= audioSource.runtimeType == String; if (audioPlayerTag == tag) { if (audioPlayer.playing) { await audioPlayer.pause(); @@ -51,10 +49,9 @@ class MediaService { ); } else { if (kIsWeb) { - await audioPlayer - .setUrl(audioSource!.uri.path.replaceAll('%3A', ':')); + await audioPlayer.setUrl(audioSource!.replaceAll('%3A', ':')); } else { - await audioPlayer.setFilePath(audioSource.path); + await audioPlayer.setFilePath(audioSource); } } audioPlayer.play(); diff --git a/lib/services/network/request.dart b/lib/services/network/request.dart index 7501746..eeabccc 100644 --- a/lib/services/network/request.dart +++ b/lib/services/network/request.dart @@ -1,6 +1,6 @@ import 'dart:convert'; import 'dart:developer'; -import 'dart:io'; +import 'package:didvan/services/storage/storage.dart'; import 'package:http/http.dart' as http; import 'package:http_parser/http_parser.dart' as parser; import 'package:permission_handler/permission_handler.dart'; @@ -164,11 +164,14 @@ class RequestService { } } - Future download() async { - Permission.manageExternalStorage.request(); + Future download(String fileName, String subDirectory) async { + Permission.storage.request(); final response = await http.get(Uri.parse(url)); - final file = await File('/storage/emulated/0/Download/file.mp3').create(); - await file.writeAsBytes(response.bodyBytes); + StorageService.createFile( + bytes: response.bodyBytes, + subDirectory: subDirectory, + name: fileName, + ); } void _handleResponse(http.Response? response) { diff --git a/lib/services/storage/storage.dart b/lib/services/storage/storage.dart index 012936c..6547633 100644 --- a/lib/services/storage/storage.dart +++ b/lib/services/storage/storage.dart @@ -1,3 +1,6 @@ +import 'dart:typed_data'; +import 'dart:io' as io; + import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:universal_html/html.dart'; @@ -6,6 +9,21 @@ class StorageService { static late String appTempsDir; static const FlutterSecureStorage _storage = FlutterSecureStorage(); + static Future createFile({ + required Uint8List bytes, + required String subDirectory, + required String name, + }) async { + final dir = io.Directory(appDocsDir + '/$subDirectory'); + if (!await dir.exists()) { + await dir.create(recursive: true); + } + final file = await io.File( + appDocsDir + '/$subDirectory/podcast-$name.mp3', + ).create(recursive: true); + await file.writeAsBytes(bytes); + } + static Future setValue({ required String key, required dynamic value, diff --git a/lib/views/home/comments/comments.dart b/lib/views/home/comments/comments.dart index baf7d3e..d4c36aa 100644 --- a/lib/views/home/comments/comments.dart +++ b/lib/views/home/comments/comments.dart @@ -59,6 +59,7 @@ class _CommentsState extends State { child: Stack( children: [ DidvanScaffold( + physics: const BouncingScrollPhysics(), backgroundColor: Theme.of(context).colorScheme.surface, appBarData: _isPage ? AppBarData( @@ -77,6 +78,7 @@ class _CommentsState extends State { itemPadding: const EdgeInsets.symmetric(vertical: 16), childCount: state.comments.length, placeholder: const _CommentPlaceholder(), + centerEmptyState: _isPage, enableEmptyState: state.comments.isEmpty, emptyState: EmptyState( asset: Assets.emptyChat, diff --git a/lib/views/home/direct/direct_state.dart b/lib/views/home/direct/direct_state.dart index a4a14aa..346eec3 100644 --- a/lib/views/home/direct/direct_state.dart +++ b/lib/views/home/direct/direct_state.dart @@ -4,6 +4,7 @@ import 'package:didvan/models/enums.dart'; import 'package:didvan/models/message_data/message_data.dart'; import 'package:didvan/models/message_data/radar_attachment.dart'; import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/services/media/media.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; import 'package:flutter/foundation.dart'; @@ -46,6 +47,7 @@ class DirectState extends CoreProvier { } Future startRecording() async { + text = null; await _recorder.hasPermission(); if (!kIsWeb) { Vibrate.feedback(FeedbackType.medium); @@ -88,6 +90,7 @@ class DirectState extends CoreProvier { Future sendMessage() async { if ((text == null || text!.isEmpty) && recordedFile == null) return; + MediaService.audioPlayer.stop(); messages.insert( 0, MessageData( diff --git a/lib/views/home/direct/widgets/audio_widget.dart b/lib/views/home/direct/widgets/audio_widget.dart index a33827c..5d99777 100644 --- a/lib/views/home/direct/widgets/audio_widget.dart +++ b/lib/views/home/direct/widgets/audio_widget.dart @@ -1,15 +1,22 @@ import 'dart:io'; +import 'package:didvan/config/theme_data.dart'; +import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/services/media/media.dart'; import 'package:didvan/views/home/widgets/audio/audio_slider.dart'; -import 'package:didvan/views/home/widgets/player_controller_button.dart'; +import 'package:didvan/views/widgets/didvan/icon_button.dart'; import 'package:flutter/material.dart'; class AudioWidget extends StatelessWidget { final String? audioUrl; final File? audioFile; - const AudioWidget({Key? key, this.audioUrl, this.audioFile}) - : super(key: key); + final int id; + const AudioWidget({ + Key? key, + this.audioUrl, + this.audioFile, + required this.id, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -20,12 +27,13 @@ class AudioWidget extends StatelessWidget { children: [ Expanded( child: AudioSlider( - tag: audioUrl ?? audioFile!.path, + tag: 'message-$id', ), ), - AudioControllerButton( + _AudioControllerButton( audioFile: audioFile, audioUrl: audioUrl, + id: id, ), ], ); @@ -33,3 +41,33 @@ class AudioWidget extends StatelessWidget { ); } } + +class _AudioControllerButton extends StatelessWidget { + final String? audioUrl; + final File? audioFile; + final int id; + + const _AudioControllerButton( + {Key? key, this.audioUrl, this.audioFile, required this.id}) + : super(key: key); + + bool get _nowPlaying => MediaService.audioPlayerTag == 'message-$id'; + + @override + Widget build(BuildContext context) { + return DidvanIconButton( + icon: MediaService.audioPlayer.playing == true && _nowPlaying + ? DidvanIcons.pause_circle_solid + : DidvanIcons.play_circle_solid, + color: Theme.of(context).colorScheme.focusedBorder, + onPressed: () { + MediaService.handleAudioPlayback( + audioSource: audioFile?.path ?? audioUrl, + id: id, + isNetworkAudio: audioFile == null, + isVoiceMessage: true, + ); + }, + ); + } +} diff --git a/lib/views/home/direct/widgets/message.dart b/lib/views/home/direct/widgets/message.dart index 49ab63d..ff90eb1 100644 --- a/lib/views/home/direct/widgets/message.dart +++ b/lib/views/home/direct/widgets/message.dart @@ -65,6 +65,7 @@ class Message extends StatelessWidget { AudioWidget( audioFile: message.audioFile, audioUrl: message.audio, + id: message.id, ), if (message.radar != null) const DidvanDivider(), if (message.radar != null) const SizedBox(height: 4), diff --git a/lib/views/home/direct/widgets/message_box.dart b/lib/views/home/direct/widgets/message_box.dart index c09d592..554e336 100644 --- a/lib/views/home/direct/widgets/message_box.dart +++ b/lib/views/home/direct/widgets/message_box.dart @@ -211,7 +211,10 @@ class _RecordChecking extends StatelessWidget { Expanded( child: Padding( padding: const EdgeInsets.symmetric(vertical: 8), - child: AudioWidget(audioFile: state.recordedFile!), + child: AudioWidget( + audioFile: state.recordedFile!, + id: 0, + ), ), ), DidvanIconButton( diff --git a/lib/views/home/studio/studio_details/studio_details.mobile.dart b/lib/views/home/studio/studio_details/studio_details.mobile.dart index 5c726fb..378644e 100644 --- a/lib/views/home/studio/studio_details/studio_details.mobile.dart +++ b/lib/views/home/studio/studio_details/studio_details.mobile.dart @@ -9,7 +9,6 @@ import 'package:didvan/views/home/studio/studio_details/widgets/studio_details_w import 'package:didvan/views/home/widgets/bookmark_button.dart'; import 'package:didvan/views/widgets/didvan/scaffold.dart'; import 'package:didvan/views/widgets/state_handlers/state_handler.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; diff --git a/lib/views/home/studio/studio_details/studio_details.web.dart b/lib/views/home/studio/studio_details/studio_details.web.dart index 65b1da5..b23740d 100644 --- a/lib/views/home/studio/studio_details/studio_details.web.dart +++ b/lib/views/home/studio/studio_details/studio_details.web.dart @@ -1,4 +1,3 @@ -import 'dart:io'; import 'dart:ui' as ui; import 'package:didvan/config/design_config.dart'; diff --git a/lib/views/home/studio/studio_details/studio_details_state.dart b/lib/views/home/studio/studio_details/studio_details_state.dart index 2f8d867..b127d42 100644 --- a/lib/views/home/studio/studio_details/studio_details_state.dart +++ b/lib/views/home/studio/studio_details/studio_details_state.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; @@ -8,6 +9,7 @@ import 'package:didvan/providers/core_provider.dart'; import 'package:didvan/services/media/media.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; +import 'package:didvan/services/storage/storage.dart'; class StudioDetailsState extends CoreProvier { late StudioDetailsData studio; @@ -17,6 +19,7 @@ class StudioDetailsState extends CoreProvier { late StudioRequestArgs args; final List relatedQueue = []; bool _positionListenerActivated = false; + final List downloadedFileIds = []; int _selectedDetailsIndex = 0; Timer? timer; @@ -42,6 +45,7 @@ class StudioDetailsState extends CoreProvier { if (MediaService.currentPodcast?.id == id && this.args.type == 'podcast') { return; } + _getDownloadsList(); _selectedDetailsIndex = 0; if (isForward != null) { if (isForward) { @@ -87,9 +91,14 @@ class StudioDetailsState extends CoreProvier { if (args.type == 'podcast') { MediaService.currentPodcast = studio; MediaService.podcastPlaylistArgs = args; + final downloaded = downloadedFileIds.contains(studio.id); await MediaService.handleAudioPlayback( - audioSource: studio.media, + audioSource: downloaded + ? StorageService.appDocsDir + '/podcasts/podcast-${studio.id}.mp3' + : studio.media, + id: studio.id, isVoiceMessage: false, + isNetworkAudio: !downloaded, ); if (nextStudio != null && !_positionListenerActivated) { _positionListenerActivated = true; @@ -104,6 +113,25 @@ class StudioDetailsState extends CoreProvier { } } + Future _getDownloadsList() async { + downloadedFileIds.clear(); + final dir = Directory( + StorageService.appDocsDir + ('/${args.type}s'), + ); + if (!await dir.exists()) { + await dir.create(); + } + dir.list(recursive: false).listen( + (event) { + downloadedFileIds.add( + int.parse( + event.path.split('/').last.split('-').last.split('.').first, + ), + ); + }, + ); + } + Future getRelatedContents() async { if (studio.relatedContents.isNotEmpty) return; relatedQueue.add(studio.id); diff --git a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart index 6edbac4..832c9d3 100644 --- a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart +++ b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart @@ -41,7 +41,7 @@ class StudioDetailsWidget extends StatelessWidget { isVideo: _isVideo, onCommentsTabSelected: onCommentsTabSelected ?? () {}, ), - if (state.selectedDetailsIndex != 1) const SizedBox(height: 16), + const SizedBox(height: 16), StateHandler( onRetry: () {}, state: state, @@ -93,7 +93,7 @@ class StudioDetailsWidget extends StatelessWidget { child: SizedBox( height: ds.height - ds.width * 9 / 16 - - 128 - + 144 - MediaQuery.of(context).padding.top, child: Comments( pageData: { diff --git a/lib/views/home/studio/studio_state.dart b/lib/views/home/studio/studio_state.dart index e6d9c66..e168e2d 100644 --- a/lib/views/home/studio/studio_state.dart +++ b/lib/views/home/studio/studio_state.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; @@ -8,10 +9,12 @@ 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'; +import 'package:didvan/services/storage/storage.dart'; class StudioState extends CoreProvier { final List studios = []; final List sliders = []; + final List downloadedFileIds = []; String search = ''; String lastSearch = ''; @@ -29,10 +32,31 @@ class StudioState extends CoreProvier { set videosSelected(bool value) { if (_videosSelected == value) return; _videosSelected = value; + selectedSortTypeIndex = 0; _getSliders(); + getDownloadsList(); getStudios(page: page); } + Future getDownloadsList() async { + downloadedFileIds.clear(); + final dir = Directory( + StorageService.appDocsDir + (videosSelected ? '/videos' : '/podcasts'), + ); + if (!await dir.exists()) { + await dir.create(); + } + dir.list(recursive: false).listen( + (event) { + downloadedFileIds.add( + int.parse( + event.path.split('/').last.split('-').last.split('.').first, + ), + ); + }, + ); + } + String get order { if (selectedSortTypeIndex == 0 || selectedSortTypeIndex == 1) return 'date'; if (selectedSortTypeIndex == 2) return 'view'; @@ -56,6 +80,7 @@ class StudioState extends CoreProvier { lastSearch = ''; _videosSelected = true; selectedSortTypeIndex = 0; + getDownloadsList(); Future.delayed(Duration.zero, () { _getSliders(); getStudios(page: 1); @@ -94,7 +119,6 @@ class StudioState extends CoreProvier { ), ), ); - await service.httpGet(); if (service.isSuccess) { if (page == 1) { @@ -122,8 +146,9 @@ class StudioState extends CoreProvier { notifyListeners(); } - void download(String url) { + Future download(String url, String fileName) async { final service = RequestService(url); - service.download(); + await service.download(fileName, videosSelected ? 'videos' : 'podcasts'); + notifyListeners(); } } diff --git a/lib/views/home/widgets/audio/audio_player_widget.dart b/lib/views/home/widgets/audio/audio_player_widget.dart index edc9675..10bbddc 100644 --- a/lib/views/home/widgets/audio/audio_player_widget.dart +++ b/lib/views/home/widgets/audio/audio_player_widget.dart @@ -57,7 +57,7 @@ class AudioPlayerWidget extends StatelessWidget { Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: AudioSlider( - tag: podcast.media, + tag: 'podcast-${podcast.id}', showTimer: true, duration: podcast.duration, ), @@ -117,6 +117,7 @@ class AudioPlayerWidget extends StatelessWidget { builder: (context, snapshot) { return _PlayPouseAnimatedIcon( audioSource: podcast.media, + id: podcast.id, ); }, ), @@ -239,7 +240,9 @@ class AudioPlayerWidget extends StatelessWidget { class _PlayPouseAnimatedIcon extends StatefulWidget { final String audioSource; - const _PlayPouseAnimatedIcon({Key? key, required this.audioSource}) + final int id; + const _PlayPouseAnimatedIcon( + {Key? key, required this.audioSource, required this.id}) : super(key: key); @override @@ -281,6 +284,11 @@ class __PlayPouseAnimatedIconState extends State<_PlayPouseAnimatedIcon> MediaService.handleAudioPlayback( audioSource: widget.audioSource, isVoiceMessage: false, + id: widget.id, + isNetworkAudio: !context + .read() + .downloadedFileIds + .contains(widget.id), ); _handleAnimation(); }, diff --git a/lib/views/home/widgets/audio/audio_slider.dart b/lib/views/home/widgets/audio/audio_slider.dart index 093bcd2..a65e126 100644 --- a/lib/views/home/widgets/audio/audio_slider.dart +++ b/lib/views/home/widgets/audio/audio_slider.dart @@ -22,7 +22,7 @@ class AudioSlider extends StatelessWidget { @override Widget build(BuildContext context) { return IgnorePointer( - ignoring: MediaService.audioPlayerTag != tag, + ignoring: !_isPlaying, child: Directionality( textDirection: TextDirection.ltr, child: StreamBuilder( diff --git a/lib/views/home/widgets/overview/podcast.dart b/lib/views/home/widgets/overview/podcast.dart index cea07fd..b42eccf 100644 --- a/lib/views/home/widgets/overview/podcast.dart +++ b/lib/views/home/widgets/overview/podcast.dart @@ -1,15 +1,19 @@ import 'package:didvan/config/theme_data.dart'; +import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/utils/date_time.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; +import 'package:didvan/views/home/studio/studio_state.dart'; import 'package:didvan/views/home/widgets/bookmark_button.dart'; import 'package:didvan/views/home/widgets/duration_widget.dart'; import 'package:didvan/views/widgets/didvan/card.dart'; import 'package:didvan/views/widgets/didvan/divider.dart'; +import 'package:didvan/views/widgets/didvan/icon_button.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/shimmer_placeholder.dart'; import 'package:didvan/views/widgets/skeleton_image.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -28,6 +32,7 @@ class PodcastOverview extends StatelessWidget { @override Widget build(BuildContext context) { + final state = context.read(); return DidvanCard( onTap: () { context @@ -75,13 +80,22 @@ class PodcastOverview extends StatelessWidget { children: [ DurationWidget(duration: podcast.duration!), const Spacer(), - // DidvanIconButton( - // gestureSize: 28, - // icon: DidvanIcons.download_regular, - // onPressed: () => - // context.read().download(podcast.media!), - // ), - // const SizedBox(width: 16), + if (!kIsWeb) ...[ + DidvanIconButton( + gestureSize: 28, + color: _isDownloaded(state) + ? Theme.of(context).colorScheme.primary + : null, + icon: _isDownloaded(state) + ? DidvanIcons.download_solid + : DidvanIcons.download_regular, + onPressed: _isDownloaded(state) + ? () {} + : () => + state.download(podcast.media!, podcast.id.toString()), + ), + const SizedBox(width: 16), + ], BookmarkButton( askForConfirmation: hasUnmarkConfirmation, gestureSize: 32, @@ -95,6 +109,10 @@ class PodcastOverview extends StatelessWidget { ); } + bool _isDownloaded(state) { + return state.downloadedFileIds.contains(podcast.id); + } + static Widget get placeholder => DidvanCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/views/home/widgets/player_controller_button.dart b/lib/views/home/widgets/player_controller_button.dart deleted file mode 100644 index 7e2cc14..0000000 --- a/lib/views/home/widgets/player_controller_button.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'dart:io'; - -import 'package:didvan/config/theme_data.dart'; -import 'package:didvan/constants/app_icons.dart'; -import 'package:didvan/services/media/media.dart'; -import 'package:didvan/views/widgets/didvan/icon_button.dart'; -import 'package:flutter/material.dart'; - -class AudioControllerButton extends StatelessWidget { - final String? audioUrl; - final File? audioFile; - - const AudioControllerButton({Key? key, this.audioUrl, this.audioFile}) - : super(key: key); - - bool get _nowPlaying => - MediaService.audioPlayerTag == audioUrl || - audioFile != null && MediaService.audioPlayerTag == audioFile!.path; - - @override - Widget build(BuildContext context) { - return DidvanIconButton( - icon: MediaService.audioPlayer.playing == true && _nowPlaying - ? DidvanIcons.pause_circle_solid - : DidvanIcons.play_circle_solid, - color: Theme.of(context).colorScheme.focusedBorder, - onPressed: () { - MediaService.handleAudioPlayback( - audioSource: audioFile ?? audioUrl, - ); - }, - ); - } -} diff --git a/lib/views/widgets/didvan/bnb.dart b/lib/views/widgets/didvan/bnb.dart index 78af00d..84eb6eb 100644 --- a/lib/views/widgets/didvan/bnb.dart +++ b/lib/views/widgets/didvan/bnb.dart @@ -24,10 +24,11 @@ class DidvanBNB extends StatelessWidget { : super(key: key); bool get _enablePlayerController => - MediaService.currentPodcast != null || MediaService.audioPlayer.playing; + MediaService.currentPodcast != null && MediaService.audioPlayer.playing; @override Widget build(BuildContext context) { + final state = context.read(); return StreamBuilder( stream: MediaService.audioPlayer.playingStream, builder: (context, snapshot) { @@ -108,7 +109,8 @@ class DidvanBNB extends StatelessWidget { : DidvanIcons.play_solid, onPressed: () { MediaService.handleAudioPlayback( - audioSource: MediaService.audioPlayerTag, + audioSource: state.studio.media, + id: state.studio.id, ); }, ), diff --git a/lib/views/widgets/didvan/scaffold.dart b/lib/views/widgets/didvan/scaffold.dart index 8e31440..38f9dde 100644 --- a/lib/views/widgets/didvan/scaffold.dart +++ b/lib/views/widgets/didvan/scaffold.dart @@ -9,6 +9,7 @@ class DidvanScaffold extends StatefulWidget { final EdgeInsets padding; final Color? backgroundColor; final bool reverse; + final ScrollPhysics? physics; final ScrollController? scrollController; final bool showSliversFirst; @@ -17,6 +18,7 @@ class DidvanScaffold extends StatefulWidget { this.slivers, required this.appBarData, this.children, + this.physics, this.padding = const EdgeInsets.symmetric(horizontal: 16), this.backgroundColor, this.reverse = false, @@ -49,6 +51,7 @@ class _DidvanScaffoldState extends State { child: Stack( children: [ CustomScrollView( + physics: widget.physics, controller: _scrollController, reverse: widget.reverse, slivers: [ From a128684f271ced4bc264c640b2cfc6bb306995d0 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Tue, 29 Mar 2022 16:28:47 +0430 Subject: [PATCH 16/54] bug fixes --- .../home/settings/direct_list/direct_list.dart | 7 +++++-- .../direct_list/widgets/direct_item.dart | 5 +++++ .../studio_details/studio_details_state.dart | 3 +++ lib/views/widgets/didvan/app_bar.dart | 3 +-- lib/views/widgets/didvan/bnb.dart | 17 +++++++++++++---- 5 files changed, 27 insertions(+), 8 deletions(-) diff --git a/lib/views/home/settings/direct_list/direct_list.dart b/lib/views/home/settings/direct_list/direct_list.dart index 7c8d264..f0f0966 100644 --- a/lib/views/home/settings/direct_list/direct_list.dart +++ b/lib/views/home/settings/direct_list/direct_list.dart @@ -36,8 +36,11 @@ class _DirectListState extends State { title: 'پیام‌ها', trailing: state.unreadCount == 0 ? null - : DidvanBadge( - text: state.unreadCount.toString(), + : Padding( + padding: const EdgeInsets.only(left: 20), + child: DidvanBadge( + text: state.unreadCount.toString(), + ), ), ), slivers: [ diff --git a/lib/views/home/settings/direct_list/widgets/direct_item.dart b/lib/views/home/settings/direct_list/widgets/direct_item.dart index 6aa9a45..da51414 100644 --- a/lib/views/home/settings/direct_list/widgets/direct_item.dart +++ b/lib/views/home/settings/direct_list/widgets/direct_item.dart @@ -3,10 +3,12 @@ import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/chat_room/chat_room.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/utils/date_time.dart'; +import 'package:didvan/views/home/settings/direct_list/direct_list_state.dart'; import 'package:didvan/views/widgets/didvan/badge.dart'; import 'package:didvan/views/widgets/didvan/divider.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; class ChatRoomItem extends StatelessWidget { final ChatRoom chatRoom; @@ -20,7 +22,10 @@ class ChatRoomItem extends StatelessWidget { Routes.direct, arguments: {'type': chatRoom.type}, ); + final state = context.read(); + int unreadCount = chatRoom.unread; chatRoom.unread = 0; + state.unreadCount -= unreadCount; }, child: Container( color: Colors.transparent, diff --git a/lib/views/home/studio/studio_details/studio_details_state.dart b/lib/views/home/studio/studio_details/studio_details_state.dart index b127d42..fa59702 100644 --- a/lib/views/home/studio/studio_details/studio_details_state.dart +++ b/lib/views/home/studio/studio_details/studio_details_state.dart @@ -103,6 +103,9 @@ class StudioDetailsState extends CoreProvier { if (nextStudio != null && !_positionListenerActivated) { _positionListenerActivated = true; MediaService.audioPlayer.positionStream.listen((event) { + if (MediaService.audioPlayerTag?.contains('message') == true) { + return; + } final duration = MediaService.audioPlayer.duration ?? Duration(seconds: studio.duration); if (event.compareTo(duration) > 0 && nextStudio != null) { diff --git a/lib/views/widgets/didvan/app_bar.dart b/lib/views/widgets/didvan/app_bar.dart index 1f1e6ed..81770c8 100644 --- a/lib/views/widgets/didvan/app_bar.dart +++ b/lib/views/widgets/didvan/app_bar.dart @@ -20,8 +20,7 @@ class DidvanAppBar extends StatelessWidget { return Container( height: appBarData.isSmall ? 56 : 72, width: MediaQuery.of(context).size.width, - padding: - EdgeInsets.only(right: 4, left: appBarData.trailing == null ? 20 : 0), + padding: const EdgeInsets.only(right: 4), decoration: BoxDecoration( border: hasBorder ? Border( diff --git a/lib/views/widgets/didvan/bnb.dart b/lib/views/widgets/didvan/bnb.dart index 84eb6eb..9982251 100644 --- a/lib/views/widgets/didvan/bnb.dart +++ b/lib/views/widgets/didvan/bnb.dart @@ -3,6 +3,7 @@ import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/enums.dart'; import 'package:didvan/services/media/media.dart'; +import 'package:didvan/services/storage/storage.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; import 'package:didvan/views/home/studio/studio_details/widgets/studio_details_widget.dart'; import 'package:didvan/views/home/studio/studio_state.dart'; @@ -24,7 +25,8 @@ class DidvanBNB extends StatelessWidget { : super(key: key); bool get _enablePlayerController => - MediaService.currentPodcast != null && MediaService.audioPlayer.playing; + MediaService.currentPodcast != null && + (MediaService.audioPlayerTag?.contains('podcast') ?? false); @override Widget build(BuildContext context) { @@ -39,7 +41,7 @@ class DidvanBNB extends StatelessWidget { child: AnimatedContainer( padding: const EdgeInsets.only(top: 12), duration: DesignConfig.lowAnimationDuration, - height: _enablePlayerController ? 120 : 72, + height: _enablePlayerController ? 128 : 72, decoration: BoxDecoration( color: DesignConfig.isDark ? Theme.of(context).colorScheme.focused @@ -51,7 +53,7 @@ class DidvanBNB extends StatelessWidget { child: !_enablePlayerController ? const SizedBox() : SizedBox( - height: 48, + height: 56, child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -109,8 +111,15 @@ class DidvanBNB extends StatelessWidget { : DidvanIcons.play_solid, onPressed: () { MediaService.handleAudioPlayback( - audioSource: state.studio.media, + audioSource: state.downloadedFileIds + .contains(state.studio.id) + ? StorageService.appDocsDir + + '/podcasts/podcast-${state.studio.id}.mp3' + : state.studio.media, + isNetworkAudio: !state.downloadedFileIds + .contains(state.studio.id), id: state.studio.id, + isVoiceMessage: false, ); }, ), From 873c1f669218753abebcdf6e9961ef7f39701922 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Wed, 30 Mar 2022 03:37:38 +0430 Subject: [PATCH 17/54] bug fixes --- .../studio_details/studio_details.mobile.dart | 5 - .../studio_details/studio_details.web.dart | 5 - .../studio_details/studio_details_state.dart | 13 + lib/views/home/studio/studio_state.dart | 8 + lib/views/home/studio/widgets/slider.dart | 1 + .../widgets/audio/audio_player_widget.dart | 259 ++++++++++------- lib/views/home/widgets/overview/podcast.dart | 38 ++- lib/views/widgets/didvan/bnb.dart | 267 +++++++++++------- 8 files changed, 377 insertions(+), 219 deletions(-) diff --git a/lib/views/home/studio/studio_details/studio_details.mobile.dart b/lib/views/home/studio/studio_details/studio_details.mobile.dart index 378644e..42f0aa7 100644 --- a/lib/views/home/studio/studio_details/studio_details.mobile.dart +++ b/lib/views/home/studio/studio_details/studio_details.mobile.dart @@ -87,11 +87,6 @@ class _StudioDetailsState extends State { state: state, onRetry: () => state.getStudioDetails(state.studio.id), builder: (context, state) { - if (state.prevStudio == null && - state.nextStudio == null && - state.args.page != 0) { - return const SizedBox(); - } return WillPopScope( onWillPop: () async { if (_isFullScreen) { diff --git a/lib/views/home/studio/studio_details/studio_details.web.dart b/lib/views/home/studio/studio_details/studio_details.web.dart index b23740d..f936205 100644 --- a/lib/views/home/studio/studio_details/studio_details.web.dart +++ b/lib/views/home/studio/studio_details/studio_details.web.dart @@ -75,11 +75,6 @@ class _StudioDetailsState extends State { state: state, onRetry: () => state.getStudioDetails(state.studio.id), builder: (context, state) { - if (state.prevStudio == null && - state.nextStudio == null && - state.args.page != 0) { - return const SizedBox(); - } // ignore: undefined_prefixed_name ui.platformViewRegistry.registerViewFactory( "video", diff --git a/lib/views/home/studio/studio_details/studio_details_state.dart b/lib/views/home/studio/studio_details/studio_details_state.dart index fa59702..5151deb 100644 --- a/lib/views/home/studio/studio_details/studio_details_state.dart +++ b/lib/views/home/studio/studio_details/studio_details_state.dart @@ -24,6 +24,7 @@ class StudioDetailsState extends CoreProvier { int _selectedDetailsIndex = 0; Timer? timer; int timerValue = 10; + bool stopOnPodcastEnds = false; int get selectedDetailsIndex => _selectedDetailsIndex; set selectedDetailsIndex(int value) { @@ -45,6 +46,7 @@ class StudioDetailsState extends CoreProvier { if (MediaService.currentPodcast?.id == id && this.args.type == 'podcast') { return; } + _getDownloadsList(); _selectedDetailsIndex = 0; if (isForward != null) { @@ -61,12 +63,19 @@ class StudioDetailsState extends CoreProvier { _handlePodcastPlayback(studio); } if (isForward == null) { + if (this.args.type == 'podcast') { + MediaService.audioPlayerTag = 'podcast'; + } appState = AppState.busy; } final service = RequestService(RequestHelper.studioDetails(id, this.args)); await service.httpGet(); nextStudio = null; prevStudio = null; + if (stopOnPodcastEnds) { + timerValue = 10; + } + stopOnPodcastEnds = false; if (service.isSuccess) { final result = service.result; studio = StudioDetailsData.fromJson(result['studio']); @@ -109,6 +118,10 @@ class StudioDetailsState extends CoreProvier { final duration = MediaService.audioPlayer.duration ?? Duration(seconds: studio.duration); if (event.compareTo(duration) > 0 && nextStudio != null) { + if (stopOnPodcastEnds) { + MediaService.resetAudioPlayer(); + return; + } getStudioDetails(nextStudio!.id, isForward: true); } }); diff --git a/lib/views/home/studio/studio_state.dart b/lib/views/home/studio/studio_state.dart index e168e2d..8d1e40a 100644 --- a/lib/views/home/studio/studio_state.dart +++ b/lib/views/home/studio/studio_state.dart @@ -15,6 +15,9 @@ class StudioState extends CoreProvier { final List studios = []; final List sliders = []; final List downloadedFileIds = []; + final List downloadQueue = []; + + AppState downloadState = AppState.idle; String search = ''; String lastSearch = ''; @@ -147,8 +150,13 @@ class StudioState extends CoreProvier { } Future download(String url, String fileName) async { + downloadState = AppState.busy; + downloadQueue.add(url); + notifyListeners(); final service = RequestService(url); await service.download(fileName, videosSelected ? 'videos' : 'podcasts'); + downloadState = AppState.idle; + downloadQueue.remove(url); notifyListeners(); } } diff --git a/lib/views/home/studio/widgets/slider.dart b/lib/views/home/studio/widgets/slider.dart index c072168..7b6339f 100644 --- a/lib/views/home/studio/widgets/slider.dart +++ b/lib/views/home/studio/widgets/slider.dart @@ -101,6 +101,7 @@ class _StudioSliderState extends State { ), ], options: CarouselOptions( + autoPlayAnimationDuration: DesignConfig.mediumAnimationDuration, onPageChanged: (index, reason) => setState( () => selectedIndex = index, ), diff --git a/lib/views/home/widgets/audio/audio_player_widget.dart b/lib/views/home/widgets/audio/audio_player_widget.dart index 10bbddc..300139d 100644 --- a/lib/views/home/widgets/audio/audio_player_widget.dart +++ b/lib/views/home/widgets/audio/audio_player_widget.dart @@ -4,6 +4,7 @@ import 'dart:math'; import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; +import 'package:didvan/models/enums.dart'; import 'package:didvan/models/studio_details_data.dart'; import 'package:didvan/models/view/action_sheet_data.dart'; import 'package:didvan/services/media/media.dart'; @@ -12,6 +13,7 @@ import 'package:didvan/views/home/studio/studio_details/studio_details_state.dar import 'package:didvan/views/home/studio/studio_state.dart'; import 'package:didvan/views/home/widgets/audio/audio_slider.dart'; import 'package:didvan/views/home/widgets/bookmark_button.dart'; +import 'package:didvan/views/widgets/didvan/button.dart'; import 'package:didvan/views/widgets/didvan/icon_button.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/ink_wrapper.dart'; @@ -64,95 +66,117 @@ class AudioPlayerWidget extends StatelessWidget { ), Row( crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const SizedBox(), - StatefulBuilder( - builder: (context, setState) => Column( - children: [ - DidvanIconButton( - icon: state.timer == null - ? DidvanIcons.sleep_timer_regular - : DidvanIcons.sleep_enabled_regular, - color: Theme.of(context).colorScheme.title, - onPressed: () => _showSleepTimer( - state, - () => setState(() {}), - ), - ), - if (state.timer != null) - DidvanText( - state.timerValue.toString() + '\'', - isEnglishFont: true, - style: Theme.of(context).textTheme.overline, - color: Theme.of(context).colorScheme.caption, - ), - ], - ), - ), - Column( - children: [ - DidvanIconButton( - color: Theme.of(context).colorScheme.title, - size: 32, - icon: DidvanIcons.media_forward_solid, - onPressed: () { - MediaService.audioPlayer.seek( - Duration( - seconds: - MediaService.audioPlayer.position.inSeconds + 30, - ), - ); - }, - ), - DidvanText( - '30', - isEnglishFont: true, - color: Theme.of(context).colorScheme.title, - ), - ], - ), - StreamBuilder( - stream: MediaService.audioPlayer.playingStream, - builder: (context, snapshot) { - return _PlayPouseAnimatedIcon( - audioSource: podcast.media, - id: podcast.id, - ); - }, - ), - Column( - children: [ - DidvanIconButton( - size: 32, - icon: DidvanIcons.media_backward_solid, - color: Theme.of(context).colorScheme.title, - onPressed: () { - MediaService.audioPlayer.seek( - Duration( - seconds: max( - 0, - MediaService.audioPlayer.position.inSeconds - 10, + Expanded( + child: Center( + child: StatefulBuilder( + builder: (context, setState) => Column( + children: [ + DidvanIconButton( + icon: state.timer == null && !state.stopOnPodcastEnds + ? DidvanIcons.sleep_timer_regular + : DidvanIcons.sleep_enabled_regular, + color: Theme.of(context).colorScheme.title, + onPressed: () => _showSleepTimer( + state, + () => setState(() {}), ), ), + if (state.timer != null) + DidvanText( + state.stopOnPodcastEnds + ? 'پایان پادکست' + : '\'' + state.timerValue.toString(), + isEnglishFont: true, + style: Theme.of(context).textTheme.overline, + color: Theme.of(context).colorScheme.title, + ), + ], + ), + ), + ), + ), + Expanded( + child: Center( + child: Column( + children: [ + DidvanIconButton( + color: Theme.of(context).colorScheme.title, + size: 32, + icon: DidvanIcons.media_forward_solid, + onPressed: () { + MediaService.audioPlayer.seek( + Duration( + seconds: + MediaService.audioPlayer.position.inSeconds + + 30, + ), + ); + }, + ), + DidvanText( + '30', + isEnglishFont: true, + color: Theme.of(context).colorScheme.title, + ), + ], + ), + ), + ), + Expanded( + child: Center( + child: StreamBuilder( + stream: MediaService.audioPlayer.playingStream, + builder: (context, snapshot) { + return _PlayPouseAnimatedIcon( + audioSource: podcast.media, + id: podcast.id, ); }, ), - DidvanText( - '10', - isEnglishFont: true, - color: Theme.of(context).colorScheme.title, + ), + ), + Expanded( + child: Center( + child: Column( + children: [ + DidvanIconButton( + size: 32, + icon: DidvanIcons.media_backward_solid, + color: Theme.of(context).colorScheme.title, + onPressed: () { + MediaService.audioPlayer.seek( + Duration( + seconds: max( + 0, + MediaService.audioPlayer.position.inSeconds - + 10, + ), + ), + ); + }, + ), + DidvanText( + '10', + isEnglishFont: true, + color: Theme.of(context).colorScheme.title, + ), + ], ), - ], + ), ), - BookmarkButton( - gestureSize: 48, - color: Theme.of(context).colorScheme.title, - value: podcast.marked, - onMarkChanged: (value) => - context.read().changeMark(podcast.id, value), + Expanded( + child: Center( + child: BookmarkButton( + gestureSize: 48, + color: Theme.of(context).colorScheme.title, + value: podcast.marked, + onMarkChanged: (value) => context + .read() + .changeMark(podcast.id, value), + ), + ), ), - const SizedBox(), ], ), ], @@ -163,13 +187,17 @@ class AudioPlayerWidget extends StatelessWidget { Future _showSleepTimer(StudioDetailsState state, update) async { int timerValue = 10; final controller = FixedExtentScrollController(); + bool isInit = true; Future.delayed( const Duration(milliseconds: 100), - () => controller.animateTo( - 50 * (state.timerValue / 5 - 2), - duration: DesignConfig.lowAnimationDuration, - curve: Curves.easeIn, - ), + () async { + await controller.animateTo( + state.timerValue * 10, + duration: DesignConfig.lowAnimationDuration, + curve: Curves.easeIn, + ); + isInit = false; + }, ); await ActionSheetUtils.showBottomSheet( data: ActionSheetData( @@ -193,38 +221,71 @@ class AudioPlayerWidget extends StatelessWidget { child: RotatedBox( quarterTurns: 3, child: ListWheelScrollView( - controller: controller, physics: const FixedExtentScrollPhysics(), - itemExtent: 48, + controller: controller, + itemExtent: 10, onSelectedItemChanged: (index) { - final minutes = (index + 2) * 5; + if (!isInit) { + state.stopOnPodcastEnds = false; + } + final minutes = index == 0 ? 1 : index; timerValue = minutes; setState(() {}); }, children: [ - for (var i = 0; i < 9; i++) - Center( - child: Container( - color: Theme.of(context).colorScheme.text, - width: 50, - height: 3, + for (var i = 0; i < 61; i++) ...[ + if (i % 5 == 0) + Center( + child: Container( + color: Theme.of(context).colorScheme.text, + width: 50, + height: 3, + ), ), - ), + if (i % 5 != 0) const SizedBox(height: 3), + ], ], ), ), ), + const SizedBox(height: 32), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 64), + child: DidvanButton( + style: state.timerValue == + MediaService.audioPlayer.duration?.inMinutes && + state.stopOnPodcastEnds + ? ButtonStyleMode.primary + : ButtonStyleMode.flat, + title: 'پایان پادکست', + onPressed: () async { + state.timerValue = + MediaService.audioPlayer.duration!.inMinutes - + MediaService.audioPlayer.position.inMinutes; + await controller.animateTo( + state.timerValue * 10, + duration: DesignConfig.lowAnimationDuration, + curve: Curves.easeIn, + ); + state.stopOnPodcastEnds = true; + setState(() {}); + }, + ), + ), ], ), ), onConfirmed: () { - state.timer = Timer( - Duration(minutes: timerValue), - MediaService.audioPlayer.stop, - ); + if (!state.stopOnPodcastEnds) { + state.timer = Timer( + Duration(minutes: timerValue), + MediaService.audioPlayer.stop, + ); + } state.timerValue = timerValue; update(); }, + confrimTitle: 'شروع زمان خواب', dismissTitle: 'لغو', onDismissed: () { state.timer?.cancel(); diff --git a/lib/views/home/widgets/overview/podcast.dart b/lib/views/home/widgets/overview/podcast.dart index b42eccf..f4cef1b 100644 --- a/lib/views/home/widgets/overview/podcast.dart +++ b/lib/views/home/widgets/overview/podcast.dart @@ -1,5 +1,6 @@ import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; +import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/utils/date_time.dart'; @@ -81,19 +82,30 @@ class PodcastOverview extends StatelessWidget { DurationWidget(duration: podcast.duration!), const Spacer(), if (!kIsWeb) ...[ - DidvanIconButton( - gestureSize: 28, - color: _isDownloaded(state) - ? Theme.of(context).colorScheme.primary - : null, - icon: _isDownloaded(state) - ? DidvanIcons.download_solid - : DidvanIcons.download_regular, - onPressed: _isDownloaded(state) - ? () {} - : () => - state.download(podcast.media!, podcast.id.toString()), - ), + if (state.downloadState == AppState.idle || + !state.downloadQueue.contains(podcast.media)) + DidvanIconButton( + gestureSize: 28, + color: _isDownloaded(state) + ? Theme.of(context).colorScheme.primary + : null, + icon: _isDownloaded(state) + ? DidvanIcons.download_solid + : DidvanIcons.download_regular, + onPressed: _isDownloaded(state) + ? () {} + : () => state.download( + podcast.media!, podcast.id.toString()), + ), + if (state.downloadState == AppState.busy && + state.downloadQueue.contains(podcast.media)) + const SizedBox( + width: 18, + height: 18, + child: CircularProgressIndicator( + strokeWidth: 2, + ), + ), const SizedBox(width: 16), ], BookmarkButton( diff --git a/lib/views/widgets/didvan/bnb.dart b/lib/views/widgets/didvan/bnb.dart index 9982251..3c4b804 100644 --- a/lib/views/widgets/didvan/bnb.dart +++ b/lib/views/widgets/didvan/bnb.dart @@ -14,6 +14,8 @@ import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:expandable_bottom_sheet/expandable_bottom_sheet.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:just_audio/just_audio.dart'; import 'package:provider/provider.dart'; class DidvanBNB extends StatelessWidget { @@ -24,109 +26,169 @@ class DidvanBNB extends StatelessWidget { {Key? key, required this.currentTabIndex, required this.onTabChanged}) : super(key: key); - bool get _enablePlayerController => - MediaService.currentPodcast != null && + bool _enablePlayerController(StudioDetailsState state) => + MediaService.currentPodcast != null || (MediaService.audioPlayerTag?.contains('podcast') ?? false); @override Widget build(BuildContext context) { - final state = context.read(); return StreamBuilder( stream: MediaService.audioPlayer.playingStream, builder: (context, snapshot) { return Stack( children: [ GestureDetector( - onTap: () => _showPlayerBottomSheet(context), - child: AnimatedContainer( - padding: const EdgeInsets.only(top: 12), - duration: DesignConfig.lowAnimationDuration, - height: _enablePlayerController ? 128 : 72, - decoration: BoxDecoration( - color: DesignConfig.isDark - ? Theme.of(context).colorScheme.focused - : Theme.of(context).colorScheme.navigation, - borderRadius: const BorderRadius.vertical( - top: Radius.circular(16), + onTap: () => MediaService.currentPodcast == null + ? null + : _showPlayerBottomSheet(context), + child: Consumer( + builder: (context, state, child) => AnimatedContainer( + padding: const EdgeInsets.only(top: 12), + duration: DesignConfig.lowAnimationDuration, + height: _enablePlayerController(state) ? 128 : 72, + decoration: BoxDecoration( + color: DesignConfig.isDark + ? Theme.of(context).colorScheme.focused + : Theme.of(context).colorScheme.navigation, + borderRadius: const BorderRadius.vertical( + top: Radius.circular(16), + ), ), - ), - child: !_enablePlayerController - ? const SizedBox() - : SizedBox( - height: 56, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only( - right: 12, - left: 16, - ), - child: DidvanIconButton( - icon: DidvanIcons.close_regular, - color: DesignConfig.isDark - ? null - : Theme.of(context).colorScheme.secondCTA, - gestureSize: 28, - onPressed: MediaService.resetAudioPlayer, - ), - ), - SkeletonImage( - imageUrl: MediaService.currentPodcast!.image, - width: 32, + alignment: Alignment.topCenter, + child: !_enablePlayerController(state) + ? const SizedBox() + : MediaService.currentPodcast == null + ? SizedBox( height: 32, - ), - const SizedBox(width: 16), - Expanded( - child: Column( + child: Center( + child: SpinKitThreeBounce( + size: 18, + color: DesignConfig.isDark + ? null + : Theme.of(context) + .colorScheme + .secondCTA, + ), + ), + ) + : SizedBox( + height: 56, + child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - DidvanText( - MediaService.currentPodcast!.title, - color: DesignConfig.isDark - ? null - : Theme.of(context) - .colorScheme - .secondCTA, + Padding( + padding: const EdgeInsets.only( + right: 12, + left: 16, + ), + child: DidvanIconButton( + icon: DidvanIcons.close_regular, + color: DesignConfig.isDark + ? null + : Theme.of(context) + .colorScheme + .secondCTA, + gestureSize: 28, + onPressed: + MediaService.resetAudioPlayer, + ), ), - AudioSlider( - disableThumb: true, - tag: MediaService.audioPlayerTag!, + SkeletonImage( + imageUrl: + MediaService.currentPodcast!.image, + width: 32, + height: 32, ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + DidvanText( + MediaService.currentPodcast!.title, + color: DesignConfig.isDark + ? null + : Theme.of(context) + .colorScheme + .secondCTA, + ), + AudioSlider( + disableThumb: true, + tag: MediaService.audioPlayerTag!, + ), + ], + ), + ), + StreamBuilder( + stream: MediaService + .audioPlayer.playerStateStream, + builder: (context, snapshot) { + if (state.appState == AppState.busy || + MediaService.audioPlayer.playerState + .processingState == + ProcessingState.loading) { + return Padding( + padding: const EdgeInsets.only( + top: 4, + left: 16, + right: 16, + ), + child: SizedBox( + height: 18, + width: 18, + child: CircularProgressIndicator( + strokeWidth: 2, + color: Theme.of(context) + .colorScheme + .secondCTA, + ), + ), + ); + } + return const SizedBox(); + }, + ), + if (state.appState != AppState.busy && + MediaService.audioPlayer.playerState + .processingState != + ProcessingState.loading) + Padding( + padding: const EdgeInsets.only( + left: 12, + right: 16, + ), + child: DidvanIconButton( + gestureSize: 28, + color: DesignConfig.isDark + ? null + : Theme.of(context) + .colorScheme + .secondCTA, + icon: snapshot.data! + ? DidvanIcons.pause_solid + : DidvanIcons.play_solid, + onPressed: () { + MediaService.handleAudioPlayback( + audioSource: state + .downloadedFileIds + .contains(state.studio.id) + ? StorageService.appDocsDir + + '/podcasts/podcast-${state.studio.id}.mp3' + : state.studio.media, + isNetworkAudio: !state + .downloadedFileIds + .contains(state.studio.id), + id: state.studio.id, + isVoiceMessage: false, + ); + }, + ), + ), ], ), ), - Padding( - padding: const EdgeInsets.only( - left: 12, - right: 16, - ), - child: DidvanIconButton( - gestureSize: 28, - color: DesignConfig.isDark - ? null - : Theme.of(context).colorScheme.secondCTA, - icon: snapshot.data! - ? DidvanIcons.pause_solid - : DidvanIcons.play_solid, - onPressed: () { - MediaService.handleAudioPlayback( - audioSource: state.downloadedFileIds - .contains(state.studio.id) - ? StorageService.appDocsDir + - '/podcasts/podcast-${state.studio.id}.mp3' - : state.studio.media, - isNetworkAudio: !state.downloadedFileIds - .contains(state.studio.id), - id: state.studio.id, - isVoiceMessage: false, - ); - }, - ), - ), - ], - ), - ), + ), ), ), Positioned( @@ -226,18 +288,23 @@ class DidvanBNB extends StatelessWidget { color: Theme.of(context).colorScheme.surface, child: Column( children: [ - DidvanIconButton( - size: 32, - icon: DidvanIcons.angle_down_regular, - onPressed: () { - if (!isExpanded) { - sheetKey.currentState?.expand(); - isExpanded = true; - return; - } - isExpanded = false; - sheetKey.currentState?.contract(); - }, + StatefulBuilder( + builder: (context, setState) => DidvanIconButton( + size: 32, + icon: isExpanded + ? DidvanIcons.angle_down_regular + : DidvanIcons.angle_up_regular, + onPressed: () { + if (!isExpanded) { + sheetKey.currentState?.expand(); + isExpanded = true; + } else { + isExpanded = false; + sheetKey.currentState?.contract(); + } + setState(() {}); + }, + ), ), const SizedBox(height: 16), ], @@ -247,7 +314,13 @@ class DidvanBNB extends StatelessWidget { ), ), expandableContent: state.appState == AppState.busy - ? const SizedBox() + ? Container( + height: MediaQuery.of(context).size.height / 2, + alignment: Alignment.center, + child: SpinKitSpinningLines( + color: Theme.of(context).colorScheme.primary, + ), + ) : StudioDetailsWidget( studio: detailsState.studio, onCommentsTabSelected: () { From 4df58d093a93321f83bad407c6e5e16ac021b30f Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Wed, 30 Mar 2022 15:36:31 +0430 Subject: [PATCH 18/54] bug fixes --- lib/main.dart | 4 + lib/providers/media.dart | 60 +++ lib/services/media/media.dart | 6 + lib/services/network/request.dart | 2 +- lib/services/storage/storage.dart | 2 +- .../studio_details/studio_details.mobile.dart | 5 +- .../studio_details/studio_details.web.dart | 5 +- .../studio_details/studio_details_state.dart | 40 +- .../widgets/studio_details_widget.dart | 208 +++++---- lib/views/home/studio/studio_state.dart | 38 -- lib/views/home/studio/widgets/tab_bar.dart | 5 +- .../widgets/audio/audio_player_widget.dart | 5 +- lib/views/home/widgets/overview/podcast.dart | 89 ++-- lib/views/home/widgets/overview/radar.dart | 16 +- lib/views/splash/splash.dart | 2 + lib/views/widgets/didvan/bnb.dart | 441 +++++++++--------- 16 files changed, 481 insertions(+), 447 deletions(-) create mode 100644 lib/providers/media.dart diff --git a/lib/main.dart b/lib/main.dart index d663c2d..d16f740 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,6 @@ import 'package:bot_toast/bot_toast.dart'; import 'package:didvan/config/theme_data.dart'; +import 'package:didvan/providers/media.dart'; import 'package:didvan/providers/theme_provider.dart'; import 'package:didvan/providers/user_provider.dart'; import 'package:didvan/routes/route_generator.dart'; @@ -21,6 +22,9 @@ class Didvan extends StatelessWidget { Widget build(BuildContext context) { return MultiProvider( providers: [ + ChangeNotifierProvider( + create: (context) => MediaProvider(), + ), ChangeNotifierProvider( create: (context) => UserProvider(), ), diff --git a/lib/providers/media.dart b/lib/providers/media.dart new file mode 100644 index 0000000..0be72e0 --- /dev/null +++ b/lib/providers/media.dart @@ -0,0 +1,60 @@ +import 'dart:io'; + +import 'package:didvan/models/enums.dart'; +import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/services/network/request.dart'; +import 'package:didvan/services/storage/storage.dart'; + +class MediaProvider extends CoreProvier { + static final List downloadedItemIds = []; + final List downloadQueue = []; + + Future getDownloadsList() async { + downloadedItemIds.clear(); + final videosDir = Directory( + StorageService.appDocsDir + ('/videos'), + ); + final podcastsDir = Directory( + StorageService.appDocsDir + ('/podcasts'), + ); + if (!await videosDir.exists()) { + await videosDir.create(); + } + if (!await podcastsDir.exists()) { + await podcastsDir.create(); + } + videosDir.list(recursive: false).listen( + (event) { + downloadedItemIds.add( + int.parse( + event.path.split('/').last.split('-').last.split('.').first, + ), + ); + }, + ); + podcastsDir.list(recursive: false).listen( + (event) { + downloadedItemIds.add( + int.parse( + event.path.split('/').last.split('-').last.split('.').first, + ), + ); + }, + ); + await Future.delayed(const Duration(milliseconds: 300), notifyListeners); + } + + Future download({ + required String url, + required String fileName, + required bool isVideo, + }) async { + appState = AppState.busy; + downloadQueue.add(url); + notifyListeners(); + final service = RequestService(url); + await service.download(fileName, isVideo ? 'videos' : 'podcasts'); + downloadQueue.remove(url); + getDownloadsList(); + } +} diff --git a/lib/services/media/media.dart b/lib/services/media/media.dart index a6595fd..5e6d816 100644 --- a/lib/services/media/media.dart +++ b/lib/services/media/media.dart @@ -1,7 +1,9 @@ import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/models/studio_details_data.dart'; +import 'package:didvan/providers/media.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; +import 'package:didvan/services/storage/storage.dart'; import 'package:flutter/foundation.dart'; import 'package:image_picker/image_picker.dart'; import 'package:just_audio/just_audio.dart'; @@ -30,6 +32,10 @@ class MediaService { String tag; tag = '${isVoiceMessage ? 'message' : 'podcast'}-$id'; isNetworkAudio ??= audioSource.runtimeType == String; + if (!isVoiceMessage && MediaProvider.downloadedItemIds.contains(id)) { + audioSource = StorageService.appDocsDir + '/podcasts/podcast-$id.mp3'; + isNetworkAudio = false; + } if (audioPlayerTag == tag) { if (audioPlayer.playing) { await audioPlayer.pause(); diff --git a/lib/services/network/request.dart b/lib/services/network/request.dart index eeabccc..d444a9a 100644 --- a/lib/services/network/request.dart +++ b/lib/services/network/request.dart @@ -165,7 +165,7 @@ class RequestService { } Future download(String fileName, String subDirectory) async { - Permission.storage.request(); + await Permission.storage.request(); final response = await http.get(Uri.parse(url)); StorageService.createFile( bytes: response.bodyBytes, diff --git a/lib/services/storage/storage.dart b/lib/services/storage/storage.dart index 6547633..ed607e8 100644 --- a/lib/services/storage/storage.dart +++ b/lib/services/storage/storage.dart @@ -19,7 +19,7 @@ class StorageService { await dir.create(recursive: true); } final file = await io.File( - appDocsDir + '/$subDirectory/podcast-$name.mp3', + appDocsDir + '/$subDirectory/$name', ).create(recursive: true); await file.writeAsBytes(bytes); } diff --git a/lib/views/home/studio/studio_details/studio_details.mobile.dart b/lib/views/home/studio/studio_details/studio_details.mobile.dart index 42f0aa7..4f9e77a 100644 --- a/lib/views/home/studio/studio_details/studio_details.mobile.dart +++ b/lib/views/home/studio/studio_details/studio_details.mobile.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/models/view/app_bar_data.dart'; +import 'package:didvan/services/media/media.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; import 'package:didvan/views/home/studio/studio_details/widgets/details_tab_bar.dart'; import 'package:didvan/views/home/studio/studio_details/widgets/studio_details_widget.dart'; @@ -93,6 +94,9 @@ class _StudioDetailsState extends State { await _changeFullSceen(false); return false; } + if (MediaService.currentPodcast != null) { + state.studio = MediaService.currentPodcast!; + } return true; }, child: DidvanScaffold( @@ -205,7 +209,6 @@ class _StudioDetailsState extends State { children: [ StudioDetailsWidget( scrollController: _scrollController, - studio: state.studio, ), ], ), diff --git a/lib/views/home/studio/studio_details/studio_details.web.dart b/lib/views/home/studio/studio_details/studio_details.web.dart index f936205..7dd83af 100644 --- a/lib/views/home/studio/studio_details/studio_details.web.dart +++ b/lib/views/home/studio/studio_details/studio_details.web.dart @@ -2,6 +2,7 @@ import 'dart:ui' as ui; import 'package:didvan/config/design_config.dart'; import 'package:didvan/models/view/app_bar_data.dart'; +import 'package:didvan/services/media/media.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; import 'package:didvan/views/home/studio/studio_details/widgets/details_tab_bar.dart'; import 'package:didvan/views/home/studio/studio_details/widgets/studio_details_widget.dart'; @@ -93,6 +94,9 @@ class _StudioDetailsState extends State { await _changeFullSceen(false); return false; } + if (MediaService.currentPodcast != null) { + state.studio = MediaService.currentPodcast!; + } return true; }, child: DidvanScaffold( @@ -144,7 +148,6 @@ class _StudioDetailsState extends State { children: [ StudioDetailsWidget( scrollController: _scrollController, - studio: state.studio, ), ], ), diff --git a/lib/views/home/studio/studio_details/studio_details_state.dart b/lib/views/home/studio/studio_details/studio_details_state.dart index 5151deb..b1843ab 100644 --- a/lib/views/home/studio/studio_details/studio_details_state.dart +++ b/lib/views/home/studio/studio_details/studio_details_state.dart @@ -6,6 +6,7 @@ import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/models/studio_details_data.dart'; import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/providers/media.dart'; import 'package:didvan/services/media/media.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; @@ -17,9 +18,9 @@ class StudioDetailsState extends CoreProvier { StudioDetailsData? prevStudio; late int initialIndex; late StudioRequestArgs args; + StudioRequestArgs? podcastArgs; final List relatedQueue = []; bool _positionListenerActivated = false; - final List downloadedFileIds = []; int _selectedDetailsIndex = 0; Timer? timer; @@ -39,15 +40,19 @@ class StudioDetailsState extends CoreProvier { int id, { StudioRequestArgs? args, bool? isForward, + bool forceFetch = false, }) async { if (args != null) { this.args = args; } - if (MediaService.currentPodcast?.id == id && this.args.type == 'podcast') { + if (this.args.type == 'podcast') { + podcastArgs = this.args; + } + if (MediaService.currentPodcast?.id == id && + this.args.type == 'podcast' && + !forceFetch) { return; } - - _getDownloadsList(); _selectedDetailsIndex = 0; if (isForward != null) { if (isForward) { @@ -85,7 +90,7 @@ class StudioDetailsState extends CoreProvier { if (result['prevStudio'].isNotEmpty && this.args.page != 0) { prevStudio = StudioDetailsData.fromJson(result['prevStudio']); } - if (isForward == null) { + if (isForward == null && !forceFetch) { await _handlePodcastPlayback(studio); } appState = AppState.idle; @@ -100,14 +105,10 @@ class StudioDetailsState extends CoreProvier { if (args.type == 'podcast') { MediaService.currentPodcast = studio; MediaService.podcastPlaylistArgs = args; - final downloaded = downloadedFileIds.contains(studio.id); await MediaService.handleAudioPlayback( - audioSource: downloaded - ? StorageService.appDocsDir + '/podcasts/podcast-${studio.id}.mp3' - : studio.media, + audioSource: studio.media, id: studio.id, isVoiceMessage: false, - isNetworkAudio: !downloaded, ); if (nextStudio != null && !_positionListenerActivated) { _positionListenerActivated = true; @@ -129,25 +130,6 @@ class StudioDetailsState extends CoreProvier { } } - Future _getDownloadsList() async { - downloadedFileIds.clear(); - final dir = Directory( - StorageService.appDocsDir + ('/${args.type}s'), - ); - if (!await dir.exists()) { - await dir.create(); - } - dir.list(recursive: false).listen( - (event) { - downloadedFileIds.add( - int.parse( - event.path.split('/').last.split('-').last.split('.').first, - ), - ); - }, - ); - } - Future getRelatedContents() async { if (studio.relatedContents.isNotEmpty) return; relatedQueue.add(studio.id); diff --git a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart index 832c9d3..67da8bf 100644 --- a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart +++ b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart @@ -15,129 +15,131 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class StudioDetailsWidget extends StatelessWidget { - final StudioDetailsData studio; final ScrollController? scrollController; final VoidCallback? onCommentsTabSelected; const StudioDetailsWidget({ Key? key, - required this.studio, this.onCommentsTabSelected, this.scrollController, }) : super(key: key); - bool get _isVideo => studio.media.contains('iframe'); - @override Widget build(BuildContext context) { final ds = MediaQuery.of(context).size; return Consumer( - builder: (context, state, child) => Container( - color: Theme.of(context).colorScheme.surface, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (!_isVideo) - DetailsTabBar( - isVideo: _isVideo, - onCommentsTabSelected: onCommentsTabSelected ?? () {}, - ), - const SizedBox(height: 16), - StateHandler( - onRetry: () {}, - state: state, - builder: (context, state) { - if (state.selectedDetailsIndex == 0) { - return SingleChildScrollView( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - DidvanText(state.studio.description), - if (studio.tags.isNotEmpty) const SizedBox(height: 20), - Wrap( - spacing: 8, - runSpacing: 8, - children: [ - for (var i = 0; i < studio.tags.length; i++) - TagItem(tag: studio.tags[i]), - ], - ), - const SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const SizedBox(), - if (state.nextStudio != null) - _StudioPreview( - isNext: true, - studio: state.nextStudio!, - scrollController: scrollController, - ), - if (state.prevStudio != null) - _StudioPreview( - isNext: false, - studio: state.prevStudio!, - scrollController: scrollController, - ), - const SizedBox(), - ], - ) - ], - ), - ); - } - if (state.selectedDetailsIndex == 1) { - return ChangeNotifierProvider( - create: (context) => CommentsState(), - child: SizedBox( - height: ds.height - - ds.width * 9 / 16 - - 144 - - MediaQuery.of(context).padding.top, - child: Comments( - pageData: { - 'id': studio.id, - 'type': 'studio', - 'title': studio.title, - 'onCommentsChanged': state.onCommentsChanged, - 'isPage': false, - }, + builder: (context, state, child) { + bool isVideo = state.studio.media.contains('iframe'); + return Container( + color: Theme.of(context).colorScheme.surface, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (!isVideo) + DetailsTabBar( + isVideo: isVideo, + onCommentsTabSelected: onCommentsTabSelected ?? () {}, + ), + const SizedBox(height: 16), + StateHandler( + onRetry: () {}, + state: state, + builder: (context, state) { + if (state.selectedDetailsIndex == 0) { + return SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + DidvanText(state.studio.description), + if (state.studio.tags.isNotEmpty) + const SizedBox(height: 20), + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + for (var i = 0; i < state.studio.tags.length; i++) + TagItem(tag: state.studio.tags[i]), + ], + ), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const SizedBox(), + if (state.nextStudio != null) + _StudioPreview( + isNext: true, + studio: state.nextStudio!, + scrollController: scrollController, + ), + if (state.prevStudio != null) + _StudioPreview( + isNext: false, + studio: state.prevStudio!, + scrollController: scrollController, + ), + const SizedBox(), + ], + ) + ], ), - ), - ); - } - return Column( - children: [ - if (studio.relatedContents.isEmpty) - for (var i = 0; i < 3; i++) + ); + } + if (state.selectedDetailsIndex == 1) { + return ChangeNotifierProvider( + create: (context) => CommentsState(), + child: SizedBox( + height: ds.height - + ds.width * 9 / 16 - + 144 - + MediaQuery.of(context).padding.top, + child: Comments( + pageData: { + 'id': state.studio.id, + 'type': 'studio', + 'title': state.studio.title, + 'onCommentsChanged': state.onCommentsChanged, + 'isPage': false, + }, + ), + ), + ); + } + return Column( + children: [ + if (state.studio.relatedContents.isEmpty) + for (var i = 0; i < 3; i++) + Padding( + padding: const EdgeInsets.only( + bottom: 8, + left: 16, + right: 16, + ), + child: MultitypeOverview.placeholder, + ), + for (var i = 0; + i < state.studio.relatedContents.length; + i++) Padding( padding: const EdgeInsets.only( bottom: 8, left: 16, right: 16, ), - child: MultitypeOverview.placeholder, + child: MultitypeOverview( + item: state.studio.relatedContents[i], + onMarkChanged: (id, value) {}, + ), ), - for (var i = 0; i < studio.relatedContents.length; i++) - Padding( - padding: const EdgeInsets.only( - bottom: 8, - left: 16, - right: 16, - ), - child: MultitypeOverview( - item: studio.relatedContents[i], - onMarkChanged: (id, value) {}, - ), - ), - ], - ); - }, - ), - ], - ), - ), + ], + ); + }, + ), + ], + ), + ); + }, ); } } diff --git a/lib/views/home/studio/studio_state.dart b/lib/views/home/studio/studio_state.dart index 8d1e40a..d80e609 100644 --- a/lib/views/home/studio/studio_state.dart +++ b/lib/views/home/studio/studio_state.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:io'; import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; @@ -9,15 +8,10 @@ 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'; -import 'package:didvan/services/storage/storage.dart'; class StudioState extends CoreProvier { final List studios = []; final List sliders = []; - final List downloadedFileIds = []; - final List downloadQueue = []; - - AppState downloadState = AppState.idle; String search = ''; String lastSearch = ''; @@ -37,29 +31,9 @@ class StudioState extends CoreProvier { _videosSelected = value; selectedSortTypeIndex = 0; _getSliders(); - getDownloadsList(); getStudios(page: page); } - Future getDownloadsList() async { - downloadedFileIds.clear(); - final dir = Directory( - StorageService.appDocsDir + (videosSelected ? '/videos' : '/podcasts'), - ); - if (!await dir.exists()) { - await dir.create(); - } - dir.list(recursive: false).listen( - (event) { - downloadedFileIds.add( - int.parse( - event.path.split('/').last.split('-').last.split('.').first, - ), - ); - }, - ); - } - String get order { if (selectedSortTypeIndex == 0 || selectedSortTypeIndex == 1) return 'date'; if (selectedSortTypeIndex == 2) return 'view'; @@ -83,7 +57,6 @@ class StudioState extends CoreProvier { lastSearch = ''; _videosSelected = true; selectedSortTypeIndex = 0; - getDownloadsList(); Future.delayed(Duration.zero, () { _getSliders(); getStudios(page: 1); @@ -148,15 +121,4 @@ class StudioState extends CoreProvier { studios.firstWhere((radar) => radar.id == id).comments = count; notifyListeners(); } - - Future download(String url, String fileName) async { - downloadState = AppState.busy; - downloadQueue.add(url); - notifyListeners(); - final service = RequestService(url); - await service.download(fileName, videosSelected ? 'videos' : 'podcasts'); - downloadState = AppState.idle; - downloadQueue.remove(url); - notifyListeners(); - } } diff --git a/lib/views/home/studio/widgets/tab_bar.dart b/lib/views/home/studio/widgets/tab_bar.dart index b1ed37b..ca27288 100644 --- a/lib/views/home/studio/widgets/tab_bar.dart +++ b/lib/views/home/studio/widgets/tab_bar.dart @@ -76,7 +76,10 @@ class _StudioTypeButton extends StatelessWidget { @override Widget build(BuildContext context) { return GestureDetector( - onTap: onTap, + onTap: () { + onTap(); + FocusScope.of(context).unfocus(); + }, child: Container( color: Colors.transparent, child: Column( diff --git a/lib/views/home/widgets/audio/audio_player_widget.dart b/lib/views/home/widgets/audio/audio_player_widget.dart index 300139d..2dfc3e7 100644 --- a/lib/views/home/widgets/audio/audio_player_widget.dart +++ b/lib/views/home/widgets/audio/audio_player_widget.dart @@ -7,6 +7,7 @@ import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/enums.dart'; import 'package:didvan/models/studio_details_data.dart'; import 'package:didvan/models/view/action_sheet_data.dart'; +import 'package:didvan/providers/media.dart'; import 'package:didvan/services/media/media.dart'; import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; @@ -346,10 +347,6 @@ class __PlayPouseAnimatedIconState extends State<_PlayPouseAnimatedIcon> audioSource: widget.audioSource, isVoiceMessage: false, id: widget.id, - isNetworkAudio: !context - .read() - .downloadedFileIds - .contains(widget.id), ); _handleAnimation(); }, diff --git a/lib/views/home/widgets/overview/podcast.dart b/lib/views/home/widgets/overview/podcast.dart index f4cef1b..16533a7 100644 --- a/lib/views/home/widgets/overview/podcast.dart +++ b/lib/views/home/widgets/overview/podcast.dart @@ -3,9 +3,10 @@ import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/requests/studio.dart'; +import 'package:didvan/providers/media.dart'; +import 'package:didvan/services/media/media.dart'; import 'package:didvan/utils/date_time.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; -import 'package:didvan/views/home/studio/studio_state.dart'; import 'package:didvan/views/home/widgets/bookmark_button.dart'; import 'package:didvan/views/home/widgets/duration_widget.dart'; import 'package:didvan/views/widgets/didvan/card.dart'; @@ -21,19 +22,18 @@ import 'package:provider/provider.dart'; class PodcastOverview extends StatelessWidget { final OverviewData podcast; final void Function(int id, bool value) onMarkChanged; - final StudioRequestArgs? studioRequestArgs; + final StudioRequestArgs studioRequestArgs; final bool hasUnmarkConfirmation; const PodcastOverview({ Key? key, required this.podcast, required this.onMarkChanged, - this.studioRequestArgs, + required this.studioRequestArgs, this.hasUnmarkConfirmation = false, }) : super(key: key); @override Widget build(BuildContext context) { - final state = context.read(); return DidvanCard( onTap: () { context @@ -77,52 +77,57 @@ class PodcastOverview extends StatelessWidget { overflow: TextOverflow.ellipsis, ), const DidvanDivider(verticalPadding: 8), - Row( - children: [ - DurationWidget(duration: podcast.duration!), - const Spacer(), - if (!kIsWeb) ...[ - if (state.downloadState == AppState.idle || - !state.downloadQueue.contains(podcast.media)) - DidvanIconButton( - gestureSize: 28, - color: _isDownloaded(state) - ? Theme.of(context).colorScheme.primary - : null, - icon: _isDownloaded(state) - ? DidvanIcons.download_solid - : DidvanIcons.download_regular, - onPressed: _isDownloaded(state) - ? () {} - : () => state.download( - podcast.media!, podcast.id.toString()), - ), - if (state.downloadState == AppState.busy && - state.downloadQueue.contains(podcast.media)) - const SizedBox( - width: 18, - height: 18, - child: CircularProgressIndicator( - strokeWidth: 2, + Consumer( + builder: (context, state, child) => Row( + children: [ + DurationWidget(duration: podcast.duration!), + const Spacer(), + if (!kIsWeb) ...[ + if (state.appState == AppState.idle || + !state.downloadQueue.contains(podcast.media)) + DidvanIconButton( + gestureSize: 28, + color: _isDownloaded + ? Theme.of(context).colorScheme.primary + : null, + icon: _isDownloaded + ? DidvanIcons.download_solid + : DidvanIcons.download_regular, + onPressed: _isDownloaded + ? () {} + : () => state.download( + fileName: 'podcast-${podcast.id}.mp3', + isVideo: false, + url: podcast.media!, + ), ), - ), - const SizedBox(width: 16), + if (state.appState == AppState.busy && + state.downloadQueue.contains(podcast.media)) + const SizedBox( + width: 18, + height: 18, + child: CircularProgressIndicator( + strokeWidth: 2, + ), + ), + const SizedBox(width: 16), + ], + BookmarkButton( + askForConfirmation: hasUnmarkConfirmation, + gestureSize: 32, + value: podcast.marked, + onMarkChanged: (value) => onMarkChanged(podcast.id, value), + ), ], - BookmarkButton( - askForConfirmation: hasUnmarkConfirmation, - gestureSize: 32, - value: podcast.marked, - onMarkChanged: (value) => onMarkChanged(podcast.id, value), - ), - ], + ), ), ], ), ); } - bool _isDownloaded(state) { - return state.downloadedFileIds.contains(podcast.id); + bool get _isDownloaded { + return MediaProvider.downloadedItemIds.contains(podcast.id); } static Widget get placeholder => DidvanCard( diff --git a/lib/views/home/widgets/overview/radar.dart b/lib/views/home/widgets/overview/radar.dart index fa79acd..856868d 100644 --- a/lib/views/home/widgets/overview/radar.dart +++ b/lib/views/home/widgets/overview/radar.dart @@ -102,13 +102,6 @@ class RadarOverview extends StatelessWidget { const DidvanDivider(), Row( children: [ - BookmarkButton( - gestureSize: 32, - value: radar.marked, - onMarkChanged: (value) => onMarkChanged(radar.id, value), - askForConfirmation: hasUnmarkConfirmation, - ), - const Spacer(), if (radar.comments != 0) DidvanText(radar.comments.toString()), const SizedBox(width: 4), DidvanIconButton( @@ -125,10 +118,17 @@ class RadarOverview extends StatelessWidget { }, ), ), - const SizedBox(width: 16), + // const SizedBox(width: 16), // const DidvanText('10'), // const SizedBox(width: 4), // const Icon(DidvanIcons.evaluation_regular), + const Spacer(), + BookmarkButton( + gestureSize: 32, + value: radar.marked, + onMarkChanged: (value) => onMarkChanged(radar.id, value), + askForConfirmation: hasUnmarkConfirmation, + ), ], ), ], diff --git a/lib/views/splash/splash.dart b/lib/views/splash/splash.dart index 72e3b26..b997efc 100644 --- a/lib/views/splash/splash.dart +++ b/lib/views/splash/splash.dart @@ -2,6 +2,7 @@ import 'dart:developer'; import 'package:didvan/config/design_config.dart'; import 'package:didvan/main.dart'; +import 'package:didvan/providers/media.dart'; import 'package:didvan/providers/server_data_provider.dart'; import 'package:didvan/providers/theme_provider.dart'; import 'package:didvan/providers/user_provider.dart'; @@ -109,6 +110,7 @@ class _SplashState extends State { final String? token = await userProvider.setAndGetToken(); if (token != null) { log(token); + context.read().getDownloadsList(); RequestService.token = token; final result = await userProvider.getUserInfo(); if (!result) { diff --git a/lib/views/widgets/didvan/bnb.dart b/lib/views/widgets/didvan/bnb.dart index 3c4b804..652c535 100644 --- a/lib/views/widgets/didvan/bnb.dart +++ b/lib/views/widgets/didvan/bnb.dart @@ -2,6 +2,7 @@ import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/enums.dart'; +import 'package:didvan/providers/media.dart'; import 'package:didvan/services/media/media.dart'; import 'package:didvan/services/storage/storage.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; @@ -26,6 +27,73 @@ class DidvanBNB extends StatelessWidget { {Key? key, required this.currentTabIndex, required this.onTabChanged}) : super(key: key); + @override + Widget build(BuildContext context) { + return Stack( + children: [ + const _PlayerNavBar(), + Positioned( + bottom: 0, + left: 0, + right: 0, + child: Container( + height: 72, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: + const BorderRadius.vertical(top: Radius.circular(16)), + boxShadow: DesignConfig.defaultShadow, + ), + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + children: [ + _NavBarItem( + isSelected: currentTabIndex == 0, + title: 'اخبار', + selectedIcon: DidvanIcons.news_solid, + unselectedIcon: DidvanIcons.news_light, + onTap: () => onTabChanged(0), + ), + _NavBarItem( + isSelected: currentTabIndex == 1, + title: 'آمار', + selectedIcon: DidvanIcons.chart_solid, + unselectedIcon: DidvanIcons.chart_light, + onTap: () => onTabChanged(1), + ), + _NavBarItem( + isSelected: currentTabIndex == 2, + title: 'رادار', + selectedIcon: DidvanIcons.radar_solid, + unselectedIcon: DidvanIcons.radar_light, + onTap: () => onTabChanged(2), + ), + _NavBarItem( + isSelected: currentTabIndex == 3, + title: 'استودیو', + selectedIcon: DidvanIcons.play_circle_solid, + unselectedIcon: DidvanIcons.play_circle_light, + onTap: () => onTabChanged(3), + ), + _NavBarItem( + isSelected: currentTabIndex == 4, + title: 'تنظیمات', + selectedIcon: DidvanIcons.setting_solid, + unselectedIcon: DidvanIcons.setting_light, + onTap: () => onTabChanged(4), + ), + ], + ), + ), + ), + ], + ); + } +} + +class _PlayerNavBar extends StatelessWidget { + const _PlayerNavBar({Key? key}) : super(key: key); + bool _enablePlayerController(StudioDetailsState state) => MediaService.currentPodcast != null || (MediaService.audioPlayerTag?.contains('podcast') ?? false); @@ -33,227 +101,170 @@ class DidvanBNB extends StatelessWidget { @override Widget build(BuildContext context) { return StreamBuilder( - stream: MediaService.audioPlayer.playingStream, - builder: (context, snapshot) { - return Stack( - children: [ - GestureDetector( - onTap: () => MediaService.currentPodcast == null - ? null - : _showPlayerBottomSheet(context), - child: Consumer( - builder: (context, state, child) => AnimatedContainer( - padding: const EdgeInsets.only(top: 12), - duration: DesignConfig.lowAnimationDuration, - height: _enablePlayerController(state) ? 128 : 72, - decoration: BoxDecoration( - color: DesignConfig.isDark - ? Theme.of(context).colorScheme.focused - : Theme.of(context).colorScheme.navigation, - borderRadius: const BorderRadius.vertical( - top: Radius.circular(16), - ), - ), - alignment: Alignment.topCenter, - child: !_enablePlayerController(state) - ? const SizedBox() - : MediaService.currentPodcast == null - ? SizedBox( - height: 32, - child: Center( - child: SpinKitThreeBounce( - size: 18, + stream: MediaService.audioPlayer.playingStream, + builder: (context, snapshot) => GestureDetector( + onTap: () => MediaService.currentPodcast == null + ? null + : _showPlayerBottomSheet(context), + child: Consumer( + builder: (context, state, child) => AnimatedContainer( + padding: const EdgeInsets.only(top: 12), + duration: DesignConfig.lowAnimationDuration, + height: _enablePlayerController(state) ? 128 : 72, + decoration: BoxDecoration( + color: DesignConfig.isDark + ? Theme.of(context).colorScheme.focused + : Theme.of(context).colorScheme.navigation, + borderRadius: const BorderRadius.vertical( + top: Radius.circular(16), + ), + ), + alignment: Alignment.topCenter, + child: !_enablePlayerController(state) + ? const SizedBox() + : MediaService.currentPodcast == null + ? SizedBox( + height: 32, + child: Center( + child: SpinKitThreeBounce( + size: 18, + color: DesignConfig.isDark + ? Theme.of(context).colorScheme.title + : Theme.of(context).colorScheme.secondCTA, + ), + ), + ) + : SizedBox( + height: 56, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only( + right: 12, + left: 16, + ), + child: DidvanIconButton( + icon: DidvanIcons.close_regular, + color: DesignConfig.isDark + ? null + : Theme.of(context).colorScheme.secondCTA, + gestureSize: 28, + onPressed: MediaService.resetAudioPlayer, + ), + ), + SkeletonImage( + imageUrl: MediaService.currentPodcast!.image, + width: 32, + height: 32, + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + DidvanText( + MediaService.currentPodcast!.title, color: DesignConfig.isDark ? null : Theme.of(context) .colorScheme .secondCTA, ), - ), - ) - : SizedBox( - height: 56, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only( - right: 12, - left: 16, - ), - child: DidvanIconButton( - icon: DidvanIcons.close_regular, + AudioSlider( + disableThumb: true, + tag: MediaService.audioPlayerTag!, + ), + ], + ), + ), + StreamBuilder( + stream: + MediaService.audioPlayer.playerStateStream, + builder: (context, snapshot) { + final playerState = MediaService + .audioPlayer.playerState.processingState; + if (playerState == ProcessingState.loading || + state.appState == AppState.busy) { + return Padding( + padding: const EdgeInsets.only( + top: 4, + left: 16, + right: 16, + ), + child: SizedBox( + height: 18, + width: 18, + child: CircularProgressIndicator( + strokeWidth: 2, color: DesignConfig.isDark - ? null + ? Theme.of(context) + .colorScheme + .title : Theme.of(context) .colorScheme .secondCTA, - gestureSize: 28, - onPressed: - MediaService.resetAudioPlayer, ), ), - SkeletonImage( - imageUrl: - MediaService.currentPodcast!.image, - width: 32, - height: 32, - ), - const SizedBox(width: 16), - Expanded( - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - DidvanText( - MediaService.currentPodcast!.title, - color: DesignConfig.isDark - ? null - : Theme.of(context) - .colorScheme - .secondCTA, - ), - AudioSlider( - disableThumb: true, - tag: MediaService.audioPlayerTag!, - ), - ], - ), - ), - StreamBuilder( - stream: MediaService - .audioPlayer.playerStateStream, - builder: (context, snapshot) { - if (state.appState == AppState.busy || - MediaService.audioPlayer.playerState - .processingState == - ProcessingState.loading) { - return Padding( - padding: const EdgeInsets.only( - top: 4, - left: 16, - right: 16, - ), - child: SizedBox( - height: 18, - width: 18, - child: CircularProgressIndicator( - strokeWidth: 2, - color: Theme.of(context) - .colorScheme - .secondCTA, - ), - ), - ); - } - return const SizedBox(); - }, - ), - if (state.appState != AppState.busy && - MediaService.audioPlayer.playerState - .processingState != - ProcessingState.loading) - Padding( - padding: const EdgeInsets.only( - left: 12, - right: 16, - ), - child: DidvanIconButton( - gestureSize: 28, - color: DesignConfig.isDark - ? null - : Theme.of(context) - .colorScheme - .secondCTA, - icon: snapshot.data! - ? DidvanIcons.pause_solid - : DidvanIcons.play_solid, - onPressed: () { - MediaService.handleAudioPlayback( - audioSource: state - .downloadedFileIds - .contains(state.studio.id) - ? StorageService.appDocsDir + - '/podcasts/podcast-${state.studio.id}.mp3' - : state.studio.media, - isNetworkAudio: !state - .downloadedFileIds - .contains(state.studio.id), - id: state.studio.id, - isVoiceMessage: false, - ); - }, - ), - ), - ], + ); + } + return const SizedBox(); + }, + ), + if (state.appState != AppState.busy && + MediaService.audioPlayer.playerState + .processingState != + ProcessingState.loading) + Padding( + padding: const EdgeInsets.only( + left: 12, + right: 16, + ), + child: DidvanIconButton( + gestureSize: 28, + color: DesignConfig.isDark + ? null + : Theme.of(context).colorScheme.secondCTA, + icon: snapshot.data! + ? DidvanIcons.pause_solid + : DidvanIcons.play_solid, + onPressed: () { + if (state.args.type == 'video') { + state.getStudioDetails( + MediaService.currentPodcast!.id, + args: state.podcastArgs, + forceFetch: true, + ); + } + MediaService.handleAudioPlayback( + audioSource: + MediaService.currentPodcast!.media, + id: MediaService.currentPodcast!.id, + isVoiceMessage: false, + ); + }, ), ), - ), - ), - ), - Positioned( - bottom: 0, - left: 0, - right: 0, - child: Container( - height: 72, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, - borderRadius: - const BorderRadius.vertical(top: Radius.circular(16)), - boxShadow: DesignConfig.defaultShadow, - ), - padding: const EdgeInsets.symmetric(horizontal: 12), - child: Row( - children: [ - _NavBarItem( - isSelected: currentTabIndex == 0, - title: 'اخبار', - selectedIcon: DidvanIcons.news_solid, - unselectedIcon: DidvanIcons.news_light, - onTap: () => onTabChanged(0), + ], + ), ), - _NavBarItem( - isSelected: currentTabIndex == 1, - title: 'آمار', - selectedIcon: DidvanIcons.chart_solid, - unselectedIcon: DidvanIcons.chart_light, - onTap: () => onTabChanged(1), - ), - _NavBarItem( - isSelected: currentTabIndex == 2, - title: 'رادار', - selectedIcon: DidvanIcons.radar_solid, - unselectedIcon: DidvanIcons.radar_light, - onTap: () => onTabChanged(2), - ), - _NavBarItem( - isSelected: currentTabIndex == 3, - title: 'استودیو', - selectedIcon: DidvanIcons.play_circle_solid, - unselectedIcon: DidvanIcons.play_circle_light, - onTap: () => onTabChanged(3), - ), - _NavBarItem( - isSelected: currentTabIndex == 4, - title: 'تنظیمات', - selectedIcon: DidvanIcons.setting_solid, - unselectedIcon: DidvanIcons.setting_light, - onTap: () => onTabChanged(4), - ), - ], - ), - ), - ), - ], - ); - }); + ), + ), + ), + ); } void _showPlayerBottomSheet(BuildContext context) { final sheetKey = GlobalKey(); bool isExpanded = false; final detailsState = context.read(); + if (detailsState.args.type == 'video') { + detailsState.getStudioDetails( + MediaService.currentPodcast!.id, + args: detailsState.podcastArgs, + forceFetch: true, + ); + } final state = context.read(); showModalBottomSheet( backgroundColor: Colors.transparent, @@ -288,23 +299,18 @@ class DidvanBNB extends StatelessWidget { color: Theme.of(context).colorScheme.surface, child: Column( children: [ - StatefulBuilder( - builder: (context, setState) => DidvanIconButton( - size: 32, - icon: isExpanded - ? DidvanIcons.angle_down_regular - : DidvanIcons.angle_up_regular, - onPressed: () { - if (!isExpanded) { - sheetKey.currentState?.expand(); - isExpanded = true; - } else { - isExpanded = false; - sheetKey.currentState?.contract(); - } - setState(() {}); - }, - ), + DidvanIconButton( + size: 32, + icon: DidvanIcons.angle_up_regular, + onPressed: () { + if (!isExpanded) { + sheetKey.currentState?.expand(); + isExpanded = true; + } else { + isExpanded = false; + sheetKey.currentState?.contract(); + } + }, ), const SizedBox(height: 16), ], @@ -322,7 +328,6 @@ class DidvanBNB extends StatelessWidget { ), ) : StudioDetailsWidget( - studio: detailsState.studio, onCommentsTabSelected: () { Future.delayed( const Duration(milliseconds: 100), From 67c583bc7e628610e349355054816dbdf2ae37aa Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Wed, 30 Mar 2022 21:24:48 +0430 Subject: [PATCH 19/54] bug fixes --- lib/views/home/studio/studio_details/studio_details_state.dart | 3 --- lib/views/home/widgets/audio/audio_player_widget.dart | 1 - lib/views/home/widgets/overview/podcast.dart | 1 - lib/views/widgets/didvan/bnb.dart | 2 -- 4 files changed, 7 deletions(-) diff --git a/lib/views/home/studio/studio_details/studio_details_state.dart b/lib/views/home/studio/studio_details/studio_details_state.dart index b1843ab..a91365b 100644 --- a/lib/views/home/studio/studio_details/studio_details_state.dart +++ b/lib/views/home/studio/studio_details/studio_details_state.dart @@ -1,16 +1,13 @@ import 'dart:async'; -import 'dart:io'; import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/models/studio_details_data.dart'; import 'package:didvan/providers/core_provider.dart'; -import 'package:didvan/providers/media.dart'; import 'package:didvan/services/media/media.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; -import 'package:didvan/services/storage/storage.dart'; class StudioDetailsState extends CoreProvier { late StudioDetailsData studio; diff --git a/lib/views/home/widgets/audio/audio_player_widget.dart b/lib/views/home/widgets/audio/audio_player_widget.dart index 2dfc3e7..7be0743 100644 --- a/lib/views/home/widgets/audio/audio_player_widget.dart +++ b/lib/views/home/widgets/audio/audio_player_widget.dart @@ -7,7 +7,6 @@ import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/enums.dart'; import 'package:didvan/models/studio_details_data.dart'; import 'package:didvan/models/view/action_sheet_data.dart'; -import 'package:didvan/providers/media.dart'; import 'package:didvan/services/media/media.dart'; import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; diff --git a/lib/views/home/widgets/overview/podcast.dart b/lib/views/home/widgets/overview/podcast.dart index 16533a7..d517dcc 100644 --- a/lib/views/home/widgets/overview/podcast.dart +++ b/lib/views/home/widgets/overview/podcast.dart @@ -4,7 +4,6 @@ import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/providers/media.dart'; -import 'package:didvan/services/media/media.dart'; import 'package:didvan/utils/date_time.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; import 'package:didvan/views/home/widgets/bookmark_button.dart'; diff --git a/lib/views/widgets/didvan/bnb.dart b/lib/views/widgets/didvan/bnb.dart index 652c535..5583482 100644 --- a/lib/views/widgets/didvan/bnb.dart +++ b/lib/views/widgets/didvan/bnb.dart @@ -2,9 +2,7 @@ import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/enums.dart'; -import 'package:didvan/providers/media.dart'; import 'package:didvan/services/media/media.dart'; -import 'package:didvan/services/storage/storage.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; import 'package:didvan/views/home/studio/studio_details/widgets/studio_details_widget.dart'; import 'package:didvan/views/home/studio/studio_state.dart'; From d83625521b022c78792b344cd47cf540550dac26 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Wed, 30 Mar 2022 21:25:19 +0430 Subject: [PATCH 20/54] registering fcm token for users --- lib/providers/user_provider.dart | 9 +++++++++ lib/services/app_initalizer.dart | 5 ++++- lib/services/network/request_helper.dart | 2 ++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/providers/user_provider.dart b/lib/providers/user_provider.dart index ad4a4fb..ab694ac 100644 --- a/lib/providers/user_provider.dart +++ b/lib/providers/user_provider.dart @@ -3,6 +3,7 @@ import 'package:didvan/models/enums.dart'; import 'package:didvan/models/user.dart'; import 'package:didvan/models/view/alert_data.dart'; import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/services/app_initalizer.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; import 'package:didvan/services/storage/storage.dart'; @@ -35,11 +36,19 @@ class UserProvider extends CoreProvier { } if (service.isSuccess) { user = User.fromJson(service.result['user']); + await _registerFirebaseToken(); return true; } throw 'Getting user from API failed!'; } + Future _registerFirebaseToken() async { + final service = RequestService(RequestHelper.firebaseToken, body: { + 'firebaseToken': AppInitializer.fcmToken, + }); + await service.put(); + } + Future setProfilePhoto(dynamic file) async { appState = AppState.isolatedBusy; final RequestService service = diff --git a/lib/services/app_initalizer.dart b/lib/services/app_initalizer.dart index 7483968..dfcc613 100644 --- a/lib/services/app_initalizer.dart +++ b/lib/services/app_initalizer.dart @@ -8,6 +8,8 @@ import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; class AppInitializer { + static String? fcmToken; + static Future setupServices() async { if (!kIsWeb) { StorageService.appDocsDir = @@ -74,7 +76,8 @@ class AppInitializer { Firebase.app(); } final FirebaseMessaging fcm = FirebaseMessaging.instance; - fcm.subscribeToTopic('general'); + fcmToken = await fcm.getToken(); + await fcm.subscribeToTopic('general'); await fcm.requestPermission( alert: true, announcement: false, diff --git a/lib/services/network/request_helper.dart b/lib/services/network/request_helper.dart index ead496c..d355237 100644 --- a/lib/services/network/request_helper.dart +++ b/lib/services/network/request_helper.dart @@ -15,6 +15,8 @@ class RequestHelper { static const String login = _baseUserUrl + '/login'; static const String directs = _baseUserUrl + '/direct'; static const String userInfo = _baseUserUrl + '/info'; + static const String firebaseToken = _baseUserUrl + '/firebaseToken'; + static const String silenceInterval = _baseUserUrl + '/silenceInterval'; static const String updateProfilePhoto = _baseUserUrl + '/profile/photo'; static const String checkUsername = _baseUserUrl + '/CheckUsername'; static const String updateProfile = _baseUserUrl + '/profile/edit'; From 74b0630328e20cf31cdf2ee0fe661055a8d38988 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Wed, 30 Mar 2022 22:16:00 +0430 Subject: [PATCH 21/54] silence interval now works --- lib/services/app_initalizer.dart | 4 +- .../settings/general_settings/settings.dart | 39 +++++++++++++------ .../general_settings/settings_state.dart | 30 ++++++++++---- pubspec.lock | 2 +- pubspec.yaml | 2 +- 5 files changed, 54 insertions(+), 23 deletions(-) diff --git a/lib/services/app_initalizer.dart b/lib/services/app_initalizer.dart index dfcc613..78a3f4b 100644 --- a/lib/services/app_initalizer.dart +++ b/lib/services/app_initalizer.dart @@ -36,11 +36,11 @@ class AppInitializer { } else { await StorageService.setValue( key: 'notificationTimeRangeStart', - value: '00:00', + value: '0', ); await StorageService.setValue( key: 'notificationTimeRangeEnd', - value: '23:59', + value: '24', ); await StorageService.setValue( key: 'fontFamily', diff --git a/lib/views/home/settings/general_settings/settings.dart b/lib/views/home/settings/general_settings/settings.dart index 6dcfd2c..9f0cadd 100644 --- a/lib/views/home/settings/general_settings/settings.dart +++ b/lib/views/home/settings/general_settings/settings.dart @@ -8,6 +8,7 @@ import 'package:didvan/models/view/action_sheet_data.dart'; import 'package:didvan/models/view/app_bar_data.dart'; import 'package:didvan/providers/theme_provider.dart'; import 'package:didvan/utils/action_sheet.dart'; +import 'package:didvan/utils/date_time.dart'; import 'package:didvan/views/home/settings/general_settings/settings_state.dart'; import 'package:didvan/views/home/widgets/menu_item.dart'; import 'package:didvan/views/widgets/didvan/card.dart'; @@ -36,6 +37,9 @@ class _GeneralSettingsState extends State { return 'کوچک'; } + int _intervalStart = 0; + int _intervalEnd = 24; + @override Widget build(BuildContext context) { return Consumer( @@ -50,7 +54,13 @@ class _GeneralSettingsState extends State { title: 'زمان دریافت اعلان', onTap: () => _pickTimeRange(context), icon: DidvanIcons.notification_regular, - suffix: state.notificationTimeRange[0], + suffix: DateTimeUtils.normalizeTimeDuration( + Duration(minutes: state.notificationTimeRange[1]), + ) + + ' - ' + + DateTimeUtils.normalizeTimeDuration( + Duration(minutes: state.notificationTimeRange[0]), + ), ), ), const ItemTitle( @@ -183,6 +193,9 @@ class _GeneralSettingsState extends State { } Future _pickTimeRange(BuildContext context) async { + final state = context.read(); + _intervalStart = state.notificationTimeRange[0]; + _intervalEnd = state.notificationTimeRange[1]; ActionSheetUtils.showBottomSheet( data: ActionSheetData( content: Row( @@ -198,6 +211,9 @@ class _GeneralSettingsState extends State { ), title: 'زمان دریافت اعلان', titleIcon: DidvanIcons.notification_regular, + onConfirmed: () { + state.notificationTimeRange = [_intervalStart, _intervalEnd]; + }, ), ); } @@ -222,7 +238,9 @@ class _GeneralSettingsState extends State { color: Theme.of(context).colorScheme.border, ), ), - child: DidvanText(state.notificationTimeRange[index]), + child: DidvanText(DateTimeUtils.normalizeTimeDuration( + Duration(minutes: index == 0 ? _intervalStart : _intervalEnd), + )), ), ), ), @@ -230,7 +248,6 @@ class _GeneralSettingsState extends State { } Future _openTimePicker(BuildContext context, int index) async { - final GeneralSettingsState state = context.read(); await Navigator.of(context).push( showPicker( okText: 'تایید', @@ -240,8 +257,9 @@ class _GeneralSettingsState extends State { cancelStyle: Theme.of(context).textTheme.bodyText2!, unselectedColor: Theme.of(context).colorScheme.text, blurredBackground: true, - hourLabel: 'ساعت', - minuteLabel: 'دقیقه', + disableMinute: true, + hourLabel: ':', + minuteLabel: '', is24HrFormat: true, iosStylePicker: true, minuteInterval: MinuteInterval.FIFTEEN, @@ -249,12 +267,11 @@ class _GeneralSettingsState extends State { value: const TimeOfDay(hour: 0, minute: 0), themeData: Theme.of(context), onChange: (time) { - state.notificationTimeRange = state.notificationTimeRange - ..replaceRange( - index, - index + 1, - ['${time.hour}:${time.minute}'], - ); + if (index == 0) { + _intervalStart = time.hour; + return; + } + _intervalEnd = time.hour; }, ), ); diff --git a/lib/views/home/settings/general_settings/settings_state.dart b/lib/views/home/settings/general_settings/settings_state.dart index 19d5d5b..5a58cf0 100644 --- a/lib/views/home/settings/general_settings/settings_state.dart +++ b/lib/views/home/settings/general_settings/settings_state.dart @@ -1,5 +1,7 @@ import 'package:didvan/models/enums.dart'; import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/services/network/request.dart'; +import 'package:didvan/services/network/request_helper.dart'; import 'package:didvan/services/storage/storage.dart'; class GeneralSettingsState extends CoreProvier { @@ -7,24 +9,26 @@ class GeneralSettingsState extends CoreProvier { getSettingsFromStorage(); } - List _notificationTimeRange = ['00:00', '23:59']; + List _notificationTimeRange = [0, 24]; String _fontFamily = 'Dana-FA'; double _fontSizeScale = 1; String _brightness = 'light'; - set notificationTimeRange(List value) { + set notificationTimeRange(List value) { _notificationTimeRange = value; StorageService.setValue( key: 'notificationTimeRangeStart', value: value[0], ); StorageService.setValue( - key: 'notificationTimeRangeStart', + key: 'notificationTimeRangeEnd', value: value[1], ); + notifyListeners(); + _setSilenceInterval(); } - List get notificationTimeRange => _notificationTimeRange; + List get notificationTimeRange => _notificationTimeRange; set fontFamily(String value) { _fontFamily = value; @@ -59,12 +63,22 @@ class GeneralSettingsState extends CoreProvier { String get brightness => _brightness; + Future _setSilenceInterval() async { + final service = RequestService(RequestHelper.silenceInterval, body: { + 'start': notificationTimeRange[0], + 'end': notificationTimeRange[1] + }); + await service.put(); + } + Future getSettingsFromStorage() async { appState = AppState.busy; - _notificationTimeRange[0] = - await StorageService.getValue(key: 'notificationTimeRangeStart'); - _notificationTimeRange[1] = - await StorageService.getValue(key: 'notificationTimeRangeEnd'); + _notificationTimeRange[0] = int.parse( + await StorageService.getValue(key: 'notificationTimeRangeStart'), + ); + _notificationTimeRange[1] = int.parse( + await StorageService.getValue(key: 'notificationTimeRangeEnd'), + ); _fontFamily = await StorageService.getValue(key: 'fontFamily'); _brightness = await StorageService.getValue(key: 'brightness'); final scale = await StorageService.getValue(key: 'fontSizeScale'); diff --git a/pubspec.lock b/pubspec.lock index bb94c55..8495b3a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -126,7 +126,7 @@ packages: name: day_night_time_picker url: "https://pub.dartlang.org" source: hosted - version: "1.0.4+1" + version: "1.0.5" expandable_bottom_sheet: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index a75edb7..7fc57f2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -40,7 +40,7 @@ dependencies: pin_code_fields: ^7.3.0 rive: ^0.7.33 image_picker: ^0.8.4+4 - day_night_time_picker: ^1.0.3+1 + day_night_time_picker: ^1.0.5 path_provider: ^2.0.8 flutter_spinkit: ^5.1.0 flutter_svg: ^1.0.0 From 01d031231037401c8ad032a8649233ad1f9bc167 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Thu, 31 Mar 2022 00:00:55 +0430 Subject: [PATCH 22/54] version upgrade --- lib/providers/user_provider.dart | 1 + lib/views/home/settings/settings.dart | 2 +- pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/providers/user_provider.dart b/lib/providers/user_provider.dart index ab694ac..413d11f 100644 --- a/lib/providers/user_provider.dart +++ b/lib/providers/user_provider.dart @@ -46,6 +46,7 @@ class UserProvider extends CoreProvier { final service = RequestService(RequestHelper.firebaseToken, body: { 'firebaseToken': AppInitializer.fcmToken, }); + await service.put(); } diff --git a/lib/views/home/settings/settings.dart b/lib/views/home/settings/settings.dart index 63c4439..993cabc 100644 --- a/lib/views/home/settings/settings.dart +++ b/lib/views/home/settings/settings.dart @@ -108,7 +108,7 @@ class Settings extends StatelessWidget { ), const SizedBox(height: 16), DidvanText( - 'نسخه نرم‌افزار: 1.1.4', + 'نسخه نرم‌افزار: 1.5.0', style: Theme.of(context).textTheme.caption, ), ], diff --git a/pubspec.yaml b/pubspec.yaml index 7fc57f2..4f32cce 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.5.0+6 +version: 1.5.0+10 environment: sdk: ">=2.12.0 <3.0.0" From eaec7340dbb284ef9914430ca28de4a54b5b3e8d Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Thu, 31 Mar 2022 00:09:13 +0430 Subject: [PATCH 23/54] silence interval bug on previews versions fixed --- .../general_settings/settings_state.dart | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/views/home/settings/general_settings/settings_state.dart b/lib/views/home/settings/general_settings/settings_state.dart index 5a58cf0..4448efb 100644 --- a/lib/views/home/settings/general_settings/settings_state.dart +++ b/lib/views/home/settings/general_settings/settings_state.dart @@ -73,12 +73,17 @@ class GeneralSettingsState extends CoreProvier { Future getSettingsFromStorage() async { appState = AppState.busy; - _notificationTimeRange[0] = int.parse( - await StorageService.getValue(key: 'notificationTimeRangeStart'), - ); - _notificationTimeRange[1] = int.parse( - await StorageService.getValue(key: 'notificationTimeRangeEnd'), - ); + try { + _notificationTimeRange[0] = int.parse( + await StorageService.getValue(key: 'notificationTimeRangeStart'), + ); + _notificationTimeRange[1] = int.parse( + await StorageService.getValue(key: 'notificationTimeRangeEnd'), + ); + } catch (e) { + notificationTimeRange = [0, 0]; + } + _fontFamily = await StorageService.getValue(key: 'fontFamily'); _brightness = await StorageService.getValue(key: 'brightness'); final scale = await StorageService.getValue(key: 'fontSizeScale'); From 169b207d9f40e86fd4dc6a06991fe299979a5613 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Thu, 31 Mar 2022 00:25:01 +0430 Subject: [PATCH 24/54] D1APP-89 tracking api connected --- lib/services/network/request_helper.dart | 2 + .../home/news/news_details/news_details.dart | 70 +++++++++------- .../news/news_details/news_details_state.dart | 14 +++- .../radar/radar_details/radar_details.dart | 84 ++++++++++--------- .../radar_details/radar_details_state.dart | 14 +++- 5 files changed, 105 insertions(+), 79 deletions(-) diff --git a/lib/services/network/request_helper.dart b/lib/services/network/request_helper.dart index d355237..525622e 100644 --- a/lib/services/network/request_helper.dart +++ b/lib/services/network/request_helper.dart @@ -119,6 +119,8 @@ class RequestHelper { ]); static String mark(int id, String type) => baseUrl + '/$type/$id/mark'; + static String tracking(int id, String type) => + baseUrl + '/$type/$id/tracking'; static String comments(int id, String type) => baseUrl + '/$type/$id/comments'; static String feedback(int id, int commentId, String type) => diff --git a/lib/views/home/news/news_details/news_details.dart b/lib/views/home/news/news_details/news_details.dart index 08d5a11..aede351 100644 --- a/lib/views/home/news/news_details/news_details.dart +++ b/lib/views/home/news/news_details/news_details.dart @@ -38,40 +38,46 @@ class _NewsDetailsState extends State { Widget build(BuildContext context) { return Scaffold( body: Consumer( - builder: (context, state, child) => StateHandler( - onRetry: () => state.getNewsDetails(state.currentNews.id), - state: state, - builder: (context, state) => Stack( - children: [ - IgnorePointer( - ignoring: state.isFetchingNewItem, - child: DidvanPageView( - isRadar: false, - initialIndex: state.initialIndex, - onPageChanged: _onPageChnaged, - scrollController: _scrollController, - items: state.news, - currentIndex: state.currentIndex, - ), - ), - Positioned( - bottom: 0, - left: 0, - right: 0, - child: FloatingNavigationBar( - hasUnmarkConfirmation: - widget.pageData['hasUnmarkConfirmation'], - scrollController: _scrollController, - item: state.currentNews, - onCommentsChanged: state.onCommentsChanged, - onMarkChanged: (value) => widget.pageData['onMarkChanged']( - state.currentNews.id, - value, + builder: (context, state, child) => WillPopScope( + onWillPop: () async { + state.handleTracking(sendRequest: true); + return true; + }, + child: StateHandler( + onRetry: () => state.getNewsDetails(state.currentNews.id), + state: state, + builder: (context, state) => Stack( + children: [ + IgnorePointer( + ignoring: state.isFetchingNewItem, + child: DidvanPageView( + isRadar: false, + initialIndex: state.initialIndex, + onPageChanged: _onPageChnaged, + scrollController: _scrollController, + items: state.news, + currentIndex: state.currentIndex, ), - isRadar: false, ), - ), - ], + Positioned( + bottom: 0, + left: 0, + right: 0, + child: FloatingNavigationBar( + hasUnmarkConfirmation: + widget.pageData['hasUnmarkConfirmation'], + scrollController: _scrollController, + item: state.currentNews, + onCommentsChanged: state.onCommentsChanged, + onMarkChanged: (value) => widget.pageData['onMarkChanged']( + state.currentNews.id, + value, + ), + isRadar: false, + ), + ), + ], + ), ), ), ), diff --git a/lib/views/home/news/news_details/news_details_state.dart b/lib/views/home/news/news_details/news_details_state.dart index 279bc78..bb6dff6 100644 --- a/lib/views/home/news/news_details/news_details_state.dart +++ b/lib/views/home/news/news_details/news_details_state.dart @@ -32,7 +32,7 @@ class NewsDetailsState extends CoreProvier { } final service = RequestService(RequestHelper.newsDetails(id, args)); await service.httpGet(); - _handleTracking(sendRequest: isForward != null); + handleTracking(sendRequest: isForward != null); if (service.isSuccess) { final result = service.result; final newsItem = NewsDetailsData.fromJson(result['news']); @@ -88,15 +88,21 @@ class NewsDetailsState extends CoreProvier { notifyListeners(); } - Future _handleTracking({bool sendRequest = true}) async { + Future handleTracking({bool sendRequest = true}) async { if (!sendRequest) { + _trackingTimerCounter = 0; _trackingTimer = Timer.periodic(const Duration(seconds: 1), (timer) { _trackingTimerCounter++; }); return; } - //send request - _trackingTimerCounter = 0; + final service = RequestService( + RequestHelper.tracking(currentNews.id, 'news'), + body: { + 'sec': _trackingTimerCounter, + }, + ); + service.put(); } Future getRelatedContents() async { diff --git a/lib/views/home/radar/radar_details/radar_details.dart b/lib/views/home/radar/radar_details/radar_details.dart index d88f98e..f3a4fc3 100644 --- a/lib/views/home/radar/radar_details/radar_details.dart +++ b/lib/views/home/radar/radar_details/radar_details.dart @@ -38,47 +38,53 @@ class _RadarDetailsState extends State { Widget build(BuildContext context) { return Scaffold( body: Consumer( - builder: (context, state, child) => StateHandler( - onRetry: () => state.getRadarDetails(widget.pageData['id']), - state: state, - builder: (context, state) => Stack( - children: [ - IgnorePointer( - ignoring: state.isFetchingNewItem, - child: DidvanPageView( - isRadar: true, - initialIndex: state.initialIndex, - onPageChanged: _onPageChanged, - scrollController: _scrollController, - items: state.radars, - currentIndex: state.currentIndex, - ), - ), - Positioned( - bottom: 0, - left: 0, - right: 0, - child: FloatingNavigationBar( - hasUnmarkConfirmation: - widget.pageData['hasUnmarkConfirmation'], - isRadar: true, - scrollController: _scrollController, - onMarkChanged: (value) => - widget.pageData['onMarkChanged']?.call( - state.currentRadar.id, - value, + builder: (context, state, child) => WillPopScope( + onWillPop: () async { + state.handleTracking(sendRequest: true); + return true; + }, + child: StateHandler( + onRetry: () => state.getRadarDetails(widget.pageData['id']), + state: state, + builder: (context, state) => Stack( + children: [ + IgnorePointer( + ignoring: state.isFetchingNewItem, + child: DidvanPageView( + isRadar: true, + initialIndex: state.initialIndex, + onPageChanged: _onPageChanged, + scrollController: _scrollController, + items: state.radars, + currentIndex: state.currentIndex, ), - item: state.currentRadar, - onCommentsChanged: (count) { - state.onCommentsChanged(count); - widget.pageData['onCommentsChanged']?.call( - state.currentRadar.id, - count, - ); - }, ), - ), - ], + Positioned( + bottom: 0, + left: 0, + right: 0, + child: FloatingNavigationBar( + hasUnmarkConfirmation: + widget.pageData['hasUnmarkConfirmation'], + isRadar: true, + scrollController: _scrollController, + onMarkChanged: (value) => + widget.pageData['onMarkChanged']?.call( + state.currentRadar.id, + value, + ), + item: state.currentRadar, + onCommentsChanged: (count) { + state.onCommentsChanged(count); + widget.pageData['onCommentsChanged']?.call( + state.currentRadar.id, + count, + ); + }, + ), + ), + ], + ), ), ), ), diff --git a/lib/views/home/radar/radar_details/radar_details_state.dart b/lib/views/home/radar/radar_details/radar_details_state.dart index 37ebb42..b31c6e9 100644 --- a/lib/views/home/radar/radar_details/radar_details_state.dart +++ b/lib/views/home/radar/radar_details/radar_details_state.dart @@ -38,7 +38,7 @@ class RadarDetailsState extends CoreProvier { } final service = RequestService(RequestHelper.radarDetails(id, args)); await service.httpGet(); - _handleTracking(sendRequest: isForward != null); + handleTracking(sendRequest: isForward != null); if (service.isSuccess) { final result = service.result; final radar = RadarDetailsData.fromJson(result['radar']); @@ -121,15 +121,21 @@ class RadarDetailsState extends CoreProvier { notifyListeners(); } - Future _handleTracking({bool sendRequest = true}) async { + Future handleTracking({bool sendRequest = true}) async { if (!sendRequest) { + _trackingTimerCounter = 0; _trackingTimer = Timer.periodic(const Duration(seconds: 1), (timer) { _trackingTimerCounter++; }); return; } - //send request - _trackingTimerCounter = 0; + final service = RequestService( + RequestHelper.tracking(currentRadar.id, 'radar'), + body: { + 'sec': _trackingTimerCounter, + }, + ); + service.put(); } @override From 3f31e7610827fb221965d2b1092c49491fd1a0a2 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Thu, 31 Mar 2022 00:26:47 +0430 Subject: [PATCH 25/54] file renaminggs --- lib/config/design_config.dart | 2 +- lib/main.dart | 4 ++-- lib/providers/{core_provider.dart => core.dart} | 0 lib/providers/media.dart | 2 +- .../{server_data_provider.dart => server_data.dart} | 0 lib/providers/{settings_provider.dart => settings.dart} | 2 +- lib/providers/{theme_provider.dart => theme.dart} | 2 +- lib/providers/{user_provider.dart => user.dart} | 2 +- lib/views/authentication/authentication_state.dart | 4 ++-- lib/views/authentication/screens/password.dart | 4 ++-- lib/views/authentication/screens/reset_password.dart | 2 +- lib/views/authentication/screens/verification.dart | 2 +- lib/views/home/comments/comments_state.dart | 4 ++-- lib/views/home/direct/direct.dart | 2 +- lib/views/home/direct/direct_state.dart | 2 +- lib/views/home/hashtag/hashtag_state.dart | 2 +- lib/views/home/home_state.dart | 2 +- lib/views/home/news/news_details/news_details_state.dart | 2 +- lib/views/home/news/news_state.dart | 4 ++-- lib/views/home/radar/radar_details/radar_details_state.dart | 2 +- lib/views/home/radar/radar_state.dart | 4 ++-- lib/views/home/settings/bookmarks/bookmark_state.dart | 4 ++-- .../filtered_bookmark/filtered_bookmarks_state.dart | 4 ++-- lib/views/home/settings/direct_list/direct_list_state.dart | 2 +- lib/views/home/settings/general_settings/settings.dart | 2 +- .../home/settings/general_settings/settings_state.dart | 2 +- lib/views/home/settings/profile/profile.dart | 2 +- lib/views/home/settings/profile/widgets/profile_photo.dart | 2 +- lib/views/home/settings/settings.dart | 4 ++-- .../home/studio/studio_details/studio_details_state.dart | 2 +- lib/views/home/studio/studio_state.dart | 4 ++-- lib/views/splash/splash.dart | 6 +++--- lib/views/widgets/state_handlers/sliver_state_handler.dart | 2 +- lib/views/widgets/state_handlers/state_handler.dart | 2 +- 34 files changed, 44 insertions(+), 44 deletions(-) rename lib/providers/{core_provider.dart => core.dart} (100%) rename lib/providers/{server_data_provider.dart => server_data.dart} (100%) rename lib/providers/{settings_provider.dart => settings.dart} (83%) rename lib/providers/{theme_provider.dart => theme.dart} (91%) rename lib/providers/{user_provider.dart => user.dart} (99%) diff --git a/lib/config/design_config.dart b/lib/config/design_config.dart index bfb0fb8..afa9a76 100644 --- a/lib/config/design_config.dart +++ b/lib/config/design_config.dart @@ -1,6 +1,6 @@ import 'package:didvan/config/theme_data.dart'; import 'package:didvan/main.dart'; -import 'package:didvan/providers/theme_provider.dart'; +import 'package:didvan/providers/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; diff --git a/lib/main.dart b/lib/main.dart index d16f740..c8b4e77 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,8 +1,8 @@ import 'package:bot_toast/bot_toast.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/providers/media.dart'; -import 'package:didvan/providers/theme_provider.dart'; -import 'package:didvan/providers/user_provider.dart'; +import 'package:didvan/providers/theme.dart'; +import 'package:didvan/providers/user.dart'; import 'package:didvan/routes/route_generator.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; import 'package:flutter/material.dart'; diff --git a/lib/providers/core_provider.dart b/lib/providers/core.dart similarity index 100% rename from lib/providers/core_provider.dart rename to lib/providers/core.dart diff --git a/lib/providers/media.dart b/lib/providers/media.dart index 0be72e0..4d93846 100644 --- a/lib/providers/media.dart +++ b/lib/providers/media.dart @@ -1,7 +1,7 @@ import 'dart:io'; import 'package:didvan/models/enums.dart'; -import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/providers/core.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/storage/storage.dart'; diff --git a/lib/providers/server_data_provider.dart b/lib/providers/server_data.dart similarity index 100% rename from lib/providers/server_data_provider.dart rename to lib/providers/server_data.dart diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings.dart similarity index 83% rename from lib/providers/settings_provider.dart rename to lib/providers/settings.dart index d49f51f..ebb9005 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings.dart @@ -1,4 +1,4 @@ -import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/providers/core.dart'; import 'package:flutter/material.dart'; class SettingsProvider extends CoreProvier { diff --git a/lib/providers/theme_provider.dart b/lib/providers/theme.dart similarity index 91% rename from lib/providers/theme_provider.dart rename to lib/providers/theme.dart index b994b5c..128443a 100644 --- a/lib/providers/theme_provider.dart +++ b/lib/providers/theme.dart @@ -1,4 +1,4 @@ -import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/providers/core.dart'; import 'package:flutter/material.dart'; class ThemeProvider extends CoreProvier { diff --git a/lib/providers/user_provider.dart b/lib/providers/user.dart similarity index 99% rename from lib/providers/user_provider.dart rename to lib/providers/user.dart index 413d11f..ef44802 100644 --- a/lib/providers/user_provider.dart +++ b/lib/providers/user.dart @@ -2,7 +2,7 @@ import 'package:collection/collection.dart'; import 'package:didvan/models/enums.dart'; import 'package:didvan/models/user.dart'; import 'package:didvan/models/view/alert_data.dart'; -import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/providers/core.dart'; import 'package:didvan/services/app_initalizer.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; diff --git a/lib/views/authentication/authentication_state.dart b/lib/views/authentication/authentication_state.dart index ffda950..441d661 100644 --- a/lib/views/authentication/authentication_state.dart +++ b/lib/views/authentication/authentication_state.dart @@ -1,7 +1,7 @@ import 'package:didvan/models/enums.dart'; import 'package:didvan/models/view/alert_data.dart'; -import 'package:didvan/providers/core_provider.dart'; -import 'package:didvan/providers/user_provider.dart'; +import 'package:didvan/providers/core.dart'; +import 'package:didvan/providers/user.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; import 'package:didvan/utils/action_sheet.dart'; diff --git a/lib/views/authentication/screens/password.dart b/lib/views/authentication/screens/password.dart index 69cdc42..6402d6a 100644 --- a/lib/views/authentication/screens/password.dart +++ b/lib/views/authentication/screens/password.dart @@ -1,8 +1,8 @@ import 'dart:developer'; import 'package:didvan/models/view/action_sheet_data.dart'; -import 'package:didvan/providers/server_data_provider.dart'; -import 'package:didvan/providers/user_provider.dart'; +import 'package:didvan/providers/server_data.dart'; +import 'package:didvan/providers/user.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/views/authentication/authentication_state.dart'; diff --git a/lib/views/authentication/screens/reset_password.dart b/lib/views/authentication/screens/reset_password.dart index 9985a95..560ea4b 100644 --- a/lib/views/authentication/screens/reset_password.dart +++ b/lib/views/authentication/screens/reset_password.dart @@ -1,4 +1,4 @@ -import 'package:didvan/providers/user_provider.dart'; +import 'package:didvan/providers/user.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/views/authentication/authentication_state.dart'; import 'package:didvan/views/authentication/widgets/authentication_layout.dart'; diff --git a/lib/views/authentication/screens/verification.dart b/lib/views/authentication/screens/verification.dart index e18e5c3..161d73b 100644 --- a/lib/views/authentication/screens/verification.dart +++ b/lib/views/authentication/screens/verification.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; -import 'package:didvan/providers/user_provider.dart'; +import 'package:didvan/providers/user.dart'; import 'package:didvan/views/authentication/authentication_state.dart'; import 'package:didvan/views/authentication/widgets/authentication_layout.dart'; import 'package:didvan/views/widgets/didvan/button.dart'; diff --git a/lib/views/home/comments/comments_state.dart b/lib/views/home/comments/comments_state.dart index 5982f54..e93f04b 100644 --- a/lib/views/home/comments/comments_state.dart +++ b/lib/views/home/comments/comments_state.dart @@ -4,8 +4,8 @@ import 'package:didvan/models/comment/feedback.dart'; import 'package:didvan/models/comment/reply.dart'; import 'package:didvan/models/comment/user.dart'; import 'package:didvan/models/enums.dart'; -import 'package:didvan/providers/core_provider.dart'; -import 'package:didvan/providers/user_provider.dart'; +import 'package:didvan/providers/core.dart'; +import 'package:didvan/providers/user.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; import 'package:provider/provider.dart'; diff --git a/lib/views/home/direct/direct.dart b/lib/views/home/direct/direct.dart index 17ddee1..d47be3c 100644 --- a/lib/views/home/direct/direct.dart +++ b/lib/views/home/direct/direct.dart @@ -1,7 +1,7 @@ import 'package:didvan/constants/assets.dart'; import 'package:didvan/models/enums.dart'; import 'package:didvan/models/view/app_bar_data.dart'; -import 'package:didvan/providers/server_data_provider.dart'; +import 'package:didvan/providers/server_data.dart'; import 'package:didvan/services/media/media.dart'; import 'package:didvan/views/home/direct/direct_state.dart'; import 'package:didvan/views/home/direct/widgets/message.dart'; diff --git a/lib/views/home/direct/direct_state.dart b/lib/views/home/direct/direct_state.dart index 346eec3..20964d5 100644 --- a/lib/views/home/direct/direct_state.dart +++ b/lib/views/home/direct/direct_state.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:didvan/models/enums.dart'; import 'package:didvan/models/message_data/message_data.dart'; import 'package:didvan/models/message_data/radar_attachment.dart'; -import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/providers/core.dart'; import 'package:didvan/services/media/media.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; diff --git a/lib/views/home/hashtag/hashtag_state.dart b/lib/views/home/hashtag/hashtag_state.dart index fbe6d78..c71acfe 100644 --- a/lib/views/home/hashtag/hashtag_state.dart +++ b/lib/views/home/hashtag/hashtag_state.dart @@ -1,6 +1,6 @@ import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; -import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/providers/core.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; diff --git a/lib/views/home/home_state.dart b/lib/views/home/home_state.dart index ed4ea99..c866e0e 100644 --- a/lib/views/home/home_state.dart +++ b/lib/views/home/home_state.dart @@ -1,4 +1,4 @@ -import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/providers/core.dart'; class HomeState extends CoreProvier { int _currentPageIndex = 2; diff --git a/lib/views/home/news/news_details/news_details_state.dart b/lib/views/home/news/news_details/news_details_state.dart index bb6dff6..8adbb91 100644 --- a/lib/views/home/news/news_details/news_details_state.dart +++ b/lib/views/home/news/news_details/news_details_state.dart @@ -5,7 +5,7 @@ import 'package:didvan/models/enums.dart'; import 'package:didvan/models/news_details_data.dart'; import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/requests/news.dart'; -import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/providers/core.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; diff --git a/lib/views/home/news/news_state.dart b/lib/views/home/news/news_state.dart index 47f3366..29b8221 100644 --- a/lib/views/home/news/news_state.dart +++ b/lib/views/home/news/news_state.dart @@ -1,8 +1,8 @@ import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/requests/news.dart'; -import 'package:didvan/providers/core_provider.dart'; -import 'package:didvan/providers/user_provider.dart'; +import 'package:didvan/providers/core.dart'; +import 'package:didvan/providers/user.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; diff --git a/lib/views/home/radar/radar_details/radar_details_state.dart b/lib/views/home/radar/radar_details/radar_details_state.dart index b31c6e9..81240a3 100644 --- a/lib/views/home/radar/radar_details/radar_details_state.dart +++ b/lib/views/home/radar/radar_details/radar_details_state.dart @@ -5,7 +5,7 @@ import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/radar_details_data.dart'; import 'package:didvan/models/requests/radar.dart'; -import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/providers/core.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; diff --git a/lib/views/home/radar/radar_state.dart b/lib/views/home/radar/radar_state.dart index 75318ee..1b3e319 100644 --- a/lib/views/home/radar/radar_state.dart +++ b/lib/views/home/radar/radar_state.dart @@ -3,8 +3,8 @@ import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/requests/radar.dart'; import 'package:didvan/models/view/radar_category.dart'; -import 'package:didvan/providers/core_provider.dart'; -import 'package:didvan/providers/user_provider.dart'; +import 'package:didvan/providers/core.dart'; +import 'package:didvan/providers/user.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; diff --git a/lib/views/home/settings/bookmarks/bookmark_state.dart b/lib/views/home/settings/bookmarks/bookmark_state.dart index 063dc4b..aa29d9c 100644 --- a/lib/views/home/settings/bookmarks/bookmark_state.dart +++ b/lib/views/home/settings/bookmarks/bookmark_state.dart @@ -1,7 +1,7 @@ import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; -import 'package:didvan/providers/core_provider.dart'; -import 'package:didvan/providers/user_provider.dart'; +import 'package:didvan/providers/core.dart'; +import 'package:didvan/providers/user.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; diff --git a/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart b/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart index 1fb2353..b275054 100644 --- a/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart +++ b/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart @@ -1,7 +1,7 @@ import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; -import 'package:didvan/providers/core_provider.dart'; -import 'package:didvan/providers/user_provider.dart'; +import 'package:didvan/providers/core.dart'; +import 'package:didvan/providers/user.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; diff --git a/lib/views/home/settings/direct_list/direct_list_state.dart b/lib/views/home/settings/direct_list/direct_list_state.dart index c6efe26..40aaee9 100644 --- a/lib/views/home/settings/direct_list/direct_list_state.dart +++ b/lib/views/home/settings/direct_list/direct_list_state.dart @@ -1,6 +1,6 @@ import 'package:didvan/models/chat_room/chat_room.dart'; import 'package:didvan/models/enums.dart'; -import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/providers/core.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; diff --git a/lib/views/home/settings/general_settings/settings.dart b/lib/views/home/settings/general_settings/settings.dart index 9f0cadd..41a9947 100644 --- a/lib/views/home/settings/general_settings/settings.dart +++ b/lib/views/home/settings/general_settings/settings.dart @@ -6,7 +6,7 @@ import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/constants/assets.dart'; import 'package:didvan/models/view/action_sheet_data.dart'; import 'package:didvan/models/view/app_bar_data.dart'; -import 'package:didvan/providers/theme_provider.dart'; +import 'package:didvan/providers/theme.dart'; import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/utils/date_time.dart'; import 'package:didvan/views/home/settings/general_settings/settings_state.dart'; diff --git a/lib/views/home/settings/general_settings/settings_state.dart b/lib/views/home/settings/general_settings/settings_state.dart index 4448efb..8f5249f 100644 --- a/lib/views/home/settings/general_settings/settings_state.dart +++ b/lib/views/home/settings/general_settings/settings_state.dart @@ -1,5 +1,5 @@ import 'package:didvan/models/enums.dart'; -import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/providers/core.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; import 'package:didvan/services/storage/storage.dart'; diff --git a/lib/views/home/settings/profile/profile.dart b/lib/views/home/settings/profile/profile.dart index 5b666e7..ca9acf6 100644 --- a/lib/views/home/settings/profile/profile.dart +++ b/lib/views/home/settings/profile/profile.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:didvan/config/design_config.dart'; import 'package:didvan/models/view/app_bar_data.dart'; -import 'package:didvan/providers/user_provider.dart'; +import 'package:didvan/providers/user.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/views/home/settings/profile/widgets/profile_photo.dart'; import 'package:didvan/views/home/widgets/menu_item.dart'; diff --git a/lib/views/home/settings/profile/widgets/profile_photo.dart b/lib/views/home/settings/profile/widgets/profile_photo.dart index 62f1018..400cfbf 100644 --- a/lib/views/home/settings/profile/widgets/profile_photo.dart +++ b/lib/views/home/settings/profile/widgets/profile_photo.dart @@ -5,7 +5,7 @@ import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/enums.dart'; import 'package:didvan/models/view/action_sheet_data.dart'; import 'package:didvan/models/view/alert_data.dart'; -import 'package:didvan/providers/user_provider.dart'; +import 'package:didvan/providers/user.dart'; import 'package:didvan/services/media/media.dart'; import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/views/home/widgets/menu_item.dart'; diff --git a/lib/views/home/settings/settings.dart b/lib/views/home/settings/settings.dart index 993cabc..e923da5 100644 --- a/lib/views/home/settings/settings.dart +++ b/lib/views/home/settings/settings.dart @@ -1,6 +1,6 @@ import 'package:didvan/constants/app_icons.dart'; -import 'package:didvan/providers/theme_provider.dart'; -import 'package:didvan/providers/user_provider.dart'; +import 'package:didvan/providers/theme.dart'; +import 'package:didvan/providers/user.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/services/storage/storage.dart'; import 'package:didvan/views/home/widgets/logo_app_bar.dart'; diff --git a/lib/views/home/studio/studio_details/studio_details_state.dart b/lib/views/home/studio/studio_details/studio_details_state.dart index a91365b..062ceec 100644 --- a/lib/views/home/studio/studio_details/studio_details_state.dart +++ b/lib/views/home/studio/studio_details/studio_details_state.dart @@ -4,7 +4,7 @@ import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/models/studio_details_data.dart'; -import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/providers/core.dart'; import 'package:didvan/services/media/media.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; diff --git a/lib/views/home/studio/studio_state.dart b/lib/views/home/studio/studio_state.dart index d80e609..2d2e76f 100644 --- a/lib/views/home/studio/studio_state.dart +++ b/lib/views/home/studio/studio_state.dart @@ -4,8 +4,8 @@ import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/models/slider_data.dart'; -import 'package:didvan/providers/core_provider.dart'; -import 'package:didvan/providers/user_provider.dart'; +import 'package:didvan/providers/core.dart'; +import 'package:didvan/providers/user.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; diff --git a/lib/views/splash/splash.dart b/lib/views/splash/splash.dart index b997efc..ad8157b 100644 --- a/lib/views/splash/splash.dart +++ b/lib/views/splash/splash.dart @@ -3,9 +3,9 @@ import 'dart:developer'; import 'package:didvan/config/design_config.dart'; import 'package:didvan/main.dart'; import 'package:didvan/providers/media.dart'; -import 'package:didvan/providers/server_data_provider.dart'; -import 'package:didvan/providers/theme_provider.dart'; -import 'package:didvan/providers/user_provider.dart'; +import 'package:didvan/providers/server_data.dart'; +import 'package:didvan/providers/theme.dart'; +import 'package:didvan/providers/user.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/services/app_initalizer.dart'; import 'package:didvan/services/network/request.dart'; diff --git a/lib/views/widgets/state_handlers/sliver_state_handler.dart b/lib/views/widgets/state_handlers/sliver_state_handler.dart index f27ed14..1e70ddb 100644 --- a/lib/views/widgets/state_handlers/sliver_state_handler.dart +++ b/lib/views/widgets/state_handlers/sliver_state_handler.dart @@ -1,5 +1,5 @@ import 'package:didvan/models/enums.dart'; -import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/providers/core.dart'; import 'package:didvan/views/widgets/state_handlers/empty_connection.dart'; import 'package:flutter/material.dart'; diff --git a/lib/views/widgets/state_handlers/state_handler.dart b/lib/views/widgets/state_handlers/state_handler.dart index 513ac3f..0addd30 100644 --- a/lib/views/widgets/state_handlers/state_handler.dart +++ b/lib/views/widgets/state_handlers/state_handler.dart @@ -1,5 +1,5 @@ import 'package:didvan/models/enums.dart'; -import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/providers/core.dart'; import 'package:didvan/views/widgets/state_handlers/empty_connection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; From c8d40adec3a852d2e1ee1f42736dfe74faf52baa Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Thu, 31 Mar 2022 00:55:36 +0430 Subject: [PATCH 26/54] comments bug fixed --- lib/models/comment/comment.dart | 6 ++-- lib/models/comment/feedback.dart | 6 ++-- lib/providers/user.dart | 3 +- lib/views/home/comments/comments.dart | 4 +++ lib/views/home/comments/comments_state.dart | 29 +++++++++++++-- .../home/comments/widgets/comment_item.dart | 35 ++++++++++++------- 6 files changed, 59 insertions(+), 24 deletions(-) diff --git a/lib/models/comment/comment.dart b/lib/models/comment/comment.dart index b4f43ea..0d58166 100644 --- a/lib/models/comment/comment.dart +++ b/lib/models/comment/comment.dart @@ -6,9 +6,9 @@ class CommentData { int id; final String text; final String createdAt; - final bool liked; - final bool disliked; - FeedbackData feedback; + bool liked; + bool disliked; + final FeedbackData feedback; final UserOverview user; final List replies; diff --git a/lib/models/comment/feedback.dart b/lib/models/comment/feedback.dart index d51d67f..31696ba 100644 --- a/lib/models/comment/feedback.dart +++ b/lib/models/comment/feedback.dart @@ -1,8 +1,8 @@ class FeedbackData { - final int like; - final int dislike; + int like; + int dislike; - const FeedbackData({required this.like, required this.dislike}); + FeedbackData({required this.like, required this.dislike}); factory FeedbackData.fromJson(Map json) => FeedbackData( like: json['like'], diff --git a/lib/providers/user.dart b/lib/providers/user.dart index ef44802..ec703e1 100644 --- a/lib/providers/user.dart +++ b/lib/providers/user.dart @@ -44,9 +44,8 @@ class UserProvider extends CoreProvier { Future _registerFirebaseToken() async { final service = RequestService(RequestHelper.firebaseToken, body: { - 'firebaseToken': AppInitializer.fcmToken, + 'token': AppInitializer.fcmToken, }); - await service.put(); } diff --git a/lib/views/home/comments/comments.dart b/lib/views/home/comments/comments.dart index d4c36aa..6e03d73 100644 --- a/lib/views/home/comments/comments.dart +++ b/lib/views/home/comments/comments.dart @@ -85,6 +85,10 @@ class _CommentsState extends State { title: 'اولین نظر را بنویسید...', ), builder: (context, state, index) => Comment( + key: ValueKey( + state.comments[index].id.toString() + + state.comments[index].text, + ), focusNode: _focusNode, comment: state.comments[index], ), diff --git a/lib/views/home/comments/comments_state.dart b/lib/views/home/comments/comments_state.dart index e93f04b..4b02d00 100644 --- a/lib/views/home/comments/comments_state.dart +++ b/lib/views/home/comments/comments_state.dart @@ -44,8 +44,31 @@ class CommentsState extends CoreProvier { appState = AppState.failed; } - Future feedback(int id, bool like, bool dislike) async { + Future feedback({ + required int id, + required bool like, + required bool dislike, + required int likeCount, + required int dislikeCount, + int? replyId, + }) async { _feedbackQueue.addAll({id: MapEntry(like, dislike)}); + dynamic comment; + if (replyId == null) { + comment = comments.firstWhere((comment) => comment.id == id); + } else { + comment = comments + .firstWhere((comment) => comment.id == id) + .replies + .firstWhere((element) => element.id == replyId); + } + + if (comment != null) { + comment.feedback.like = likeCount; + comment.feedback.dislike = dislikeCount; + comment.disliked = dislike; + comment.liked = like; + } Future.delayed(const Duration(milliseconds: 500), () async { if (!_feedbackQueue.containsKey(id)) return; final service = RequestService( @@ -70,7 +93,7 @@ class CommentsState extends CoreProvier { createdAt: DateTime.now().toString(), liked: false, disliked: false, - feedback: const FeedbackData(like: 0, dislike: 0), + feedback: FeedbackData(like: 0, dislike: 0), toUser: replyingTo!, user: UserOverview( id: user.id, @@ -88,7 +111,7 @@ class CommentsState extends CoreProvier { createdAt: DateTime.now().toString(), liked: false, disliked: false, - feedback: const FeedbackData(like: 0, dislike: 0), + feedback: FeedbackData(like: 0, dislike: 0), user: UserOverview( id: user.id, fullName: user.fullName, diff --git a/lib/views/home/comments/widgets/comment_item.dart b/lib/views/home/comments/widgets/comment_item.dart index 62fc0a2..4748031 100644 --- a/lib/views/home/comments/widgets/comment_item.dart +++ b/lib/views/home/comments/widgets/comment_item.dart @@ -49,7 +49,7 @@ class CommentState extends State { duration: DesignConfig.lowAnimationDuration, isVisible: _showSubComments, child: _commentBuilder( - isSubComment: true, + isReply: true, comment: _comment.replies[i], ), ), @@ -57,11 +57,10 @@ class CommentState extends State { ); } - Widget _commentBuilder({required comment, bool isSubComment = false}) => - Container( + Widget _commentBuilder({required comment, bool isReply = false}) => Container( decoration: BoxDecoration( border: Border( - right: isSubComment + right: isReply ? BorderSide(color: Theme.of(context).colorScheme.caption) : BorderSide.none, ), @@ -69,7 +68,7 @@ class CommentState extends State { child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (isSubComment) const SizedBox(width: 12), + if (isReply) const SizedBox(width: 12), if (comment.user.photo == null) const Icon(DidvanIcons.avatar_light), if (comment.user.photo != null) @@ -99,7 +98,7 @@ class CommentState extends State { ], ), const SizedBox(height: 8), - if (isSubComment) + if (isReply) DidvanText( 'پاسخ به ${comment.toUser.fullName}', style: Theme.of(context).textTheme.caption, @@ -124,8 +123,8 @@ class CommentState extends State { color: Theme.of(context).colorScheme.primary, ), ), - if (!isSubComment) const SizedBox(width: 20), - if (!isSubComment && comment.replies.isNotEmpty) + if (!isReply) const SizedBox(width: 20), + if (!isReply && comment.replies.isNotEmpty) InkWrapper( onPressed: () => setState( () => _showSubComments = !_showSubComments, @@ -154,8 +153,15 @@ class CommentState extends State { dislikeCount: comment.feedback.dislike, likeValue: comment.liked, dislikeValue: comment.disliked, - onFeedback: (like, dislike) => - state.feedback(comment.id, like, dislike), + onFeedback: (like, dislike, likeCount, dislikeCount) => + state.feedback( + id: _comment.id, + like: like, + dislike: dislike, + likeCount: likeCount, + dislikeCount: dislikeCount, + replyId: isReply ? comment.id : null, + ), ), ], ), @@ -172,7 +178,8 @@ class _FeedbackButtons extends StatefulWidget { final int dislikeCount; final bool likeValue; final bool dislikeValue; - final void Function(bool like, bool dislike) onFeedback; + final void Function(bool like, bool dislike, int likeCount, int dislikeCount) + onFeedback; const _FeedbackButtons({ Key? key, required this.onFeedback, @@ -228,7 +235,8 @@ class _FeedbackButtonsState extends State<_FeedbackButtons> { } _likeValue = !_likeValue; }); - widget.onFeedback(_likeValue, _dislikeValue); + widget.onFeedback( + _likeValue, _dislikeValue, _likeCount, _dislikeCount); }, ), const SizedBox(width: 16), @@ -257,7 +265,8 @@ class _FeedbackButtonsState extends State<_FeedbackButtons> { } _dislikeValue = !_dislikeValue; }); - widget.onFeedback(_likeValue, _dislikeValue); + widget.onFeedback( + _likeValue, _dislikeValue, _likeCount, _dislikeCount); }, ), ], From 91d8469d19245b100f0785b021791103c68d41e6 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Thu, 31 Mar 2022 01:58:51 +0430 Subject: [PATCH 27/54] bug fixes --- .../studio_details/widgets/studio_details_widget.dart | 1 + lib/views/home/studio/studio_state.dart | 2 +- lib/views/home/widgets/audio/audio_player_widget.dart | 11 ++++++++--- lib/views/splash/splash.dart | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart index 67da8bf..60a9024 100644 --- a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart +++ b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart @@ -180,6 +180,7 @@ class _StudioPreview extends StatelessWidget { }, child: Container( width: 88, + height: 216, color: Colors.transparent, child: Column( children: [ diff --git a/lib/views/home/studio/studio_state.dart b/lib/views/home/studio/studio_state.dart index 2d2e76f..afab668 100644 --- a/lib/views/home/studio/studio_state.dart +++ b/lib/views/home/studio/studio_state.dart @@ -27,7 +27,7 @@ class StudioState extends CoreProvier { bool get searching => search.isNotEmpty; set videosSelected(bool value) { - if (_videosSelected == value) return; + if (_videosSelected == value || appState == AppState.busy) return; _videosSelected = value; selectedSortTypeIndex = 0; _getSliders(); diff --git a/lib/views/home/widgets/audio/audio_player_widget.dart b/lib/views/home/widgets/audio/audio_player_widget.dart index 7be0743..c289a96 100644 --- a/lib/views/home/widgets/audio/audio_player_widget.dart +++ b/lib/views/home/widgets/audio/audio_player_widget.dart @@ -277,9 +277,14 @@ class AudioPlayerWidget extends StatelessWidget { ), onConfirmed: () { if (!state.stopOnPodcastEnds) { - state.timer = Timer( - Duration(minutes: timerValue), - MediaService.audioPlayer.stop, + state.timer = Timer.periodic( + const Duration(minutes: 1), + (timer) { + timerValue--; + if (timerValue == 0) { + MediaService.audioPlayer.stop(); + } + }, ); } state.timerValue = timerValue; diff --git a/lib/views/splash/splash.dart b/lib/views/splash/splash.dart index ad8157b..6626557 100644 --- a/lib/views/splash/splash.dart +++ b/lib/views/splash/splash.dart @@ -94,7 +94,6 @@ class _SplashState extends State { .removeWhere((key, value) => key == 'image-cache'); }); } - await AppInitializer.setupServices(); final settingsData = await AppInitializer.initilizeSettings(); final themeProvider = context.read(); themeProvider.themeMode = settingsData.themeMode; @@ -106,6 +105,7 @@ class _SplashState extends State { _isGettingThemeData = false; }), ); + await AppInitializer.setupServices(); final userProvider = context.read(); final String? token = await userProvider.setAndGetToken(); if (token != null) { From 56f8b386930b948076b2b17567bcc406381b485c Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Thu, 31 Mar 2022 13:53:36 +0430 Subject: [PATCH 28/54] studio error handlign + bug fixes --- lib/views/home/studio/studio.dart | 148 +++++------ .../studio_details/studio_details.mobile.dart | 8 +- lib/views/home/widgets/search_field.dart | 1 + lib/views/widgets/didvan/bnb.dart | 242 +++++++++--------- 4 files changed, 207 insertions(+), 192 deletions(-) diff --git a/lib/views/home/studio/studio.dart b/lib/views/home/studio/studio.dart index e23a38a..79619e8 100644 --- a/lib/views/home/studio/studio.dart +++ b/lib/views/home/studio/studio.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:didvan/config/design_config.dart'; import 'package:didvan/constants/app_icons.dart'; +import 'package:didvan/models/enums.dart'; import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/models/view/action_sheet_data.dart'; import 'package:didvan/routes/routes.dart'; @@ -18,6 +19,7 @@ import 'package:didvan/views/widgets/didvan/divider.dart'; import 'package:didvan/views/widgets/didvan/icon_button.dart'; import 'package:didvan/views/widgets/didvan/radial_button.dart'; import 'package:didvan/views/widgets/item_title.dart'; +import 'package:didvan/views/widgets/state_handlers/empty_result.dart'; import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -41,89 +43,91 @@ class _StudioState extends State { @override Widget build(BuildContext context) { - return CustomScrollView( - slivers: [ - SliverToBoxAdapter( - child: Row( - children: [ - const Expanded(child: LogoAppBar(type: 'studio')), - Padding( - padding: - EdgeInsets.only(top: MediaQuery.of(context).padding.top), - child: DidvanIconButton( - icon: DidvanIcons.bookmark_regular, - onPressed: () => Navigator.of(context).pushNamed( - Routes.filteredBookmarks, - arguments: { - 'type': context.read().type, - 'onDeleted': (_) {} - }, - ), - ), - ), - ], - ), - ), - const SliverToBoxAdapter( - child: StudioTabBar(), - ), - SliverToBoxAdapter( - child: Padding( - padding: const EdgeInsets.all(16.0), - child: SearchField( - title: 'استودیو', - onChanged: _onChanged, - focusNode: _focusNode, - ), - ), - ), - SliverToBoxAdapter( - child: Consumer( - builder: (context, state, child) => AnimatedVisibility( - isVisible: !state.searching, - duration: DesignConfig.lowAnimationDuration, - child: const StudioSlider(), - ), - ), - ), - const SliverPadding( - padding: EdgeInsets.symmetric(horizontal: 16), - sliver: SliverToBoxAdapter( - child: DidvanDivider( - verticalPadding: 0, - ), - ), - ), - SliverPadding( - padding: const EdgeInsets.symmetric(horizontal: 16), - sliver: SliverToBoxAdapter( + return Consumer( + builder: (context, state, child) => CustomScrollView( + slivers: [ + SliverToBoxAdapter( child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Consumer( - builder: (context, state, child) => AnimatedVisibility( - isVisible: !state.searching, - duration: DesignConfig.lowAnimationDuration, - child: ItemTitle(title: state.orderString), + const Expanded(child: LogoAppBar(type: 'studio')), + Padding( + padding: + EdgeInsets.only(top: MediaQuery.of(context).padding.top), + child: DidvanIconButton( + icon: DidvanIcons.bookmark_regular, + onPressed: () => Navigator.of(context).pushNamed( + Routes.filteredBookmarks, + arguments: {'type': state.type, 'onDeleted': (_) {}}, + ), ), ), - DidvanIconButton( - gestureSize: 36, - icon: DidvanIcons.sort_regular, - onPressed: _showSortDialog, - ), ], ), ), - ), - Consumer( - builder: (context, state, child) => SliverStateHandler( + const SliverToBoxAdapter( + child: StudioTabBar(), + ), + if (state.appState != AppState.failed) + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: SearchField( + title: 'استودیو', + onChanged: _onChanged, + focusNode: _focusNode, + ), + ), + ), + if (state.appState != AppState.failed) + SliverToBoxAdapter( + child: AnimatedVisibility( + isVisible: !state.searching, + duration: DesignConfig.lowAnimationDuration, + child: const StudioSlider(), + ), + ), + if (state.appState != AppState.failed && state.studios.isNotEmpty) + const SliverPadding( + padding: EdgeInsets.symmetric(horizontal: 16), + sliver: SliverToBoxAdapter( + child: DidvanDivider( + verticalPadding: 0, + ), + ), + ), + if (state.appState != AppState.failed && state.studios.isNotEmpty) + SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 16), + sliver: SliverToBoxAdapter( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + AnimatedVisibility( + isVisible: !state.searching, + duration: DesignConfig.lowAnimationDuration, + child: ItemTitle(title: state.orderString), + ), + DidvanIconButton( + gestureSize: 36, + icon: DidvanIcons.sort_regular, + onPressed: _showSortDialog, + ), + ], + ), + ), + ), + SliverStateHandler( state: state, itemPadding: const EdgeInsets.only( bottom: 8, left: 16, right: 16, ), + emptyState: EmptyResult( + onNewSearch: () => _focusNode.requestFocus(), + ), + centerEmptyState: false, + enableEmptyState: state.studios.isEmpty, placeholder: state.videosSelected ? VideoOverview.placeHolder : PodcastOverview.placeholder, @@ -154,8 +158,8 @@ class _StudioState extends State { childCount: state.studios.length, onRetry: () => state.getStudios(page: 1), ), - ), - ], + ], + ), ); } diff --git a/lib/views/home/studio/studio_details/studio_details.mobile.dart b/lib/views/home/studio/studio_details/studio_details.mobile.dart index 4f9e77a..2331fb1 100644 --- a/lib/views/home/studio/studio_details/studio_details.mobile.dart +++ b/lib/views/home/studio/studio_details/studio_details.mobile.dart @@ -86,7 +86,13 @@ class _StudioDetailsState extends State { return Consumer( builder: (context, state, child) => StateHandler( state: state, - onRetry: () => state.getStudioDetails(state.studio.id), + onRetry: () { + try { + state.getStudioDetails(state.studio.id); + } catch (e) { + state.getStudioDetails(widget.pageData['id']); + } + }, builder: (context, state) { return WillPopScope( onWillPop: () async { diff --git a/lib/views/home/widgets/search_field.dart b/lib/views/home/widgets/search_field.dart index dea0aa1..9a979b7 100644 --- a/lib/views/home/widgets/search_field.dart +++ b/lib/views/home/widgets/search_field.dart @@ -125,6 +125,7 @@ class _SearchFieldState extends State { @override void dispose() { + widget.focusNode.removeListener(() {}); super.dispose(); } } diff --git a/lib/views/widgets/didvan/bnb.dart b/lib/views/widgets/didvan/bnb.dart index 5583482..8641ef5 100644 --- a/lib/views/widgets/didvan/bnb.dart +++ b/lib/views/widgets/didvan/bnb.dart @@ -118,134 +118,138 @@ class _PlayerNavBar extends StatelessWidget { ), ), alignment: Alignment.topCenter, - child: !_enablePlayerController(state) - ? const SizedBox() - : MediaService.currentPodcast == null - ? SizedBox( - height: 32, - child: Center( - child: SpinKitThreeBounce( - size: 18, + child: Builder(builder: (context) { + if (!_enablePlayerController(state)) return const SizedBox(); + if (state.appState == AppState.failed) { + Future.delayed(const Duration(seconds: 2), () { + MediaService.resetAudioPlayer(); + }); + return DidvanText( + 'اتصال اینترنت برقرار نمی‌باشد', + color: DesignConfig.isDark + ? Theme.of(context).colorScheme.title + : Theme.of(context).colorScheme.secondCTA, + ); + } + if (MediaService.currentPodcast == null) { + return SizedBox( + height: 32, + child: Center( + child: SpinKitThreeBounce( + size: 18, + color: DesignConfig.isDark + ? Theme.of(context).colorScheme.title + : Theme.of(context).colorScheme.secondCTA, + ), + ), + ); + } + return SizedBox( + height: 56, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only( + right: 12, + left: 16, + ), + child: DidvanIconButton( + icon: DidvanIcons.close_regular, + color: DesignConfig.isDark + ? null + : Theme.of(context).colorScheme.secondCTA, + gestureSize: 28, + onPressed: MediaService.resetAudioPlayer, + ), + ), + SkeletonImage( + imageUrl: MediaService.currentPodcast!.image, + width: 32, + height: 32, + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + DidvanText( + MediaService.currentPodcast!.title, color: DesignConfig.isDark - ? Theme.of(context).colorScheme.title + ? null : Theme.of(context).colorScheme.secondCTA, ), - ), - ) - : SizedBox( - height: 56, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only( - right: 12, - left: 16, - ), - child: DidvanIconButton( - icon: DidvanIcons.close_regular, + AudioSlider( + disableThumb: true, + tag: MediaService.audioPlayerTag!, + ), + ], + ), + ), + StreamBuilder( + stream: MediaService.audioPlayer.playerStateStream, + builder: (context, snapshot) { + final playerState = MediaService + .audioPlayer.playerState.processingState; + if (playerState == ProcessingState.loading || + state.appState == AppState.busy) { + return Padding( + padding: const EdgeInsets.only( + top: 4, + left: 16, + right: 16, + ), + child: SizedBox( + height: 18, + width: 18, + child: CircularProgressIndicator( + strokeWidth: 2, color: DesignConfig.isDark - ? null + ? Theme.of(context).colorScheme.title : Theme.of(context).colorScheme.secondCTA, - gestureSize: 28, - onPressed: MediaService.resetAudioPlayer, ), ), - SkeletonImage( - imageUrl: MediaService.currentPodcast!.image, - width: 32, - height: 32, - ), - const SizedBox(width: 16), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - DidvanText( - MediaService.currentPodcast!.title, - color: DesignConfig.isDark - ? null - : Theme.of(context) - .colorScheme - .secondCTA, - ), - AudioSlider( - disableThumb: true, - tag: MediaService.audioPlayerTag!, - ), - ], - ), - ), - StreamBuilder( - stream: - MediaService.audioPlayer.playerStateStream, - builder: (context, snapshot) { - final playerState = MediaService - .audioPlayer.playerState.processingState; - if (playerState == ProcessingState.loading || - state.appState == AppState.busy) { - return Padding( - padding: const EdgeInsets.only( - top: 4, - left: 16, - right: 16, - ), - child: SizedBox( - height: 18, - width: 18, - child: CircularProgressIndicator( - strokeWidth: 2, - color: DesignConfig.isDark - ? Theme.of(context) - .colorScheme - .title - : Theme.of(context) - .colorScheme - .secondCTA, - ), - ), - ); - } - return const SizedBox(); - }, - ), - if (state.appState != AppState.busy && - MediaService.audioPlayer.playerState - .processingState != - ProcessingState.loading) - Padding( - padding: const EdgeInsets.only( - left: 12, - right: 16, - ), - child: DidvanIconButton( - gestureSize: 28, - color: DesignConfig.isDark - ? null - : Theme.of(context).colorScheme.secondCTA, - icon: snapshot.data! - ? DidvanIcons.pause_solid - : DidvanIcons.play_solid, - onPressed: () { - if (state.args.type == 'video') { - state.getStudioDetails( - MediaService.currentPodcast!.id, - args: state.podcastArgs, - forceFetch: true, - ); - } - MediaService.handleAudioPlayback( - audioSource: - MediaService.currentPodcast!.media, - id: MediaService.currentPodcast!.id, - isVoiceMessage: false, - ); - }, - ), - ), - ], + ); + } + return const SizedBox(); + }, + ), + if (state.appState != AppState.busy && + MediaService.audioPlayer.playerState.processingState != + ProcessingState.loading) + Padding( + padding: const EdgeInsets.only( + left: 12, + right: 16, + ), + child: DidvanIconButton( + gestureSize: 28, + color: DesignConfig.isDark + ? null + : Theme.of(context).colorScheme.secondCTA, + icon: snapshot.data! + ? DidvanIcons.pause_solid + : DidvanIcons.play_solid, + onPressed: () { + if (state.args.type == 'video') { + state.getStudioDetails( + MediaService.currentPodcast!.id, + args: state.podcastArgs, + forceFetch: true, + ); + } + MediaService.handleAudioPlayback( + audioSource: MediaService.currentPodcast!.media, + id: MediaService.currentPodcast!.id, + isVoiceMessage: false, + ); + }, ), ), + ], + ), + ); + }), ), ), ), From 556f010e270b8adc7a422f4da99e851f84e39bad Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Thu, 31 Mar 2022 14:16:55 +0430 Subject: [PATCH 29/54] fcm splash bug fixed --- lib/providers/user.dart | 3 ++- lib/services/app_initalizer.dart | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/providers/user.dart b/lib/providers/user.dart index ec703e1..d87e0ce 100644 --- a/lib/providers/user.dart +++ b/lib/providers/user.dart @@ -36,7 +36,8 @@ class UserProvider extends CoreProvier { } if (service.isSuccess) { user = User.fromJson(service.result['user']); - await _registerFirebaseToken(); + AppInitializer.initializeFirebase().then((_) => _registerFirebaseToken()); + _registerFirebaseToken(); return true; } throw 'Getting user from API failed!'; diff --git a/lib/services/app_initalizer.dart b/lib/services/app_initalizer.dart index 78a3f4b..33c0170 100644 --- a/lib/services/app_initalizer.dart +++ b/lib/services/app_initalizer.dart @@ -15,7 +15,6 @@ class AppInitializer { StorageService.appDocsDir = (await getApplicationDocumentsDirectory()).path; StorageService.appTempsDir = (await getTemporaryDirectory()).path; - await _initializeFirebase(); MediaService.init(); } } @@ -62,7 +61,7 @@ class AppInitializer { } } - static Future _initializeFirebase() async { + static Future initializeFirebase() async { try { await Firebase.initializeApp( options: const FirebaseOptions( From 8c988412d2e6a550d30edfd13b9ac696f5ac2a4c Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Fri, 1 Apr 2022 16:58:05 +0430 Subject: [PATCH 30/54] studio description html parser added --- .../widgets/studio_details_widget.dart | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart index 60a9024..1248309 100644 --- a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart +++ b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart @@ -12,7 +12,9 @@ import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:didvan/views/widgets/state_handlers/state_handler.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_html/flutter_html.dart'; import 'package:provider/provider.dart'; +import 'package:url_launcher/url_launcher.dart'; class StudioDetailsWidget extends StatelessWidget { final ScrollController? scrollController; @@ -46,12 +48,33 @@ class StudioDetailsWidget extends StatelessWidget { builder: (context, state) { if (state.selectedDetailsIndex == 0) { return SingleChildScrollView( - padding: const EdgeInsets.symmetric(horizontal: 20), + // padding: const EdgeInsets.symmetric(horizontal: 20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - DidvanText(state.studio.description), + ConstrainedBox( + constraints: BoxConstraints( + maxHeight: ds.height - + ds.width * 9 / 16 - + 144 - + MediaQuery.of(context).padding.top, + ), + child: Html( + data: state.studio.description, + onAnchorTap: (href, context, map, element) => + launch(href!), + style: { + '*': Style( + direction: TextDirection.rtl, + textAlign: TextAlign.right, + lineHeight: LineHeight.percent(135), + margin: EdgeInsets.zero, + padding: EdgeInsets.zero, + ), + }, + ), + ), if (state.studio.tags.isNotEmpty) const SizedBox(height: 20), Wrap( From 64a0fca55dbb3d9dfc38510ed3e638cfb35203ee Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Fri, 1 Apr 2022 16:58:20 +0430 Subject: [PATCH 31/54] simpler image loader! --- lib/views/widgets/skeleton_image.dart | 28 +++++++++++---------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/lib/views/widgets/skeleton_image.dart b/lib/views/widgets/skeleton_image.dart index c59f431..0c16a7b 100644 --- a/lib/views/widgets/skeleton_image.dart +++ b/lib/views/widgets/skeleton_image.dart @@ -24,29 +24,23 @@ class SkeletonImage extends StatelessWidget { @override Widget build(BuildContext context) { return _aspectRatioGenerator( - child: CachedNetworkImage( - imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet, - httpHeaders: {'Authorization': 'Bearer ${RequestService.token}'}, - width: width, - height: height, - imageUrl: RequestHelper.baseUrl + imageUrl, - imageBuilder: (context, imageProvider) => ClipRRect( - borderRadius: borderRadius ?? DesignConfig.lowBorderRadius, - child: Image( - image: imageProvider, - fit: BoxFit.cover, - ), - ), - progressIndicatorBuilder: (context, url, progress) => - ShimmerPlaceholder( - borderRadius: borderRadius, + child: ClipRRect( + borderRadius: borderRadius, + child: CachedNetworkImage( + fit: BoxFit.cover, + imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet, + httpHeaders: {'Authorization': 'Bearer ${RequestService.token}'}, + width: width, + height: height, + imageUrl: RequestHelper.baseUrl + imageUrl, + placeholder: (context, _) => const ShimmerPlaceholder(), ), ), ); } Widget _aspectRatioGenerator({required Widget child}) => aspectRatio == null - ? SizedBox(key: ValueKey(imageUrl), child: child) + ? child : AspectRatio( key: ValueKey(imageUrl), aspectRatio: aspectRatio!, From a287496a9171baf827f3273e75750f2f82edba97 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Fri, 1 Apr 2022 17:58:44 +0430 Subject: [PATCH 32/54] duration widget alignment corrention --- lib/views/home/widgets/duration_widget.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/views/home/widgets/duration_widget.dart b/lib/views/home/widgets/duration_widget.dart index 025224d..b27f210 100644 --- a/lib/views/home/widgets/duration_widget.dart +++ b/lib/views/home/widgets/duration_widget.dart @@ -19,11 +19,10 @@ class DurationWidget extends StatelessWidget { borderRadius: BorderRadius.circular(5), ), child: Row( - crossAxisAlignment: CrossAxisAlignment.center, children: [ Icon( DidvanIcons.timer_regular, - size: 16, + size: 18, color: Theme.of(context).colorScheme.focusedBorder, ), const SizedBox(width: 4), @@ -32,12 +31,13 @@ class DurationWidget extends StatelessWidget { Duration(seconds: duration), ), isEnglishFont: true, + style: Theme.of(context).textTheme.caption, color: Theme.of(context).colorScheme.focusedBorder, ), const SizedBox(width: 4), Icon( DidvanIcons.play_circle_regular, - size: 16, + size: 18, color: Theme.of(context).colorScheme.focusedBorder, ), ], From c911698855ca7ecda903015e3c0e40247c54605c Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Fri, 1 Apr 2022 18:05:37 +0430 Subject: [PATCH 33/54] profile image compress quality increased --- lib/views/home/settings/profile/widgets/profile_photo.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/views/home/settings/profile/widgets/profile_photo.dart b/lib/views/home/settings/profile/widgets/profile_photo.dart index 400cfbf..00e1e01 100644 --- a/lib/views/home/settings/profile/widgets/profile_photo.dart +++ b/lib/views/home/settings/profile/widgets/profile_photo.dart @@ -149,7 +149,7 @@ class _ProfilePhotoState extends State { cancelButtonTitle: 'بازگشت', ), androidUiSettings: const AndroidUiSettings(toolbarTitle: 'برش تصویر'), - compressQuality: 70, + compressQuality: 30, ); if (file == null) return; } From f0bd19c0301aa2848b7c92d7616cab79776c98ac Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Sat, 2 Apr 2022 00:45:52 +0430 Subject: [PATCH 34/54] ready for v1.5 final release --- .fandogh/fandogh.yaml | 2 +- .../studio_details/studio_details_state.dart | 8 + .../widgets/studio_details_widget.dart | 195 ++++++++++-------- lib/views/splash/splash.dart | 5 +- lib/views/widgets/didvan/bnb.dart | 2 + pubspec.yaml | 2 +- web/index.html | 2 +- 7 files changed, 125 insertions(+), 91 deletions(-) diff --git a/.fandogh/fandogh.yaml b/.fandogh/fandogh.yaml index d7044ce..d089a85 100644 --- a/.fandogh/fandogh.yaml +++ b/.fandogh/fandogh.yaml @@ -3,7 +3,7 @@ name: app-dev spec: allow_http: false disable_default_domains: true - image: app-dev:1.5.0 + image: app-dev:1.5.4 image_pull_policy: IfNotPresent path: / replicas: 1 diff --git a/lib/views/home/studio/studio_details/studio_details_state.dart b/lib/views/home/studio/studio_details/studio_details_state.dart index 062ceec..bf7f5c8 100644 --- a/lib/views/home/studio/studio_details/studio_details_state.dart +++ b/lib/views/home/studio/studio_details/studio_details_state.dart @@ -18,6 +18,7 @@ class StudioDetailsState extends CoreProvier { StudioRequestArgs? podcastArgs; final List relatedQueue = []; bool _positionListenerActivated = false; + AppState alongSideState = AppState.idle; int _selectedDetailsIndex = 0; Timer? timer; @@ -69,6 +70,9 @@ class StudioDetailsState extends CoreProvier { MediaService.audioPlayerTag = 'podcast'; } appState = AppState.busy; + } else { + alongSideState = AppState.busy; + notifyListeners(); } final service = RequestService(RequestHelper.studioDetails(id, this.args)); await service.httpGet(); @@ -90,11 +94,15 @@ class StudioDetailsState extends CoreProvier { if (isForward == null && !forceFetch) { await _handlePodcastPlayback(studio); } + alongSideState = AppState.idle; appState = AppState.idle; return; } if (isForward == null) { appState = AppState.failed; + } else { + alongSideState = AppState.failed; + notifyListeners(); } } diff --git a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart index 1248309..ebc9d4f 100644 --- a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart +++ b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart @@ -1,6 +1,7 @@ import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; +import 'package:didvan/models/enums.dart'; import 'package:didvan/models/studio_details_data.dart'; import 'package:didvan/views/home/comments/comments.dart'; import 'package:didvan/views/home/comments/comments_state.dart'; @@ -9,6 +10,7 @@ import 'package:didvan/views/home/studio/studio_details/widgets/details_tab_bar. import 'package:didvan/views/home/widgets/overview/multitype.dart'; import 'package:didvan/views/home/widgets/tag_item.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; +import 'package:didvan/views/widgets/shimmer_placeholder.dart'; import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:didvan/views/widgets/state_handlers/state_handler.dart'; import 'package:flutter/material.dart'; @@ -42,25 +44,26 @@ class StudioDetailsWidget extends StatelessWidget { onCommentsTabSelected: onCommentsTabSelected ?? () {}, ), const SizedBox(height: 16), - StateHandler( - onRetry: () {}, - state: state, - builder: (context, state) { - if (state.selectedDetailsIndex == 0) { - return SingleChildScrollView( - // padding: const EdgeInsets.symmetric(horizontal: 20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - ConstrainedBox( - constraints: BoxConstraints( - maxHeight: ds.height - - ds.width * 9 / 16 - - 144 - - MediaQuery.of(context).padding.top, - ), - child: Html( + ConstrainedBox( + constraints: BoxConstraints( + maxHeight: ds.height - + ds.width * 9 / 16 - + 144 - + MediaQuery.of(context).padding.top, + ), + child: StateHandler( + onRetry: () {}, + state: state, + builder: (context, state) { + if (state.selectedDetailsIndex == 0) { + return SingleChildScrollView( + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Html( data: state.studio.description, onAnchorTap: (href, context, map, element) => launch(href!), @@ -74,49 +77,51 @@ class StudioDetailsWidget extends StatelessWidget { ), }, ), - ), - if (state.studio.tags.isNotEmpty) + if (state.studio.tags.isNotEmpty) + const SizedBox(height: 20), + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + for (var i = 0; + i < state.studio.tags.length; + i++) + TagItem(tag: state.studio.tags[i]), + ], + ), const SizedBox(height: 20), - Wrap( - spacing: 8, - runSpacing: 8, - children: [ - for (var i = 0; i < state.studio.tags.length; i++) - TagItem(tag: state.studio.tags[i]), - ], - ), - const SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const SizedBox(), - if (state.nextStudio != null) - _StudioPreview( - isNext: true, - studio: state.nextStudio!, - scrollController: scrollController, - ), - if (state.prevStudio != null) - _StudioPreview( - isNext: false, - studio: state.prevStudio!, - scrollController: scrollController, - ), - const SizedBox(), - ], - ) - ], - ), - ); - } - if (state.selectedDetailsIndex == 1) { - return ChangeNotifierProvider( - create: (context) => CommentsState(), - child: SizedBox( - height: ds.height - - ds.width * 9 / 16 - - 144 - - MediaQuery.of(context).padding.top, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const SizedBox(), + if (state.nextStudio != null && + state.alongSideState == AppState.idle) + _StudioPreview( + isNext: true, + studio: state.nextStudio!, + scrollController: scrollController, + ), + if (state.alongSideState == AppState.busy) + _StudioPreview.placeHolder, + if (state.prevStudio != null && + state.alongSideState == AppState.idle) + _StudioPreview( + isNext: false, + studio: state.prevStudio!, + scrollController: scrollController, + ), + if (state.alongSideState == AppState.busy) + _StudioPreview.placeHolder, + const SizedBox(), + ], + ), + ], + ), + ); + } + if (state.selectedDetailsIndex == 1) { + return ChangeNotifierProvider( + create: (context) => CommentsState(), child: Comments( pageData: { 'id': state.studio.id, @@ -126,38 +131,38 @@ class StudioDetailsWidget extends StatelessWidget { 'isPage': false, }, ), - ), - ); - } - return Column( - children: [ - if (state.studio.relatedContents.isEmpty) - for (var i = 0; i < 3; i++) + ); + } + return Column( + children: [ + if (state.studio.relatedContents.isEmpty) + for (var i = 0; i < 3; i++) + Padding( + padding: const EdgeInsets.only( + bottom: 8, + left: 16, + right: 16, + ), + child: MultitypeOverview.placeholder, + ), + for (var i = 0; + i < state.studio.relatedContents.length; + i++) Padding( padding: const EdgeInsets.only( bottom: 8, left: 16, right: 16, ), - child: MultitypeOverview.placeholder, + child: MultitypeOverview( + item: state.studio.relatedContents[i], + onMarkChanged: (id, value) {}, + ), ), - for (var i = 0; - i < state.studio.relatedContents.length; - i++) - Padding( - padding: const EdgeInsets.only( - bottom: 8, - left: 16, - right: 16, - ), - child: MultitypeOverview( - item: state.studio.relatedContents[i], - onMarkChanged: (id, value) {}, - ), - ), - ], - ); - }, + ], + ); + }, + ), ), ], ), @@ -237,4 +242,22 @@ class _StudioPreview extends StatelessWidget { ), ); } + + static Widget get placeHolder => SizedBox( + width: 88, + height: 216, + child: Column( + children: const [ + ShimmerPlaceholder(width: 88, height: 88), + SizedBox(height: 8), + ShimmerPlaceholder(height: 20, width: 20), + SizedBox(height: 16), + ShimmerPlaceholder(height: 14, width: 60), + SizedBox(height: 16), + ShimmerPlaceholder(height: 12, width: double.infinity), + SizedBox(height: 8), + ShimmerPlaceholder(height: 12, width: 40), + ], + ), + ); } diff --git a/lib/views/splash/splash.dart b/lib/views/splash/splash.dart index 6626557..b06a114 100644 --- a/lib/views/splash/splash.dart +++ b/lib/views/splash/splash.dart @@ -19,6 +19,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:provider/provider.dart'; import 'package:universal_html/html.dart' as html; +import 'package:universal_html/js.dart' as js; class Splash extends StatefulWidget { const Splash({Key? key}) : super(key: key); @@ -50,8 +51,8 @@ class _SplashState extends State { value: DesignConfig.systemUiOverlayStyle.copyWith( systemNavigationBarColor: Theme.of(context).colorScheme.background, ), - child: Scaffold( - body: Container( + child: Material( + child: Container( alignment: Alignment.center, padding: const EdgeInsets.all(60), color: Theme.of(context).colorScheme.background, diff --git a/lib/views/widgets/didvan/bnb.dart b/lib/views/widgets/didvan/bnb.dart index 8641ef5..002dea5 100644 --- a/lib/views/widgets/didvan/bnb.dart +++ b/lib/views/widgets/didvan/bnb.dart @@ -175,6 +175,8 @@ class _PlayerNavBar extends StatelessWidget { children: [ DidvanText( MediaService.currentPodcast!.title, + maxLines: 1, + overflow: TextOverflow.ellipsis, color: DesignConfig.isDark ? null : Theme.of(context).colorScheme.secondCTA, diff --git a/pubspec.yaml b/pubspec.yaml index 4f32cce..d54e6b4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -87,7 +87,7 @@ flutter: # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. - uses-material-design: true + uses-material-design: false # To add assets to your application, add an assets section, like this: assets: diff --git a/web/index.html b/web/index.html index a2dfb65..b00093f 100644 --- a/web/index.html +++ b/web/index.html @@ -60,7 +60,7 @@ } scriptLoaded = true; var scriptTag = document.createElement('script'); - scriptTag.src = 'main.dart.js'; + scriptTag.src = `main.dart.js?version=${Math.random()}`; scriptTag.type = 'application/javascript'; document.body.append(scriptTag); } From d7a39dda263b53e2d3f753bebcd54bb6cffa3860 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Sat, 2 Apr 2022 04:39:43 +0430 Subject: [PATCH 35/54] build error fixed --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index d54e6b4..4f32cce 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -87,7 +87,7 @@ flutter: # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. - uses-material-design: false + uses-material-design: true # To add assets to your application, add an assets section, like this: assets: From 8666a6a7409849b2777047bdf4f6871853a4f18e Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Sat, 2 Apr 2022 15:44:02 +0430 Subject: [PATCH 36/54] bug fixes --- lib/views/home/hashtag/hashtag.dart | 42 +++++++++++++------ lib/views/home/hashtag/hashtag_state.dart | 2 - .../widgets/studio_details_widget.dart | 1 + lib/views/home/studio/widgets/slider.dart | 18 ++++++++ lib/views/home/widgets/overview/video.dart | 35 +++++++--------- 5 files changed, 64 insertions(+), 34 deletions(-) diff --git a/lib/views/home/hashtag/hashtag.dart b/lib/views/home/hashtag/hashtag.dart index 24a9a7c..83ee9d4 100644 --- a/lib/views/home/hashtag/hashtag.dart +++ b/lib/views/home/hashtag/hashtag.dart @@ -1,8 +1,11 @@ +import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/models/tag.dart'; import 'package:didvan/models/view/app_bar_data.dart'; import 'package:didvan/views/home/hashtag/hashtag_state.dart'; import 'package:didvan/views/home/widgets/overview/news.dart'; +import 'package:didvan/views/home/widgets/overview/podcast.dart'; import 'package:didvan/views/home/widgets/overview/radar.dart'; +import 'package:didvan/views/home/widgets/overview/video.dart'; import 'package:didvan/views/widgets/didvan/scaffold.dart'; import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart'; import 'package:flutter/material.dart'; @@ -50,19 +53,34 @@ class _HashtagState extends State { } final item = state.items[index]; final type = item.type; - if (type == 'radar') { - return RadarOverview( - radar: item, - onCommentsChanged: (id, count) => item.comments = count, - onMarkChanged: (id, value) => item.marked = value, - ); - } else if (type == 'news') { - return NewsOverview( - news: item, - onMarkChanged: (id, value) => item.marked = value, - ); + switch (type) { + case 'radar': + return RadarOverview( + radar: item, + onCommentsChanged: (_, count) => item.comments = count, + onMarkChanged: (_, value) => item.marked = value, + ); + case 'news': + return NewsOverview( + news: item, + onMarkChanged: (_, value) => item.marked = value, + ); + case 'podcast': + return PodcastOverview( + podcast: item, + onMarkChanged: (_, value) => item.marked = value, + studioRequestArgs: + const StudioRequestArgs(page: 0, type: 'podcast'), + ); + case 'video': + return VideoOverview( + video: item, + onMarkChanged: (_, value) => item.marked = value, + studioRequestArgs: + const StudioRequestArgs(page: 0, type: 'video'), + ); } - return Container(); + return const SizedBox(); }, childCount: state.items.length + (state.page != state.lastPage ? 1 : 0), diff --git a/lib/views/home/hashtag/hashtag_state.dart b/lib/views/home/hashtag/hashtag_state.dart index c71acfe..0c41280 100644 --- a/lib/views/home/hashtag/hashtag_state.dart +++ b/lib/views/home/hashtag/hashtag_state.dart @@ -18,8 +18,6 @@ class HashtagState extends CoreProvier { } final service = RequestService(RequestHelper.tag( ids: [id], - itemId: 1, - type: 'radar', limit: 15, page: page, )); diff --git a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart index ebc9d4f..c02c0a4 100644 --- a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart +++ b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart @@ -64,6 +64,7 @@ class StudioDetailsWidget extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ Html( + key: ValueKey(state.studio.id), data: state.studio.description, onAnchorTap: (href, context, map, element) => launch(href!), diff --git a/lib/views/home/studio/widgets/slider.dart b/lib/views/home/studio/widgets/slider.dart index 7b6339f..8f80c6a 100644 --- a/lib/views/home/studio/widgets/slider.dart +++ b/lib/views/home/studio/widgets/slider.dart @@ -1,6 +1,7 @@ import 'package:carousel_slider/carousel_slider.dart'; import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; +import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/enums.dart'; import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/routes/routes.dart'; @@ -61,6 +62,7 @@ class _StudioSliderState extends State { ); }, child: Stack( + alignment: Alignment.center, children: [ SkeletonImage( borderRadius: DesignConfig.mediumBorderRadius, @@ -95,6 +97,22 @@ class _StudioSliderState extends State { ), ), ), + Container( + height: 52, + width: 52, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Theme.of(context) + .colorScheme + .secondary + .withOpacity(0.7), + ), + child: Icon( + DidvanIcons.play_solid, + color: Theme.of(context).colorScheme.white, + size: 48, + ), + ), ], ), ), diff --git a/lib/views/home/widgets/overview/video.dart b/lib/views/home/widgets/overview/video.dart index f88c842..8ceb648 100644 --- a/lib/views/home/widgets/overview/video.dart +++ b/lib/views/home/widgets/overview/video.dart @@ -17,13 +17,13 @@ class VideoOverview extends StatelessWidget { final OverviewData video; final void Function(int id, bool value) onMarkChanged; final bool hasUnmarkConfirmation; - final StudioRequestArgs? studioRequestArgs; + final StudioRequestArgs studioRequestArgs; const VideoOverview({ Key? key, required this.video, required this.onMarkChanged, - required this.hasUnmarkConfirmation, - this.studioRequestArgs, + required this.studioRequestArgs, + this.hasUnmarkConfirmation = false, }) : super(key: key); @override @@ -42,29 +42,24 @@ class VideoOverview extends StatelessWidget { child: Row( children: [ Stack( + alignment: Alignment.center, children: [ SkeletonImage( imageUrl: video.image, height: 108, width: 108, ), - Positioned.fill( - child: Center( - child: Container( - height: 28, - width: 28, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Theme.of(context) - .colorScheme - .secondary - .withOpacity(0.7), - ), - child: Icon( - DidvanIcons.play_solid, - color: Theme.of(context).colorScheme.white, - ), - ), + Container( + height: 28, + width: 28, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: + Theme.of(context).colorScheme.secondary.withOpacity(0.7), + ), + child: Icon( + DidvanIcons.play_solid, + color: Theme.of(context).colorScheme.white, ), ), ], From 56a885a6eeaee2807c17733d05ab69470a7a0eea Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Sat, 2 Apr 2022 16:40:51 +0430 Subject: [PATCH 37/54] bug fixes --- .fandogh/fandogh.yaml | 2 +- lib/views/home/hashtag/hashtag.dart | 8 +++---- lib/views/home/news/news.dart | 2 +- lib/views/home/news/news_state.dart | 8 +++---- lib/views/home/radar/radar.dart | 2 +- lib/views/home/radar/radar_state.dart | 8 +++---- .../settings/bookmarks/bookmark_state.dart | 11 ---------- .../filtered_bookmark/filtered_bookmark.dart | 2 +- .../filtered_bookmarks_state.dart | 8 ------- .../studio_details/studio_details.mobile.dart | 8 +++++-- .../studio_details/studio_details.web.dart | 2 ++ lib/views/home/studio/studio_state.dart | 8 +++---- .../widgets/audio/audio_player_widget.dart | 4 +++- lib/views/home/widgets/bookmark_button.dart | 22 ++++++++++++++++++- .../home/widgets/floating_navigation_bar.dart | 4 ++++ lib/views/home/widgets/overview/news.dart | 8 ++++--- lib/views/home/widgets/overview/podcast.dart | 7 ++++-- lib/views/home/widgets/overview/radar.dart | 8 ++++--- lib/views/home/widgets/overview/video.dart | 9 +++++--- lib/views/splash/splash.dart | 1 - 20 files changed, 77 insertions(+), 55 deletions(-) diff --git a/.fandogh/fandogh.yaml b/.fandogh/fandogh.yaml index d089a85..2de4a83 100644 --- a/.fandogh/fandogh.yaml +++ b/.fandogh/fandogh.yaml @@ -3,7 +3,7 @@ name: app-dev spec: allow_http: false disable_default_domains: true - image: app-dev:1.5.4 + image: app-dev:1.5.10 image_pull_policy: IfNotPresent path: / replicas: 1 diff --git a/lib/views/home/hashtag/hashtag.dart b/lib/views/home/hashtag/hashtag.dart index 83ee9d4..91b2060 100644 --- a/lib/views/home/hashtag/hashtag.dart +++ b/lib/views/home/hashtag/hashtag.dart @@ -58,24 +58,24 @@ class _HashtagState extends State { return RadarOverview( radar: item, onCommentsChanged: (_, count) => item.comments = count, - onMarkChanged: (_, value) => item.marked = value, + onMarkChanged: (_, value, __) => item.marked = value, ); case 'news': return NewsOverview( news: item, - onMarkChanged: (_, value) => item.marked = value, + onMarkChanged: (_, value, __) => item.marked = value, ); case 'podcast': return PodcastOverview( podcast: item, - onMarkChanged: (_, value) => item.marked = value, + onMarkChanged: (_, value, __) => item.marked = value, studioRequestArgs: const StudioRequestArgs(page: 0, type: 'podcast'), ); case 'video': return VideoOverview( video: item, - onMarkChanged: (_, value) => item.marked = value, + onMarkChanged: (_, value, __) => item.marked = value, studioRequestArgs: const StudioRequestArgs(page: 0, type: 'video'), ); diff --git a/lib/views/home/news/news.dart b/lib/views/home/news/news.dart index 421a045..9387dae 100644 --- a/lib/views/home/news/news.dart +++ b/lib/views/home/news/news.dart @@ -66,7 +66,7 @@ class _NewsState extends State { final news = state.news[index]; return NewsOverview( news: news, - onMarkChanged: (id, value) => state.onMarkChanged(id, value), + onMarkChanged: state.onMarkChanged, newsRequestArgs: NewsRequestArgs( page: state.page, endDate: state.endDate, diff --git a/lib/views/home/news/news_state.dart b/lib/views/home/news/news_state.dart index 29b8221..b022538 100644 --- a/lib/views/home/news/news_state.dart +++ b/lib/views/home/news/news_state.dart @@ -2,7 +2,6 @@ import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/requests/news.dart'; import 'package:didvan/providers/core.dart'; -import 'package:didvan/providers/user.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; @@ -69,10 +68,11 @@ class NewsState extends CoreProvier { appState = AppState.failed; } - Future onMarkChanged(int id, bool value) async { + Future onMarkChanged(int id, bool value, bool shouldUpdate) async { news.firstWhere((element) => element.id == id).marked = value; - notifyListeners(); - UserProvider.changeNewsMark(id, value); + if (shouldUpdate) { + notifyListeners(); + } } bool get isFiltering => startDate != null || endDate != null; diff --git a/lib/views/home/radar/radar.dart b/lib/views/home/radar/radar.dart index 6058543..6384c96 100644 --- a/lib/views/home/radar/radar.dart +++ b/lib/views/home/radar/radar.dart @@ -139,7 +139,7 @@ class _RadarState extends State { final radar = state.radars[index]; return RadarOverview( radar: radar, - onMarkChanged: (id, value) => state.changeMark(id, value), + onMarkChanged: state.changeMark, onCommentsChanged: (id, count) => state.onCommentsChanged(id, count), radarRequestArgs: RadarRequestArgs( diff --git a/lib/views/home/radar/radar_state.dart b/lib/views/home/radar/radar_state.dart index 1b3e319..fb293a2 100644 --- a/lib/views/home/radar/radar_state.dart +++ b/lib/views/home/radar/radar_state.dart @@ -4,7 +4,6 @@ import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/requests/radar.dart'; import 'package:didvan/models/view/radar_category.dart'; import 'package:didvan/providers/core.dart'; -import 'package:didvan/providers/user.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; @@ -82,10 +81,11 @@ class RadarState extends CoreProvier { appState = AppState.failed; } - Future changeMark(int id, bool value) async { + Future changeMark(int id, bool value, bool shouldUpdate) async { radars.firstWhere((element) => element.id == id).marked = value; - notifyListeners(); - UserProvider.changeRadarMark(id, value); + if (shouldUpdate) { + notifyListeners(); + } } void onCommentsChanged(int id, int count) { diff --git a/lib/views/home/settings/bookmarks/bookmark_state.dart b/lib/views/home/settings/bookmarks/bookmark_state.dart index aa29d9c..85d5a44 100644 --- a/lib/views/home/settings/bookmarks/bookmark_state.dart +++ b/lib/views/home/settings/bookmarks/bookmark_state.dart @@ -1,7 +1,6 @@ import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; import 'package:didvan/providers/core.dart'; -import 'package:didvan/providers/user.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; @@ -41,16 +40,6 @@ class BookmarksState extends CoreProvier { void onMarkChanged(int id, bool value) { if (value) return; - final type = bookmarks.firstWhere((element) => element.id == id).type; - switch (type) { - case 'radar': - UserProvider.changeRadarMark(id, value); - break; - case 'news': - UserProvider.changeNewsMark(id, value); - break; - default: - } bookmarks.removeWhere((element) => element.id == id); notifyListeners(); } diff --git a/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmark.dart b/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmark.dart index bc553e4..0ecc547 100644 --- a/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmark.dart +++ b/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmark.dart @@ -103,7 +103,7 @@ class _FilteredBookmarksState extends State { ); } - Future _onBookmarkChanged(int id, bool value) async { + Future _onBookmarkChanged(int id, bool value, bool shouldUpdate) async { if (value) return; final state = context.read(); state.onMarkChanged(id, false); diff --git a/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart b/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart index b275054..0940883 100644 --- a/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart +++ b/lib/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart @@ -1,7 +1,6 @@ import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; import 'package:didvan/providers/core.dart'; -import 'package:didvan/providers/user.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; @@ -45,13 +44,6 @@ class FilteredBookmarksState extends CoreProvier { } void onMarkChanged(int id, bool value) { - if (type == 'radar') { - UserProvider.changeRadarMark(id, value); - } else if (type == 'news') { - UserProvider.changeNewsMark(id, value); - } else { - UserProvider.changeStudioMark(id, value); - } bookmarks.removeWhere((element) => element.id == id); notifyListeners(); } diff --git a/lib/views/home/studio/studio_details/studio_details.mobile.dart b/lib/views/home/studio/studio_details/studio_details.mobile.dart index 2331fb1..b43ff6a 100644 --- a/lib/views/home/studio/studio_details/studio_details.mobile.dart +++ b/lib/views/home/studio/studio_details/studio_details.mobile.dart @@ -114,9 +114,13 @@ class _StudioDetailsState extends State { ? null : AppBarData( trailing: BookmarkButton( + itemId: state.studio.id, + type: 'video', value: state.studio.marked, - onMarkChanged: (value) => widget - .pageData['onMarkChanged'](state.studio.id, value), + onMarkChanged: (value) { + widget.pageData['onMarkChanged']( + state.studio.id, value); + }, gestureSize: 48, ), isSmall: true, diff --git a/lib/views/home/studio/studio_details/studio_details.web.dart b/lib/views/home/studio/studio_details/studio_details.web.dart index 7dd83af..38a2579 100644 --- a/lib/views/home/studio/studio_details/studio_details.web.dart +++ b/lib/views/home/studio/studio_details/studio_details.web.dart @@ -108,6 +108,8 @@ class _StudioDetailsState extends State { isSmall: true, title: state.studio.title, trailing: BookmarkButton( + itemId: state.studio.id, + type: 'video', value: state.studio.marked, onMarkChanged: (value) => widget .pageData['onMarkChanged'](state.studio.id, value), diff --git a/lib/views/home/studio/studio_state.dart b/lib/views/home/studio/studio_state.dart index afab668..bb5b65a 100644 --- a/lib/views/home/studio/studio_state.dart +++ b/lib/views/home/studio/studio_state.dart @@ -5,7 +5,6 @@ import 'package:didvan/models/overview_data.dart'; import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/models/slider_data.dart'; import 'package:didvan/providers/core.dart'; -import 'package:didvan/providers/user.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; @@ -111,10 +110,11 @@ class StudioState extends CoreProvier { appState = AppState.failed; } - Future changeMark(int id, bool value) async { + Future changeMark(int id, bool value, bool shouldUpdate) async { studios.firstWhere((element) => element.id == id).marked = value; - notifyListeners(); - UserProvider.changeStudioMark(id, value); + if (shouldUpdate) { + notifyListeners(); + } } void onCommentsChanged(int id, int count) { diff --git a/lib/views/home/widgets/audio/audio_player_widget.dart b/lib/views/home/widgets/audio/audio_player_widget.dart index c289a96..0b95eba 100644 --- a/lib/views/home/widgets/audio/audio_player_widget.dart +++ b/lib/views/home/widgets/audio/audio_player_widget.dart @@ -168,12 +168,14 @@ class AudioPlayerWidget extends StatelessWidget { Expanded( child: Center( child: BookmarkButton( + itemId: state.studio.id, + type: 'podcast', gestureSize: 48, color: Theme.of(context).colorScheme.title, value: podcast.marked, onMarkChanged: (value) => context .read() - .changeMark(podcast.id, value), + .changeMark(podcast.id, value, true), ), ), ), diff --git a/lib/views/home/widgets/bookmark_button.dart b/lib/views/home/widgets/bookmark_button.dart index 2387ca0..79939d5 100644 --- a/lib/views/home/widgets/bookmark_button.dart +++ b/lib/views/home/widgets/bookmark_button.dart @@ -1,6 +1,7 @@ import 'package:didvan/config/design_config.dart'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/view/action_sheet_data.dart'; +import 'package:didvan/providers/user.dart'; import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/views/widgets/didvan/icon_button.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; @@ -12,12 +13,16 @@ class BookmarkButton extends StatefulWidget { final void Function(bool value) onMarkChanged; final bool askForConfirmation; final double gestureSize; + final String type; + final int itemId; const BookmarkButton({ Key? key, required this.value, required this.onMarkChanged, - this.askForConfirmation = false, required this.gestureSize, + required this.type, + required this.itemId, + this.askForConfirmation = false, this.color, }) : super(key: key); @@ -69,6 +74,21 @@ class _BookmarkButtonState extends State { _value = !_value; }); widget.onMarkChanged(_value); + switch (widget.type) { + case 'radar': + UserProvider.changeRadarMark(widget.itemId, _value); + break; + case 'news': + UserProvider.changeNewsMark(widget.itemId, _value); + break; + case 'podcast': + UserProvider.changeStudioMark(widget.itemId, _value); + break; + case 'video': + UserProvider.changeStudioMark(widget.itemId, _value); + break; + default: + } } }, ); diff --git a/lib/views/home/widgets/floating_navigation_bar.dart b/lib/views/home/widgets/floating_navigation_bar.dart index aac4297..754e40d 100644 --- a/lib/views/home/widgets/floating_navigation_bar.dart +++ b/lib/views/home/widgets/floating_navigation_bar.dart @@ -104,6 +104,8 @@ class _FloatingNavigationBarState extends State { const Spacer(), if (widget.isRadar) BookmarkButton( + itemId: widget.item.id, + type: 'radar', color: DesignConfig.isDark ? Theme.of(context).colorScheme.focusedBorder : Theme.of(context).colorScheme.focused, @@ -146,6 +148,8 @@ class _FloatingNavigationBarState extends State { if (!widget.isRadar) const SizedBox(width: 12), if (!widget.isRadar) BookmarkButton( + itemId: widget.item.id, + type: 'news', color: DesignConfig.isDark ? Theme.of(context).colorScheme.focusedBorder : Theme.of(context).colorScheme.focused, diff --git a/lib/views/home/widgets/overview/news.dart b/lib/views/home/widgets/overview/news.dart index ee22add..6a8db98 100644 --- a/lib/views/home/widgets/overview/news.dart +++ b/lib/views/home/widgets/overview/news.dart @@ -13,7 +13,7 @@ import 'package:flutter/material.dart'; class NewsOverview extends StatelessWidget { final OverviewData news; final NewsRequestArgs? newsRequestArgs; - final void Function(int id, bool value) onMarkChanged; + final void Function(int id, bool value, bool shouldUpdate) onMarkChanged; final bool hasUnmarkConfirmation; const NewsOverview({ Key? key, @@ -29,7 +29,7 @@ class NewsOverview extends StatelessWidget { onTap: () => Navigator.of(context).pushNamed( Routes.newsDetails, arguments: { - 'onMarkChanged': onMarkChanged, + 'onMarkChanged': (id, value) => onMarkChanged(id, value, true), 'id': news.id, 'args': newsRequestArgs, 'hasUnmarkConfirmation': hasUnmarkConfirmation, @@ -79,9 +79,11 @@ class NewsOverview extends StatelessWidget { ], ), BookmarkButton( + itemId: news.id, + type: 'news', gestureSize: 32, value: news.marked, - onMarkChanged: (value) => onMarkChanged(news.id, value), + onMarkChanged: (value) => onMarkChanged(news.id, value, false), askForConfirmation: hasUnmarkConfirmation, ), ], diff --git a/lib/views/home/widgets/overview/podcast.dart b/lib/views/home/widgets/overview/podcast.dart index d517dcc..94cd330 100644 --- a/lib/views/home/widgets/overview/podcast.dart +++ b/lib/views/home/widgets/overview/podcast.dart @@ -20,7 +20,7 @@ import 'package:provider/provider.dart'; class PodcastOverview extends StatelessWidget { final OverviewData podcast; - final void Function(int id, bool value) onMarkChanged; + final void Function(int id, bool value, bool shouldUpdate) onMarkChanged; final StudioRequestArgs studioRequestArgs; final bool hasUnmarkConfirmation; const PodcastOverview({ @@ -112,10 +112,13 @@ class PodcastOverview extends StatelessWidget { const SizedBox(width: 16), ], BookmarkButton( + itemId: podcast.id, + type: 'podcast', askForConfirmation: hasUnmarkConfirmation, gestureSize: 32, value: podcast.marked, - onMarkChanged: (value) => onMarkChanged(podcast.id, value), + onMarkChanged: (value) => + onMarkChanged(podcast.id, value, false), ), ], ), diff --git a/lib/views/home/widgets/overview/radar.dart b/lib/views/home/widgets/overview/radar.dart index 856868d..3d0e6ff 100644 --- a/lib/views/home/widgets/overview/radar.dart +++ b/lib/views/home/widgets/overview/radar.dart @@ -16,7 +16,7 @@ import 'package:flutter/material.dart'; class RadarOverview extends StatelessWidget { final OverviewData radar; final void Function(int id, int count) onCommentsChanged; - final void Function(int id, bool value) onMarkChanged; + final void Function(int id, bool value, bool shouldUpdate) onMarkChanged; final bool hasUnmarkConfirmation; final RadarRequestArgs? radarRequestArgs; const RadarOverview({ @@ -34,7 +34,7 @@ class RadarOverview extends StatelessWidget { onTap: () => Navigator.of(context).pushNamed( Routes.radarDetails, arguments: { - 'onMarkChanged': onMarkChanged, + 'onMarkChanged': (id, value) => onMarkChanged(id, value, true), 'onCommentsChanged': onCommentsChanged, 'id': radar.id, 'args': radarRequestArgs, @@ -124,9 +124,11 @@ class RadarOverview extends StatelessWidget { // const Icon(DidvanIcons.evaluation_regular), const Spacer(), BookmarkButton( + itemId: radar.id, + type: 'radar', gestureSize: 32, value: radar.marked, - onMarkChanged: (value) => onMarkChanged(radar.id, value), + onMarkChanged: (value) => onMarkChanged(radar.id, value, false), askForConfirmation: hasUnmarkConfirmation, ), ], diff --git a/lib/views/home/widgets/overview/video.dart b/lib/views/home/widgets/overview/video.dart index 8ceb648..b70918b 100644 --- a/lib/views/home/widgets/overview/video.dart +++ b/lib/views/home/widgets/overview/video.dart @@ -15,7 +15,7 @@ import 'package:flutter/material.dart'; class VideoOverview extends StatelessWidget { final OverviewData video; - final void Function(int id, bool value) onMarkChanged; + final void Function(int id, bool value, bool shouldUpdate) onMarkChanged; final bool hasUnmarkConfirmation; final StudioRequestArgs studioRequestArgs; const VideoOverview({ @@ -32,7 +32,7 @@ class VideoOverview extends StatelessWidget { onTap: () => Navigator.of(context).pushNamed( Routes.studioDetails, arguments: { - 'onMarkChanged': onMarkChanged, + 'onMarkChanged': (id, value) => onMarkChanged(id, value, true), 'id': video.id, 'args': studioRequestArgs, 'hasUnmarkConfirmation': hasUnmarkConfirmation, @@ -101,9 +101,12 @@ class VideoOverview extends StatelessWidget { // ), // const SizedBox(width: 16), BookmarkButton( + itemId: video.id, + type: 'video', gestureSize: 32, value: video.marked, - onMarkChanged: (value) => onMarkChanged(video.id, value), + onMarkChanged: (value) => + onMarkChanged(video.id, value, false), askForConfirmation: hasUnmarkConfirmation, ), ], diff --git a/lib/views/splash/splash.dart b/lib/views/splash/splash.dart index b06a114..27943c1 100644 --- a/lib/views/splash/splash.dart +++ b/lib/views/splash/splash.dart @@ -19,7 +19,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:provider/provider.dart'; import 'package:universal_html/html.dart' as html; -import 'package:universal_html/js.dart' as js; class Splash extends StatefulWidget { const Splash({Key? key}) : super(key: key); From b343541200fa8a3aad7b5c54a91de2776058d558 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Sat, 2 Apr 2022 17:06:54 +0430 Subject: [PATCH 38/54] bug fixes --- lib/routes/route_generator.dart | 3 +- lib/views/home/hashtag/hashtag.dart | 33 ++++++++++++++----- .../home/news/news_details/news_details.dart | 2 ++ .../radar/radar_details/radar_details.dart | 2 ++ .../studio_details/studio_details.mobile.dart | 1 + .../studio_details/studio_details.web.dart | 1 + .../widgets/studio_details_widget.dart | 8 ++++- lib/views/home/widgets/tag_item.dart | 12 +++++-- lib/views/widgets/didvan/bnb.dart | 2 ++ lib/views/widgets/didvan/page_view.dart | 8 ++++- 10 files changed, 59 insertions(+), 13 deletions(-) diff --git a/lib/routes/route_generator.dart b/lib/routes/route_generator.dart index 2716aaf..43bfd51 100644 --- a/lib/routes/route_generator.dart +++ b/lib/routes/route_generator.dart @@ -142,7 +142,8 @@ class RouteGenerator { return _createRoute( ChangeNotifierProvider( create: (context) => HashtagState(), - child: Hashtag(tag: settings.arguments as Tag), + child: + Hashtag(pageData: settings.arguments as Map), ), ); case Routes.filteredBookmarks: diff --git a/lib/views/home/hashtag/hashtag.dart b/lib/views/home/hashtag/hashtag.dart index 91b2060..c12e92e 100644 --- a/lib/views/home/hashtag/hashtag.dart +++ b/lib/views/home/hashtag/hashtag.dart @@ -13,18 +13,20 @@ import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:provider/provider.dart'; class Hashtag extends StatefulWidget { - final Tag tag; - const Hashtag({Key? key, required this.tag}) : super(key: key); + final Map pageData; + const Hashtag({Key? key, required this.pageData}) : super(key: key); @override _HashtagState createState() => _HashtagState(); } class _HashtagState extends State { + Tag get _tag => widget.pageData['tag']; + @override void initState() { final state = context.read(); - state.id = widget.tag.id; + state.id = _tag.id; Future.delayed(Duration.zero, () => state.getTagItems(page: 1)); super.initState(); } @@ -32,7 +34,7 @@ class _HashtagState extends State { @override Widget build(BuildContext context) { return DidvanScaffold( - appBarData: AppBarData(title: '#' + widget.tag.label, hasBack: true), + appBarData: AppBarData(title: '#' + _tag.label, hasBack: true), slivers: [ Consumer( builder: (context, state, child) => SliverStateHandler( @@ -57,25 +59,29 @@ class _HashtagState extends State { case 'radar': return RadarOverview( radar: item, + onMarkChanged: (_, value, __) => + _changeMark(item.id, value, type), onCommentsChanged: (_, count) => item.comments = count, - onMarkChanged: (_, value, __) => item.marked = value, ); case 'news': return NewsOverview( news: item, - onMarkChanged: (_, value, __) => item.marked = value, + onMarkChanged: (_, value, __) => + _changeMark(item.id, value, type), ); case 'podcast': return PodcastOverview( podcast: item, - onMarkChanged: (_, value, __) => item.marked = value, + onMarkChanged: (_, value, __) => + _changeMark(item.id, value, type), studioRequestArgs: const StudioRequestArgs(page: 0, type: 'podcast'), ); case 'video': return VideoOverview( video: item, - onMarkChanged: (_, value, __) => item.marked = value, + onMarkChanged: (_, value, __) => + _changeMark(item.id, value, type), studioRequestArgs: const StudioRequestArgs(page: 0, type: 'video'), ); @@ -90,4 +96,15 @@ class _HashtagState extends State { ], ); } + + void _changeMark(int id, bool value, String type) { + final state = context.read(); + state.items + .firstWhere((element) => element.id == id && element.type == type) + .marked = value; + state.update(); + if (type == widget.pageData['type']) { + widget.pageData['onMarkChanged'](id, value); + } + } } diff --git a/lib/views/home/news/news_details/news_details.dart b/lib/views/home/news/news_details/news_details.dart index aede351..3becf7e 100644 --- a/lib/views/home/news/news_details/news_details.dart +++ b/lib/views/home/news/news_details/news_details.dart @@ -57,6 +57,8 @@ class _NewsDetailsState extends State { scrollController: _scrollController, items: state.news, currentIndex: state.currentIndex, + onMarkChanged: (id, value) => + widget.pageData['onMarkChanged'](id, value), ), ), Positioned( diff --git a/lib/views/home/radar/radar_details/radar_details.dart b/lib/views/home/radar/radar_details/radar_details.dart index f3a4fc3..c371e5f 100644 --- a/lib/views/home/radar/radar_details/radar_details.dart +++ b/lib/views/home/radar/radar_details/radar_details.dart @@ -57,6 +57,8 @@ class _RadarDetailsState extends State { scrollController: _scrollController, items: state.radars, currentIndex: state.currentIndex, + onMarkChanged: (id, value) => + widget.pageData['onMarkChanged']?.call(id, value), ), ), Positioned( diff --git a/lib/views/home/studio/studio_details/studio_details.mobile.dart b/lib/views/home/studio/studio_details/studio_details.mobile.dart index b43ff6a..789cf0e 100644 --- a/lib/views/home/studio/studio_details/studio_details.mobile.dart +++ b/lib/views/home/studio/studio_details/studio_details.mobile.dart @@ -219,6 +219,7 @@ class _StudioDetailsState extends State { children: [ StudioDetailsWidget( scrollController: _scrollController, + onMarkChanged: widget.pageData['onMarkChanged'], ), ], ), diff --git a/lib/views/home/studio/studio_details/studio_details.web.dart b/lib/views/home/studio/studio_details/studio_details.web.dart index 38a2579..b8d37ca 100644 --- a/lib/views/home/studio/studio_details/studio_details.web.dart +++ b/lib/views/home/studio/studio_details/studio_details.web.dart @@ -150,6 +150,7 @@ class _StudioDetailsState extends State { children: [ StudioDetailsWidget( scrollController: _scrollController, + onMarkChanged: widget.pageData['onMarkChanged'], ), ], ), diff --git a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart index c02c0a4..4344f27 100644 --- a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart +++ b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart @@ -21,8 +21,10 @@ import 'package:url_launcher/url_launcher.dart'; class StudioDetailsWidget extends StatelessWidget { final ScrollController? scrollController; final VoidCallback? onCommentsTabSelected; + final void Function(int id, bool value) onMarkChanged; const StudioDetailsWidget({ Key? key, + required this.onMarkChanged, this.onCommentsTabSelected, this.scrollController, }) : super(key: key); @@ -87,7 +89,11 @@ class StudioDetailsWidget extends StatelessWidget { for (var i = 0; i < state.studio.tags.length; i++) - TagItem(tag: state.studio.tags[i]), + TagItem( + tag: state.studio.tags[i], + onMarkChanged: onMarkChanged, + type: isVideo ? 'video' : 'podcast', + ), ], ), const SizedBox(height: 20), diff --git a/lib/views/home/widgets/tag_item.dart b/lib/views/home/widgets/tag_item.dart index 1265361..56d14ef 100644 --- a/lib/views/home/widgets/tag_item.dart +++ b/lib/views/home/widgets/tag_item.dart @@ -9,18 +9,26 @@ import 'package:flutter/material.dart'; class TagItem extends StatelessWidget { final Tag tag; + final void Function(int id, bool value) onMarkChanged; + final String type; const TagItem({ Key? key, required this.tag, + required this.onMarkChanged, + required this.type, }) : super(key: key); @override Widget build(BuildContext context) { return InkWrapper( borderRadius: DesignConfig.lowBorderRadius, - onPressed: () => - Navigator.of(context).pushNamed(Routes.hashtag, arguments: tag), + onPressed: () => Navigator.of(context).pushNamed(Routes.hashtag, + arguments: { + 'tag': tag, + 'onMarkChanged': onMarkChanged, + 'type': type + }), child: Container( padding: const EdgeInsets.symmetric( vertical: 4, diff --git a/lib/views/widgets/didvan/bnb.dart b/lib/views/widgets/didvan/bnb.dart index 002dea5..deee1bb 100644 --- a/lib/views/widgets/didvan/bnb.dart +++ b/lib/views/widgets/didvan/bnb.dart @@ -338,6 +338,8 @@ class _PlayerNavBar extends StatelessWidget { sheetKey.currentState?.expand, ); }, + onMarkChanged: (id, value) => + context.read().changeMark(id, value, true), ), ), ), diff --git a/lib/views/widgets/didvan/page_view.dart b/lib/views/widgets/didvan/page_view.dart index fd3a8e9..a7cb236 100644 --- a/lib/views/widgets/didvan/page_view.dart +++ b/lib/views/widgets/didvan/page_view.dart @@ -21,6 +21,7 @@ class DidvanPageView extends StatefulWidget { final int initialIndex; final int currentIndex; final bool isRadar; + final void Function(int id, bool value) onMarkChanged; final ScrollController scrollController; final void Function(int index) onPageChanged; @@ -32,6 +33,7 @@ class DidvanPageView extends StatefulWidget { required this.onPageChanged, required this.isRadar, required this.currentIndex, + required this.onMarkChanged, }) : super(key: key); @override @@ -119,7 +121,11 @@ class _DidvanPageViewState extends State { runSpacing: 8, children: [ for (var i = 0; i < item.tags.length; i++) - TagItem(tag: item.tags[i]), + TagItem( + tag: item.tags[i], + onMarkChanged: widget.onMarkChanged, + type: widget.isRadar ? 'radar' : 'news', + ), ], ), ), From 7f0d0689f8c472195b905a3e7fb2521b7534ef65 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Sat, 2 Apr 2022 22:17:30 +0430 Subject: [PATCH 39/54] bug fixes --- .../studio_details/studio_details.mobile.dart | 3 ++- .../widgets/studio_details_widget.dart | 14 +++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/views/home/studio/studio_details/studio_details.mobile.dart b/lib/views/home/studio/studio_details/studio_details.mobile.dart index 789cf0e..4e4f404 100644 --- a/lib/views/home/studio/studio_details/studio_details.mobile.dart +++ b/lib/views/home/studio/studio_details/studio_details.mobile.dart @@ -219,7 +219,8 @@ class _StudioDetailsState extends State { children: [ StudioDetailsWidget( scrollController: _scrollController, - onMarkChanged: widget.pageData['onMarkChanged'], + onMarkChanged: (id, value) => + widget.pageData['onMarkChanged'](id, value, true), ), ], ), diff --git a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart index 4344f27..02ebd40 100644 --- a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart +++ b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart @@ -91,7 +91,8 @@ class StudioDetailsWidget extends StatelessWidget { i++) TagItem( tag: state.studio.tags[i], - onMarkChanged: onMarkChanged, + onMarkChanged: (id, value) => + _onMarkChanged(id, value, state), type: isVideo ? 'video' : 'podcast', ), ], @@ -177,6 +178,17 @@ class StudioDetailsWidget extends StatelessWidget { }, ); } + + void _onMarkChanged(id, value, state) { + onMarkChanged(id, value); + if (state.studio.id == id) { + state.studio.marked = value; + } else if (state.nextStudio?.id == id) { + state.nextStudio!.marked = value; + } else if (state.prevStudio?.id == id) { + state.prevStudio!.marked = value; + } + } } class _StudioPreview extends StatelessWidget { From 3a0622939e8188c8986f31f396f3b3319e8e82cc Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Sun, 3 Apr 2022 00:29:53 +0430 Subject: [PATCH 40/54] podcast overview --- lib/models/overview_data.dart | 50 ++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/lib/models/overview_data.dart b/lib/models/overview_data.dart index 6a20c71..beba572 100644 --- a/lib/models/overview_data.dart +++ b/lib/models/overview_data.dart @@ -1,4 +1,5 @@ import 'package:didvan/models/category.dart'; +import 'package:html/parser.dart'; class OverviewData { final int id; @@ -33,28 +34,33 @@ class OverviewData { this.categories, }); - 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'] ?? false, - comments: json['comments'] ?? 0, - createdAt: json['createdAt'], - duration: json['duration'], - type: json['type'] ?? '', - marked: json['marked'] ?? true, - media: json['media'], - categories: json['categories'] != null - ? List.from( - json['categories'].map( - (e) => CategoryData.fromJson(e), - ), - ) - : null, - ); + factory OverviewData.fromJson(Map json) { + final document = parse(json['description']); + final String parsedString = + parse(document.body!.text).documentElement!.text; + return OverviewData( + id: json['id'], + title: json['title'], + image: json['image'], + description: parsedString, + timeToRead: json['timeToRead'], + reference: json['reference'], + forManagers: json['forManagers'] ?? false, + comments: json['comments'] ?? 0, + createdAt: json['createdAt'], + duration: json['duration'], + type: json['type'] ?? '', + marked: json['marked'] ?? true, + media: json['media'], + categories: json['categories'] != null + ? List.from( + json['categories'].map( + (e) => CategoryData.fromJson(e), + ), + ) + : null, + ); + } Map toJson() => { 'id': id, From 42006f5fc88d8fb9cd36146f7822558949105d8e Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Sun, 3 Apr 2022 02:43:18 +0430 Subject: [PATCH 41/54] bug fix --- lib/utils/action_sheet.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/utils/action_sheet.dart b/lib/utils/action_sheet.dart index 15ea262..b8c1ad0 100644 --- a/lib/utils/action_sheet.dart +++ b/lib/utils/action_sheet.dart @@ -17,6 +17,7 @@ class ActionSheetUtils { static Future showLogoLoadingIndicator() async { await showDialog( + barrierDismissible: false, context: context, builder: (context) => Padding( padding: EdgeInsets.symmetric( From 939dd9b358c727e66f6c41beab3fb749ed56a42f Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Sun, 3 Apr 2022 03:22:53 +0430 Subject: [PATCH 42/54] bug fix --- .../authentication/screens/password.dart | 2 + .../studio_details/studio_details.mobile.dart | 4 +- .../widgets/studio_details_widget.dart | 42 ++++++++++--------- lib/views/home/studio/widgets/slider.dart | 31 +++++++------- 4 files changed, 42 insertions(+), 37 deletions(-) diff --git a/lib/views/authentication/screens/password.dart b/lib/views/authentication/screens/password.dart index 6402d6a..d558a49 100644 --- a/lib/views/authentication/screens/password.dart +++ b/lib/views/authentication/screens/password.dart @@ -76,7 +76,9 @@ class _PasswordInputState extends State { final token = await state.login(userProvider); if (token != null) { log(token); + ActionSheetUtils.showLogoLoadingIndicator(); await ServerDataProvider.getData(); + ActionSheetUtils.pop(); Navigator.of(context).pushReplacementNamed(Routes.home); _showResetPasswordDialog(); } diff --git a/lib/views/home/studio/studio_details/studio_details.mobile.dart b/lib/views/home/studio/studio_details/studio_details.mobile.dart index 4e4f404..6ee9550 100644 --- a/lib/views/home/studio/studio_details/studio_details.mobile.dart +++ b/lib/views/home/studio/studio_details/studio_details.mobile.dart @@ -145,8 +145,8 @@ class _StudioDetailsState extends State { child: Stack( children: [ WebView( - backgroundColor: - Theme.of(context).colorScheme.black, + zoomEnabled: false, + backgroundColor: Colors.black, allowsInlineMediaPlayback: true, initialUrl: Uri.dataFromString( ''' diff --git a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart index 02ebd40..12150b3 100644 --- a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart +++ b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart @@ -141,33 +141,35 @@ class StudioDetailsWidget extends StatelessWidget { ), ); } - return Column( - children: [ - if (state.studio.relatedContents.isEmpty) - for (var i = 0; i < 3; i++) + return SingleChildScrollView( + child: Column( + children: [ + if (state.studio.relatedContents.isEmpty) + for (var i = 0; i < 3; i++) + Padding( + padding: const EdgeInsets.only( + bottom: 8, + left: 16, + right: 16, + ), + child: MultitypeOverview.placeholder, + ), + for (var i = 0; + i < state.studio.relatedContents.length; + i++) Padding( padding: const EdgeInsets.only( bottom: 8, left: 16, right: 16, ), - child: MultitypeOverview.placeholder, + child: MultitypeOverview( + item: state.studio.relatedContents[i], + onMarkChanged: (id, value) {}, + ), ), - for (var i = 0; - i < state.studio.relatedContents.length; - i++) - Padding( - padding: const EdgeInsets.only( - bottom: 8, - left: 16, - right: 16, - ), - child: MultitypeOverview( - item: state.studio.relatedContents[i], - onMarkChanged: (id, value) {}, - ), - ), - ], + ], + ), ); }, ), diff --git a/lib/views/home/studio/widgets/slider.dart b/lib/views/home/studio/widgets/slider.dart index 8f80c6a..78bf110 100644 --- a/lib/views/home/studio/widgets/slider.dart +++ b/lib/views/home/studio/widgets/slider.dart @@ -97,22 +97,23 @@ class _StudioSliderState extends State { ), ), ), - Container( - height: 52, - width: 52, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Theme.of(context) - .colorScheme - .secondary - .withOpacity(0.7), + if (state.videosSelected) + Container( + height: 52, + width: 52, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Theme.of(context) + .colorScheme + .secondary + .withOpacity(0.7), + ), + child: Icon( + DidvanIcons.play_solid, + color: Theme.of(context).colorScheme.white, + size: 48, + ), ), - child: Icon( - DidvanIcons.play_solid, - color: Theme.of(context).colorScheme.white, - size: 48, - ), - ), ], ), ), From 17cd060ffc7985758b2eddd36c1e56b43e65c9b7 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Sun, 3 Apr 2022 23:25:33 +0430 Subject: [PATCH 43/54] code cleaning --- lib/routes/route_generator.dart | 1 - lib/views/home/studio/studio_details/studio_details.mobile.dart | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/routes/route_generator.dart b/lib/routes/route_generator.dart index 43bfd51..b70afa5 100644 --- a/lib/routes/route_generator.dart +++ b/lib/routes/route_generator.dart @@ -1,4 +1,3 @@ -import 'package:didvan/models/tag.dart'; import 'package:didvan/views/authentication/authentication.dart'; import 'package:didvan/views/authentication/authentication_state.dart'; import 'package:didvan/views/home/comments/comments.dart'; diff --git a/lib/views/home/studio/studio_details/studio_details.mobile.dart b/lib/views/home/studio/studio_details/studio_details.mobile.dart index 6ee9550..276d426 100644 --- a/lib/views/home/studio/studio_details/studio_details.mobile.dart +++ b/lib/views/home/studio/studio_details/studio_details.mobile.dart @@ -1,7 +1,6 @@ import 'dart:io'; import 'package:didvan/config/design_config.dart'; -import 'package:didvan/config/theme_data.dart'; import 'package:didvan/models/view/app_bar_data.dart'; import 'package:didvan/services/media/media.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; From 58ca866acbb4a19c0686ef44b67dd35a5371a1ea Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Sun, 3 Apr 2022 23:53:44 +0430 Subject: [PATCH 44/54] packages updated --- pubspec.lock | 76 ++++++++++++++++++++++++++-------------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 8495b3a..4182fc8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -161,7 +161,7 @@ packages: name: firebase_core url: "https://pub.dartlang.org" source: hosted - version: "1.13.1" + version: "1.14.0" firebase_core_platform_interface: dependency: transitive description: @@ -182,21 +182,21 @@ packages: name: firebase_messaging url: "https://pub.dartlang.org" source: hosted - version: "11.2.8" + version: "11.2.12" firebase_messaging_platform_interface: dependency: transitive description: name: firebase_messaging_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "3.2.1" + version: "3.2.2" firebase_messaging_web: dependency: transitive description: name: firebase_messaging_web url: "https://pub.dartlang.org" source: hosted - version: "2.2.9" + version: "2.2.10" flutter: dependency: "direct main" description: flutter @@ -208,7 +208,7 @@ packages: name: flutter_blurhash url: "https://pub.dartlang.org" source: hosted - version: "0.6.0" + version: "0.6.4" flutter_cache_manager: dependency: transitive description: @@ -297,7 +297,7 @@ packages: name: flutter_svg url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.0.3" flutter_test: dependency: "direct dev" description: flutter @@ -349,28 +349,28 @@ packages: name: image_cropper url: "https://pub.dartlang.org" source: hosted - version: "1.5.0" + version: "1.5.1" image_picker: dependency: "direct main" description: name: image_picker url: "https://pub.dartlang.org" source: hosted - version: "0.8.4+4" + version: "0.8.4+11" image_picker_for_web: dependency: transitive description: name: image_picker_for_web url: "https://pub.dartlang.org" source: hosted - version: "2.1.4" + version: "2.1.6" image_picker_platform_interface: dependency: transitive description: name: image_picker_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.4.1" + version: "2.4.4" intl: dependency: transitive description: @@ -391,21 +391,21 @@ packages: name: just_audio url: "https://pub.dartlang.org" source: hosted - version: "0.9.18" + version: "0.9.20" just_audio_platform_interface: dependency: transitive description: name: just_audio_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "4.0.0" + version: "4.1.0" just_audio_web: dependency: transitive description: name: just_audio_web url: "https://pub.dartlang.org" source: hosted - version: "0.4.2" + version: "0.4.7" lints: dependency: transitive description: @@ -482,49 +482,49 @@ packages: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "2.0.8" + version: "2.0.9" path_provider_android: dependency: transitive description: name: path_provider_android url: "https://pub.dartlang.org" source: hosted - version: "2.0.11" + version: "2.0.12" path_provider_ios: dependency: transitive description: name: path_provider_ios url: "https://pub.dartlang.org" source: hosted - version: "2.0.7" + version: "2.0.8" path_provider_linux: dependency: transitive description: name: path_provider_linux url: "https://pub.dartlang.org" source: hosted - version: "2.1.4" + version: "2.1.5" path_provider_macos: dependency: transitive description: name: path_provider_macos url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.0.5" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.3" path_provider_windows: dependency: transitive description: name: path_provider_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.0.5" pedantic: dependency: transitive description: @@ -552,7 +552,7 @@ packages: name: permission_handler_apple url: "https://pub.dartlang.org" source: hosted - version: "9.0.3" + version: "9.0.4" permission_handler_platform_interface: dependency: transitive description: @@ -629,7 +629,7 @@ packages: name: record url: "https://pub.dartlang.org" source: hosted - version: "3.0.2" + version: "3.0.4" record_platform_interface: dependency: transitive description: @@ -683,14 +683,14 @@ packages: name: sqflite url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.2" sqflite_common: dependency: transitive description: name: sqflite_common url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.2.1" stack_trace: dependency: transitive description: @@ -718,7 +718,7 @@ packages: name: synchronized url: "https://pub.dartlang.org" source: hosted - version: "3.0.0" + version: "3.0.0+2" term_glyph: dependency: transitive description: @@ -760,63 +760,63 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "6.0.18" + version: "6.0.20" url_launcher_android: dependency: transitive description: name: url_launcher_android url: "https://pub.dartlang.org" source: hosted - version: "6.0.14" + version: "6.0.15" url_launcher_ios: dependency: transitive description: name: url_launcher_ios url: "https://pub.dartlang.org" source: hosted - version: "6.0.14" + version: "6.0.15" url_launcher_linux: dependency: transitive description: name: url_launcher_linux url: "https://pub.dartlang.org" source: hosted - version: "2.0.3" + version: "3.0.0" url_launcher_macos: dependency: transitive description: name: url_launcher_macos url: "https://pub.dartlang.org" source: hosted - version: "2.0.3" + version: "3.0.0" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.0.5" url_launcher_web: dependency: transitive description: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.6" + version: "2.0.9" url_launcher_windows: dependency: transitive description: name: url_launcher_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "3.0.0" uuid: dependency: transitive description: name: uuid url: "https://pub.dartlang.org" source: hosted - version: "3.0.5" + version: "3.0.6" vector_math: dependency: transitive description: @@ -837,7 +837,7 @@ packages: name: webview_flutter_android url: "https://pub.dartlang.org" source: hosted - version: "2.8.3" + version: "2.8.4" webview_flutter_platform_interface: dependency: transitive description: @@ -858,14 +858,14 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.3.3" + version: "2.5.1" xdg_directories: dependency: transitive description: name: xdg_directories url: "https://pub.dartlang.org" source: hosted - version: "0.2.0" + version: "0.2.0+1" xml: dependency: transitive description: @@ -875,4 +875,4 @@ packages: version: "5.3.1" sdks: dart: ">=2.16.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" From 48387b0b79cf412ea807c4b02fee5ff050303032 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Mon, 4 Apr 2022 00:05:30 +0430 Subject: [PATCH 45/54] bnb shadow offset fixed --- lib/views/widgets/didvan/bnb.dart | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/views/widgets/didvan/bnb.dart b/lib/views/widgets/didvan/bnb.dart index deee1bb..337c583 100644 --- a/lib/views/widgets/didvan/bnb.dart +++ b/lib/views/widgets/didvan/bnb.dart @@ -40,7 +40,14 @@ class DidvanBNB extends StatelessWidget { color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.vertical(top: Radius.circular(16)), - boxShadow: DesignConfig.defaultShadow, + boxShadow: [ + BoxShadow( + color: const Color(0XFF1B3C59).withOpacity(0.15), + blurRadius: 8, + spreadRadius: 0, + offset: const Offset(0, -8), + ) + ], ), padding: const EdgeInsets.symmetric(horizontal: 12), child: Row( From 00f108710a7076027566d1bb3acc05a60a96748b Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Mon, 4 Apr 2022 14:16:21 +0430 Subject: [PATCH 46/54] new video player basic version --- ios/Podfile | 2 +- ios/Podfile.lock | 99 ++++-- .../studio_details/studio_details.mobile.dart | 195 +++--------- .../studio_details/studio_details.web.dart | 19 +- .../widgets/details_tab_bar.dart | 3 - .../widgets/studio_details_widget.dart | 296 +++++++++--------- lib/views/widgets/didvan/bnb.dart | 6 - pubspec.lock | 63 ++++ pubspec.yaml | 1 + 9 files changed, 331 insertions(+), 353 deletions(-) diff --git a/ios/Podfile b/ios/Podfile index 9411102..313ea4a 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '10.0' +platform :ios, '11.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 98e5804..5e2c2df 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,33 +1,40 @@ PODS: - audio_session (0.0.1): - Flutter - - Firebase/CoreOnly (8.11.0): - - FirebaseCore (= 8.11.0) - - Firebase/Messaging (8.11.0): - - Firebase/CoreOnly - - FirebaseMessaging (~> 8.11.0) - - firebase_core (1.13.1): - - Firebase/CoreOnly (= 8.11.0) + - better_player (0.0.1): + - Cache (~> 6.0.0) - Flutter - - firebase_messaging (11.2.8): - - Firebase/Messaging (= 8.11.0) + - GCDWebServer + - HLSCachingReverseProxyServer + - PINCache + - Cache (6.0.0) + - Firebase/CoreOnly (8.14.0): + - FirebaseCore (= 8.14.0) + - Firebase/Messaging (8.14.0): + - Firebase/CoreOnly + - FirebaseMessaging (~> 8.14.0) + - firebase_core (1.14.0): + - Firebase/CoreOnly (= 8.14.0) + - Flutter + - firebase_messaging (11.2.12): + - Firebase/Messaging (= 8.14.0) - firebase_core - Flutter - - FirebaseCore (8.11.0): + - FirebaseCore (8.14.0): - FirebaseCoreDiagnostics (~> 8.0) - GoogleUtilities/Environment (~> 7.7) - GoogleUtilities/Logger (~> 7.7) - - FirebaseCoreDiagnostics (8.12.0): + - FirebaseCoreDiagnostics (8.14.0): - GoogleDataTransport (~> 9.1) - GoogleUtilities/Environment (~> 7.7) - GoogleUtilities/Logger (~> 7.7) - nanopb (~> 2.30908.0) - - FirebaseInstallations (8.12.0): + - FirebaseInstallations (8.14.0): - FirebaseCore (~> 8.0) - GoogleUtilities/Environment (~> 7.7) - GoogleUtilities/UserDefaults (~> 7.7) - PromisesObjC (< 3.0, >= 1.2) - - FirebaseMessaging (8.11.0): + - FirebaseMessaging (8.14.0): - FirebaseCore (~> 8.0) - FirebaseInstallations (~> 8.0) - GoogleDataTransport (~> 9.1) @@ -44,6 +51,9 @@ PODS: - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) + - GCDWebServer (3.5.4): + - GCDWebServer/Core (= 3.5.4) + - GCDWebServer/Core (3.5.4) - GoogleDataTransport (9.1.2): - GoogleUtilities/Environment (~> 7.2) - nanopb (~> 2.30908.0) @@ -65,6 +75,9 @@ PODS: - GoogleUtilities/Logger - GoogleUtilities/UserDefaults (7.7.0): - GoogleUtilities/Logger + - HLSCachingReverseProxyServer (0.1.0): + - GCDWebServer (~> 3.5) + - PINCache (>= 3.0.1-beta.3) - image_cropper (0.0.4): - Flutter - TOCropViewController (~> 2.6.1) @@ -79,8 +92,16 @@ PODS: - nanopb/encode (2.30908.0) - path_provider_ios (0.0.1): - Flutter - - permission_handler_apple (9.0.2): + - permission_handler_apple (9.0.4): - Flutter + - PINCache (3.0.3): + - PINCache/Arc-exception-safe (= 3.0.3) + - PINCache/Core (= 3.0.3) + - PINCache/Arc-exception-safe (3.0.3): + - PINCache/Core + - PINCache/Core (3.0.3): + - PINOperation (~> 1.2.1) + - PINOperation (1.2.1) - PromisesObjC (2.0.0) - record (0.0.1): - Flutter @@ -90,11 +111,16 @@ PODS: - TOCropViewController (2.6.1) - url_launcher_ios (0.0.1): - Flutter + - video_player_avfoundation (0.0.1): + - Flutter + - wakelock (0.0.1): + - Flutter - webview_flutter_wkwebview (0.0.1): - Flutter DEPENDENCIES: - audio_session (from `.symlinks/plugins/audio_session/ios`) + - better_player (from `.symlinks/plugins/better_player/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) - Flutter (from `Flutter`) @@ -108,25 +134,34 @@ DEPENDENCIES: - record (from `.symlinks/plugins/record/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) + - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`) + - wakelock (from `.symlinks/plugins/wakelock/ios`) - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`) SPEC REPOS: trunk: + - Cache - Firebase - FirebaseCore - FirebaseCoreDiagnostics - FirebaseInstallations - FirebaseMessaging - FMDB + - GCDWebServer - GoogleDataTransport - GoogleUtilities + - HLSCachingReverseProxyServer - nanopb + - PINCache + - PINOperation - PromisesObjC - TOCropViewController EXTERNAL SOURCES: audio_session: :path: ".symlinks/plugins/audio_session/ios" + better_player: + :path: ".symlinks/plugins/better_player/ios" firebase_core: :path: ".symlinks/plugins/firebase_core/ios" firebase_messaging: @@ -153,37 +188,49 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/sqflite/ios" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" + video_player_avfoundation: + :path: ".symlinks/plugins/video_player_avfoundation/ios" + wakelock: + :path: ".symlinks/plugins/wakelock/ios" webview_flutter_wkwebview: :path: ".symlinks/plugins/webview_flutter_wkwebview/ios" SPEC CHECKSUMS: audio_session: 4f3e461722055d21515cf3261b64c973c062f345 - Firebase: 44dd9724c84df18b486639e874f31436eaa9a20c - firebase_core: 08f6a85f62060111de5e98d6a214810d11365de9 - firebase_messaging: 36238f3d0b933af8c919aef608408aae06ba22e8 - FirebaseCore: 2f4f85b453cc8fea4bb2b37e370007d2bcafe3f0 - FirebaseCoreDiagnostics: 3b40dfadef5b90433a60ae01f01e90fe87aa76aa - FirebaseInstallations: 25764cf322e77f99449395870a65b2bef88e1545 - FirebaseMessaging: 02e248e8997f71fa8cc9d78e9d49ec1a701ba14a + better_player: 2406bfe8175203c7a46fa15f9d778d73b12e1646 + Cache: 4ca7e00363fca5455f26534e5607634c820ffc2d + Firebase: 7e8fe528c161b9271d365217a74c16aaf834578e + firebase_core: b0b382f1497ab407aceb25e41e3036c8798c1609 + firebase_messaging: 34dd10d1aa6d8f40d03660eeacd0452d62eec7aa + FirebaseCore: b84a44ee7ba999e0f9f76d198a9c7f60a797b848 + FirebaseCoreDiagnostics: fd0c8490f34287229c1d6c103d3a55f81ec85712 + FirebaseInstallations: 7d1d967a307c12f1aadd76844fc321cef699b1ce + FirebaseMessaging: 5ebc42d281567658a2cb72b9ef3506e4a1a1a6e4 Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec flutter_vibrate: 9f4c2ab57008965f78969472367c329dd77eb801 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a + GCDWebServer: 2c156a56c8226e2d5c0c3f208a3621ccffbe3ce4 GoogleDataTransport: 629c20a4d363167143f30ea78320d5a7eb8bd940 GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1 + HLSCachingReverseProxyServer: 59935e1e0244ad7f3375d75b5ef46e8eb26ab181 image_cropper: 60c2789d1f1a78c873235d4319ca0c34a69f2d98 - image_picker: 9aa50e1d8cdacdbed739e925b7eea16d014367e6 + image_picker: 541dcbb3b9cf32d87eacbd957845d8651d6c62c3 just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 - path_provider_ios: 7d7ce634493af4477d156294792024ec3485acd5 - permission_handler_apple: d21b38e1a4b2e041c63af9568f9165e114e507a6 + path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 + permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce + PINCache: 7a8fc1a691173d21dbddbf86cd515de6efa55086 + PINOperation: 00c935935f1e8cf0d1e2d6b542e75b88fc3e5e20 PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58 record: 7ee2393532f8553bbb09fa19e95478323b7c0a99 sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863 - url_launcher_ios: 02f1989d4e14e998335b02b67a7590fa34f971af + url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de + video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff + wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f webview_flutter_wkwebview: 005fbd90c888a42c5690919a1527ecc6649e1162 -PODFILE CHECKSUM: fe0e1ee7f3d1f7d00b11b474b62dd62134535aea +PODFILE CHECKSUM: 7368163408c647b7eb699d0d788ba6718e18fb8d COCOAPODS: 1.11.2 diff --git a/lib/views/home/studio/studio_details/studio_details.mobile.dart b/lib/views/home/studio/studio_details/studio_details.mobile.dart index 276d426..ea72e90 100644 --- a/lib/views/home/studio/studio_details/studio_details.mobile.dart +++ b/lib/views/home/studio/studio_details/studio_details.mobile.dart @@ -1,16 +1,15 @@ import 'dart:io'; -import 'package:didvan/config/design_config.dart'; +import 'package:better_player/better_player.dart'; import 'package:didvan/models/view/app_bar_data.dart'; import 'package:didvan/services/media/media.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; import 'package:didvan/views/home/studio/studio_details/widgets/details_tab_bar.dart'; import 'package:didvan/views/home/studio/studio_details/widgets/studio_details_widget.dart'; import 'package:didvan/views/home/widgets/bookmark_button.dart'; -import 'package:didvan/views/widgets/didvan/scaffold.dart'; +import 'package:didvan/views/widgets/didvan/app_bar.dart'; import 'package:didvan/views/widgets/state_handlers/state_handler.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'package:webview_flutter/webview_flutter.dart'; @@ -26,61 +25,23 @@ class StudioDetails extends StatefulWidget { class _StudioDetailsState extends State { final _scrollController = ScrollController(); - bool _isFullScreen = false; - bool _isInit = true; - - double _dwInPortrait = 0; - double _scaleInPortrait = 1; @override void initState() { final state = context.read(); + state.args = widget.pageData['args']; Future.delayed( Duration.zero, () => state.getStudioDetails(widget.pageData['id']), ); - state.args = widget.pageData['args']; + if (Platform.isAndroid) WebView.platform = AndroidWebView(); super.initState(); } - Future _changeFullSceen(bool value) async { - if (value) { - await SystemChrome.setEnabledSystemUIMode( - SystemUiMode.manual, - overlays: [], - ); - SystemChrome.setSystemUIOverlayStyle( - const SystemUiOverlayStyle( - systemNavigationBarColor: Colors.black, - ), - ); - await SystemChrome.setPreferredOrientations( - [DeviceOrientation.landscapeLeft], - ); - } else { - await SystemChrome.setEnabledSystemUIMode( - SystemUiMode.manual, - overlays: [SystemUiOverlay.bottom, SystemUiOverlay.top], - ); - await SystemChrome.setPreferredOrientations( - [DeviceOrientation.portraitUp], - ); - DesignConfig.updateSystemUiOverlayStyle(); - } - setState(() { - _isFullScreen = value; - }); - } - @override Widget build(BuildContext context) { - final ds = MediaQuery.of(context).size; - if (_isInit) { - _dwInPortrait = MediaQuery.of(context).size.width; - _scaleInPortrait = _dwInPortrait / 576; - _isInit = false; - } + final d = MediaQuery.of(context); return Consumer( builder: (context, state, child) => StateHandler( @@ -95,23 +56,19 @@ class _StudioDetailsState extends State { builder: (context, state) { return WillPopScope( onWillPop: () async { - if (_isFullScreen) { - await _changeFullSceen(false); - return false; - } if (MediaService.currentPodcast != null) { state.studio = MediaService.currentPodcast!; } return true; }, - child: DidvanScaffold( - key: ValueKey(state.studio.id), - scrollController: _scrollController, - backgroundColor: Theme.of(context).colorScheme.surface, - padding: EdgeInsets.zero, - appBarData: _isFullScreen - ? null - : AppBarData( + child: SafeArea( + child: Scaffold( + key: ValueKey(state.studio.id), + backgroundColor: Theme.of(context).colorScheme.surface, + appBar: PreferredSize( + preferredSize: const Size.fromHeight(56), + child: DidvanAppBar( + appBarData: AppBarData( trailing: BookmarkButton( itemId: state.studio.id, type: 'video', @@ -125,103 +82,41 @@ class _StudioDetailsState extends State { isSmall: true, title: state.studio.title, ), - showSliversFirst: true, - slivers: [ - SliverAppBar( - automaticallyImplyLeading: false, - pinned: true, - backgroundColor: Theme.of(context).colorScheme.surface, - toolbarHeight: - (_isFullScreen ? ds.height : ds.width * 9 / 16) + - 72 - - MediaQuery.of(context).padding.top, - elevation: 0, - flexibleSpace: Column( - children: [ - SizedBox( - width: ds.width, - height: _isFullScreen ? ds.height : ds.width * 9 / 16, - child: Stack( - children: [ - WebView( - zoomEnabled: false, - backgroundColor: Colors.black, - allowsInlineMediaPlayback: true, - initialUrl: Uri.dataFromString( - ''' - - - - - - - ${state.studio.media} - - - ''', - mimeType: 'text/html', - ).toString(), - javascriptMode: JavascriptMode.unrestricted, - ), - Positioned( - right: 42, - bottom: 8, - child: GestureDetector( - onTap: () => _changeFullSceen(!_isFullScreen), - child: Container( - color: Colors.transparent, - width: 24, - height: 30, - ), - ), - ), - ], - ), - ), - DetailsTabBar( - isVideo: true, - onCommentsTabSelected: () => Future.delayed( - const Duration(milliseconds: 100), - () => _scrollController.animateTo( - _scrollController.position.maxScrollExtent, - duration: DesignConfig.lowAnimationDuration, - curve: Curves.easeIn, - ), - ), - ), - ], ), ), - ], - children: [ - StudioDetailsWidget( - scrollController: _scrollController, - onMarkChanged: (id, value) => - widget.pageData['onMarkChanged'](id, value, true), + body: Column( + children: [ + Directionality( + textDirection: TextDirection.ltr, + child: BetterPlayer.network( + 'https://studio-didvan.arvanvod.com/Vz01Bxq2bQ/nylPWJ4B63/h_,144_200,240_400,360_800,480_1215,720_1215,k.mp4.list/master.m3u8', + betterPlayerConfiguration: + const BetterPlayerConfiguration( + aspectRatio: 16 / 9, + controlsConfiguration: + BetterPlayerControlsConfiguration( + enablePlaybackSpeed: false, + enableSubtitles: false, + enableAudioTracks: false, + ), + autoDetectFullscreenAspectRatio: true, + autoDetectFullscreenDeviceOrientation: true, + fullScreenAspectRatio: 16 / 9, + ), + ), + ), + const DetailsTabBar( + isVideo: true, + ), + Expanded( + child: StudioDetailsWidget( + onMarkChanged: (id, value) => + widget.pageData['onMarkChanged'](id, value, true), + ), + ), + ], ), - ], + ), ), ); }, diff --git a/lib/views/home/studio/studio_details/studio_details.web.dart b/lib/views/home/studio/studio_details/studio_details.web.dart index b8d37ca..a1409c2 100644 --- a/lib/views/home/studio/studio_details/studio_details.web.dart +++ b/lib/views/home/studio/studio_details/studio_details.web.dart @@ -24,18 +24,17 @@ class StudioDetails extends StatefulWidget { } class _StudioDetailsState extends State { - final _scrollController = ScrollController(); - bool _isFullScreen = false; @override void initState() { final state = context.read(); + state.args = widget.pageData['args']; Future.delayed( Duration.zero, () => state.getStudioDetails(widget.pageData['id']), ); - state.args = widget.pageData['args']; + super.initState(); } @@ -100,7 +99,6 @@ class _StudioDetailsState extends State { return true; }, child: DidvanScaffold( - scrollController: _scrollController, padding: EdgeInsets.zero, appBarData: _isFullScreen ? null @@ -127,21 +125,13 @@ class _StudioDetailsState extends State { 72 - MediaQuery.of(context).padding.top, flexibleSpace: Column( - children: [ - const AspectRatio( + children: const [ + AspectRatio( aspectRatio: 16 / 9, child: HtmlElementView(viewType: 'video'), ), DetailsTabBar( isVideo: true, - onCommentsTabSelected: () => Future.delayed( - const Duration(milliseconds: 100), - () => _scrollController.animateTo( - _scrollController.position.maxScrollExtent, - duration: DesignConfig.lowAnimationDuration, - curve: Curves.easeIn, - ), - ), ), ], ), @@ -149,7 +139,6 @@ class _StudioDetailsState extends State { ], children: [ StudioDetailsWidget( - scrollController: _scrollController, onMarkChanged: widget.pageData['onMarkChanged'], ), ], diff --git a/lib/views/home/studio/studio_details/widgets/details_tab_bar.dart b/lib/views/home/studio/studio_details/widgets/details_tab_bar.dart index ce2a4a2..f5ef4d3 100644 --- a/lib/views/home/studio/studio_details/widgets/details_tab_bar.dart +++ b/lib/views/home/studio/studio_details/widgets/details_tab_bar.dart @@ -8,12 +8,10 @@ import 'package:provider/provider.dart'; class DetailsTabBar extends StatelessWidget { final bool isVideo; - final VoidCallback onCommentsTabSelected; const DetailsTabBar({ Key? key, required this.isVideo, - required this.onCommentsTabSelected, }) : super(key: key); @override @@ -55,7 +53,6 @@ class DetailsTabBar extends StatelessWidget { title: 'نظرات', onTap: () { state.selectedDetailsIndex = 1; - onCommentsTabSelected(); }, isSelected: state.selectedDetailsIndex == 1, isVideo: isVideo, diff --git a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart index 12150b3..bdfc44e 100644 --- a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart +++ b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart @@ -19,165 +19,164 @@ import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; class StudioDetailsWidget extends StatelessWidget { - final ScrollController? scrollController; - final VoidCallback? onCommentsTabSelected; final void Function(int id, bool value) onMarkChanged; const StudioDetailsWidget({ Key? key, required this.onMarkChanged, - this.onCommentsTabSelected, - this.scrollController, }) : super(key: key); @override Widget build(BuildContext context) { final ds = MediaQuery.of(context).size; - return Consumer( - builder: (context, state, child) { - bool isVideo = state.studio.media.contains('iframe'); - return Container( - color: Theme.of(context).colorScheme.surface, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (!isVideo) - DetailsTabBar( - isVideo: isVideo, - onCommentsTabSelected: onCommentsTabSelected ?? () {}, - ), - const SizedBox(height: 16), - ConstrainedBox( - constraints: BoxConstraints( - maxHeight: ds.height - - ds.width * 9 / 16 - - 144 - - MediaQuery.of(context).padding.top, - ), - child: StateHandler( - onRetry: () {}, - state: state, - builder: (context, state) { - if (state.selectedDetailsIndex == 0) { - return SingleChildScrollView( - physics: const BouncingScrollPhysics(), - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Html( - key: ValueKey(state.studio.id), - data: state.studio.description, - onAnchorTap: (href, context, map, element) => - launch(href!), - style: { - '*': Style( - direction: TextDirection.rtl, - textAlign: TextAlign.right, - lineHeight: LineHeight.percent(135), - margin: EdgeInsets.zero, - padding: EdgeInsets.zero, - ), - }, - ), - if (state.studio.tags.isNotEmpty) + return SafeArea( + bottom: true, + child: Consumer( + builder: (context, state, child) { + bool isVideo = state.studio.media.contains('iframe'); + return Container( + color: Theme.of(context).colorScheme.surface, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (!isVideo) + DetailsTabBar( + isVideo: isVideo, + ), + const SizedBox(height: 16), + ConstrainedBox( + constraints: BoxConstraints( + maxHeight: isVideo + ? double.infinity + : ds.height - + ds.width * 9 / 16 - + 144 - + MediaQuery.of(context).padding.top, + ), + child: StateHandler( + onRetry: () {}, + state: state, + builder: (context, state) { + if (state.selectedDetailsIndex == 0) { + return SingleChildScrollView( + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Html( + key: ValueKey(state.studio.id), + data: state.studio.description, + onAnchorTap: (href, context, map, element) => + launch(href!), + style: { + '*': Style( + direction: TextDirection.rtl, + textAlign: TextAlign.right, + lineHeight: LineHeight.percent(135), + margin: EdgeInsets.zero, + padding: EdgeInsets.zero, + ), + }, + ), + if (state.studio.tags.isNotEmpty) + const SizedBox(height: 20), + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + for (var i = 0; + i < state.studio.tags.length; + i++) + TagItem( + tag: state.studio.tags[i], + onMarkChanged: (id, value) => + _onMarkChanged(id, value, state), + type: isVideo ? 'video' : 'podcast', + ), + ], + ), const SizedBox(height: 20), - Wrap( - spacing: 8, - runSpacing: 8, - children: [ - for (var i = 0; - i < state.studio.tags.length; - i++) - TagItem( - tag: state.studio.tags[i], - onMarkChanged: (id, value) => - _onMarkChanged(id, value, state), - type: isVideo ? 'video' : 'podcast', + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + const SizedBox(), + if (state.nextStudio != null && + state.alongSideState == AppState.idle) + _StudioPreview( + isNext: true, + studio: state.nextStudio!, + ), + if (state.alongSideState == AppState.busy) + _StudioPreview.placeHolder, + if (state.prevStudio != null && + state.alongSideState == AppState.idle) + _StudioPreview( + isNext: false, + studio: state.prevStudio!, + ), + if (state.alongSideState == AppState.busy) + _StudioPreview.placeHolder, + const SizedBox(), + ], + ), + ], + ), + ); + } + if (state.selectedDetailsIndex == 1) { + return ChangeNotifierProvider( + create: (context) => CommentsState(), + child: Comments( + pageData: { + 'id': state.studio.id, + 'type': 'studio', + 'title': state.studio.title, + 'onCommentsChanged': state.onCommentsChanged, + 'isPage': false, + }, + ), + ); + } + return SingleChildScrollView( + child: Column( + children: [ + if (state.studio.relatedContents.isEmpty) + for (var i = 0; i < 3; i++) + Padding( + padding: const EdgeInsets.only( + bottom: 8, + left: 16, + right: 16, ), - ], - ), - const SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const SizedBox(), - if (state.nextStudio != null && - state.alongSideState == AppState.idle) - _StudioPreview( - isNext: true, - studio: state.nextStudio!, - scrollController: scrollController, - ), - if (state.alongSideState == AppState.busy) - _StudioPreview.placeHolder, - if (state.prevStudio != null && - state.alongSideState == AppState.idle) - _StudioPreview( - isNext: false, - studio: state.prevStudio!, - scrollController: scrollController, - ), - if (state.alongSideState == AppState.busy) - _StudioPreview.placeHolder, - const SizedBox(), - ], - ), - ], - ), - ); - } - if (state.selectedDetailsIndex == 1) { - return ChangeNotifierProvider( - create: (context) => CommentsState(), - child: Comments( - pageData: { - 'id': state.studio.id, - 'type': 'studio', - 'title': state.studio.title, - 'onCommentsChanged': state.onCommentsChanged, - 'isPage': false, - }, - ), - ); - } - return SingleChildScrollView( - child: Column( - children: [ - if (state.studio.relatedContents.isEmpty) - for (var i = 0; i < 3; i++) + child: MultitypeOverview.placeholder, + ), + for (var i = 0; + i < state.studio.relatedContents.length; + i++) Padding( padding: const EdgeInsets.only( bottom: 8, left: 16, right: 16, ), - child: MultitypeOverview.placeholder, + child: MultitypeOverview( + item: state.studio.relatedContents[i], + onMarkChanged: (id, value) {}, + ), ), - for (var i = 0; - i < state.studio.relatedContents.length; - i++) - Padding( - padding: const EdgeInsets.only( - bottom: 8, - left: 16, - right: 16, - ), - child: MultitypeOverview( - item: state.studio.relatedContents[i], - onMarkChanged: (id, value) {}, - ), - ), - ], - ), - ); - }, + ], + ), + ); + }, + ), ), - ), - ], - ), - ); - }, + ], + ), + ); + }, + ), ); } @@ -196,13 +195,11 @@ class StudioDetailsWidget extends StatelessWidget { class _StudioPreview extends StatelessWidget { final bool isNext; final StudioDetailsData studio; - final ScrollController? scrollController; - const _StudioPreview( - {Key? key, - required this.isNext, - required this.studio, - this.scrollController}) - : super(key: key); + const _StudioPreview({ + Key? key, + required this.isNext, + required this.studio, + }) : super(key: key); String get _previewTitle { if (studio.media.contains('iframe')) { @@ -221,11 +218,6 @@ class _StudioPreview extends StatelessWidget { args: state.args, isForward: isNext, ); - scrollController?.animateTo( - 0, - duration: DesignConfig.lowAnimationDuration, - curve: Curves.easeIn, - ); }, child: Container( width: 88, diff --git a/lib/views/widgets/didvan/bnb.dart b/lib/views/widgets/didvan/bnb.dart index 337c583..28c2d61 100644 --- a/lib/views/widgets/didvan/bnb.dart +++ b/lib/views/widgets/didvan/bnb.dart @@ -339,12 +339,6 @@ class _PlayerNavBar extends StatelessWidget { ), ) : StudioDetailsWidget( - onCommentsTabSelected: () { - Future.delayed( - const Duration(milliseconds: 100), - sheetKey.currentState?.expand, - ); - }, onMarkChanged: (id, value) => context.read().changeMark(id, value, true), ), diff --git a/pubspec.lock b/pubspec.lock index 4182fc8..cbb3c93 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -22,6 +22,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.10.0" + better_player: + dependency: "direct main" + description: + name: better_player + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.81" boolean_selector: dependency: transitive description: @@ -315,6 +322,20 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_widget_from_html_core: + dependency: transitive + description: + name: flutter_widget_from_html_core + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.5+1" + fwfh_text_style: + dependency: transitive + description: + name: fwfh_text_style + url: "https://pub.dartlang.org" + source: hosted + version: "2.7.3+1" graphs: dependency: transitive description: @@ -824,6 +845,48 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.1" + visibility_detector: + dependency: transitive + description: + name: visibility_detector + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.2" + wakelock: + dependency: transitive + description: + name: wakelock + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.6" + wakelock_macos: + dependency: transitive + description: + name: wakelock_macos + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.0" + wakelock_platform_interface: + dependency: transitive + description: + name: wakelock_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0" + wakelock_web: + dependency: transitive + description: + name: wakelock_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.0" + wakelock_windows: + dependency: transitive + description: + name: wakelock_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" webview_flutter: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 4f32cce..63202fa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -65,6 +65,7 @@ dependencies: webview_flutter: ^3.0.1 expandable_bottom_sheet: ^1.1.1+1 permission_handler: ^9.2.0 + better_player: ^0.0.81 dev_dependencies: From a7c0fcf6f0d120d743a469841a82c436f02f13ef Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Mon, 4 Apr 2022 18:16:03 +0430 Subject: [PATCH 47/54] new video player complete version --- lib/models/overview_data.dart | 9 +- lib/models/slider_data.dart | 7 +- lib/models/studio_details_data.dart | 10 +- lib/routes/route_generator.dart | 9 +- lib/utils/action_sheet.dart | 4 +- lib/views/home/studio/studio.dart | 2 +- .../studio_details/studio_details.mobile.dart | 85 +++++++------ .../studio_details/studio_details.web.dart | 112 ++++++------------ .../studio_details/studio_details_state.dart | 2 +- .../widgets/details_tab_bar.dart | 59 +++++---- .../widgets/studio_details_widget.dart | 106 +++++++++-------- .../widgets/audio/audio_player_widget.dart | 2 +- lib/views/home/widgets/overview/podcast.dart | 6 +- lib/views/widgets/didvan/bnb.dart | 2 +- 14 files changed, 198 insertions(+), 217 deletions(-) diff --git a/lib/models/overview_data.dart b/lib/models/overview_data.dart index beba572..f4995b0 100644 --- a/lib/models/overview_data.dart +++ b/lib/models/overview_data.dart @@ -9,7 +9,8 @@ class OverviewData { final int? timeToRead; final int? duration; final String? reference; - final String? media; + final String? link; + final String? iframe; final bool forManagers; final String createdAt; final String type; @@ -27,7 +28,8 @@ class OverviewData { required this.marked, required this.comments, required this.forManagers, - this.media, + this.link, + this.iframe, this.duration, this.timeToRead, this.reference, @@ -51,7 +53,8 @@ class OverviewData { duration: json['duration'], type: json['type'] ?? '', marked: json['marked'] ?? true, - media: json['media'], + link: json['link'], + iframe: json['iframe'], categories: json['categories'] != null ? List.from( json['categories'].map( diff --git a/lib/models/slider_data.dart b/lib/models/slider_data.dart index a99c52d..c6ff0ed 100644 --- a/lib/models/slider_data.dart +++ b/lib/models/slider_data.dart @@ -2,26 +2,25 @@ class SliderData { final int id; final String title; final String image; - final String media; + final String link; const SliderData({ required this.id, required this.title, required this.image, - required this.media, + required this.link, }); factory SliderData.fromJson(Map json) => SliderData( id: json['id'], title: json['title'], image: json['image'], - media: json['media'], + link: json['link'], ); Map toJson() => { 'id': id, 'title': title, 'image': image, - 'media': media, }; } diff --git a/lib/models/studio_details_data.dart b/lib/models/studio_details_data.dart index b83722e..334b4b2 100644 --- a/lib/models/studio_details_data.dart +++ b/lib/models/studio_details_data.dart @@ -7,7 +7,8 @@ class StudioDetailsData { final String title; final String description; final String image; - final String media; + final String link; + final String? iframe; final String createdAt; final int order; bool marked; @@ -21,7 +22,8 @@ class StudioDetailsData { required this.title, required this.description, required this.image, - required this.media, + required this.link, + required this.iframe, required this.createdAt, required this.order, required this.marked, @@ -36,7 +38,8 @@ class StudioDetailsData { title: json['title'], description: json['description'], image: json['image'], - media: json['media'], + link: json['link'], + iframe: json['iframe'], createdAt: json['createdAt'], order: json['order'], marked: json['marked'], @@ -51,7 +54,6 @@ class StudioDetailsData { 'title': title, 'description': description, 'image': image, - 'media': media, 'createdAt': createdAt, 'order': order, 'marked': marked, diff --git a/lib/routes/route_generator.dart b/lib/routes/route_generator.dart index b70afa5..308bc61 100644 --- a/lib/routes/route_generator.dart +++ b/lib/routes/route_generator.dart @@ -180,8 +180,15 @@ class RouteGenerator { final shortestSide = MediaQuery.of(context).size.shortestSide; final bool useMobileLayout = shortestSide < 600; if (kIsWeb && !useMobileLayout) { + final deviceSize = MediaQuery.of(context).size; return MediaQuery( - data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), + data: MediaQuery.of(context).copyWith( + textScaleFactor: 1.0, + size: Size( + deviceSize.width / 16 * 9, + deviceSize.height, + ), + ), child: Container( color: Theme.of(context).colorScheme.background, alignment: Alignment.center, diff --git a/lib/utils/action_sheet.dart b/lib/utils/action_sheet.dart index b8c1ad0..bbb3c2c 100644 --- a/lib/utils/action_sheet.dart +++ b/lib/utils/action_sheet.dart @@ -80,7 +80,9 @@ class ActionSheetUtils { isScrollControlled: true, context: context, builder: (context) => Container( - padding: data.hasPadding ? const EdgeInsets.all(20) : EdgeInsets.zero, + padding: data.hasPadding + ? const EdgeInsets.all(20).copyWith(top: 0) + : EdgeInsets.zero, decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.vertical( diff --git a/lib/views/home/studio/studio.dart b/lib/views/home/studio/studio.dart index 79619e8..296a552 100644 --- a/lib/views/home/studio/studio.dart +++ b/lib/views/home/studio/studio.dart @@ -126,7 +126,7 @@ class _StudioState extends State { emptyState: EmptyResult( onNewSearch: () => _focusNode.requestFocus(), ), - centerEmptyState: false, + centerEmptyState: true, enableEmptyState: state.studios.isEmpty, placeholder: state.videosSelected ? VideoOverview.placeHolder diff --git a/lib/views/home/studio/studio_details/studio_details.mobile.dart b/lib/views/home/studio/studio_details/studio_details.mobile.dart index ea72e90..5d89fca 100644 --- a/lib/views/home/studio/studio_details/studio_details.mobile.dart +++ b/lib/views/home/studio/studio_details/studio_details.mobile.dart @@ -1,10 +1,7 @@ -import 'dart:io'; - import 'package:better_player/better_player.dart'; import 'package:didvan/models/view/app_bar_data.dart'; import 'package:didvan/services/media/media.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; -import 'package:didvan/views/home/studio/studio_details/widgets/details_tab_bar.dart'; import 'package:didvan/views/home/studio/studio_details/widgets/studio_details_widget.dart'; import 'package:didvan/views/home/widgets/bookmark_button.dart'; import 'package:didvan/views/widgets/didvan/app_bar.dart'; @@ -12,8 +9,6 @@ import 'package:didvan/views/widgets/state_handlers/state_handler.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:webview_flutter/webview_flutter.dart'; - class StudioDetails extends StatefulWidget { final Map pageData; @@ -24,7 +19,8 @@ class StudioDetails extends StatefulWidget { } class _StudioDetailsState extends State { - final _scrollController = ScrollController(); + int _currentlyPlayingId = 0; + late BetterPlayerController _betterPlayerController; @override void initState() { @@ -34,15 +30,12 @@ class _StudioDetailsState extends State { Duration.zero, () => state.getStudioDetails(widget.pageData['id']), ); - - if (Platform.isAndroid) WebView.platform = AndroidWebView(); super.initState(); } @override Widget build(BuildContext context) { final d = MediaQuery.of(context); - return Consumer( builder: (context, state, child) => StateHandler( state: state, @@ -54,6 +47,32 @@ class _StudioDetailsState extends State { } }, builder: (context, state) { + if (_currentlyPlayingId != state.studio.id) { + BetterPlayerDataSource betterPlayerDataSource = + BetterPlayerDataSource( + BetterPlayerDataSourceType.network, + state.studio.link, + ); + _betterPlayerController = BetterPlayerController( + BetterPlayerConfiguration( + aspectRatio: 16 / 9, + showPlaceholderUntilPlay: true, + controlsConfiguration: BetterPlayerControlsConfiguration( + enablePlaybackSpeed: false, + enableSubtitles: false, + enableAudioTracks: false, + progressBarPlayedColor: + Theme.of(context).colorScheme.secondary, + progressBarHandleColor: + Theme.of(context).colorScheme.secondary, + ), + fit: BoxFit.contain, + fullScreenAspectRatio: 16 / 9, + ), + betterPlayerDataSource: betterPlayerDataSource, + ); + _currentlyPlayingId = state.studio.id; + } return WillPopScope( onWillPop: () async { if (MediaService.currentPodcast != null) { @@ -63,7 +82,6 @@ class _StudioDetailsState extends State { }, child: SafeArea( child: Scaffold( - key: ValueKey(state.studio.id), backgroundColor: Theme.of(context).colorScheme.surface, appBar: PreferredSize( preferredSize: const Size.fromHeight(56), @@ -84,37 +102,22 @@ class _StudioDetailsState extends State { ), ), ), - body: Column( - children: [ - Directionality( - textDirection: TextDirection.ltr, - child: BetterPlayer.network( - 'https://studio-didvan.arvanvod.com/Vz01Bxq2bQ/nylPWJ4B63/h_,144_200,240_400,360_800,480_1215,720_1215,k.mp4.list/master.m3u8', - betterPlayerConfiguration: - const BetterPlayerConfiguration( - aspectRatio: 16 / 9, - controlsConfiguration: - BetterPlayerControlsConfiguration( - enablePlaybackSpeed: false, - enableSubtitles: false, - enableAudioTracks: false, + body: SingleChildScrollView( + physics: const NeverScrollableScrollPhysics(), + child: SizedBox( + height: d.size.height - d.padding.top - 56, + child: Column( + children: [ + BetterPlayer(controller: _betterPlayerController), + Expanded( + child: StudioDetailsWidget( + onMarkChanged: (id, value) => widget + .pageData['onMarkChanged'](id, value, true), ), - autoDetectFullscreenAspectRatio: true, - autoDetectFullscreenDeviceOrientation: true, - fullScreenAspectRatio: 16 / 9, ), - ), + ], ), - const DetailsTabBar( - isVideo: true, - ), - Expanded( - child: StudioDetailsWidget( - onMarkChanged: (id, value) => - widget.pageData['onMarkChanged'](id, value, true), - ), - ), - ], + ), ), ), ), @@ -123,4 +126,10 @@ class _StudioDetailsState extends State { ), ); } + + // @override + // void dispose() { + // _betterPlayerController.dispose(); + // super.dispose(); + // } } diff --git a/lib/views/home/studio/studio_details/studio_details.web.dart b/lib/views/home/studio/studio_details/studio_details.web.dart index a1409c2..b944905 100644 --- a/lib/views/home/studio/studio_details/studio_details.web.dart +++ b/lib/views/home/studio/studio_details/studio_details.web.dart @@ -1,16 +1,13 @@ import 'dart:ui' as ui; -import 'package:didvan/config/design_config.dart'; import 'package:didvan/models/view/app_bar_data.dart'; import 'package:didvan/services/media/media.dart'; import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart'; -import 'package:didvan/views/home/studio/studio_details/widgets/details_tab_bar.dart'; import 'package:didvan/views/home/studio/studio_details/widgets/studio_details_widget.dart'; import 'package:didvan/views/home/widgets/bookmark_button.dart'; -import 'package:didvan/views/widgets/didvan/scaffold.dart'; +import 'package:didvan/views/widgets/didvan/app_bar.dart'; import 'package:didvan/views/widgets/state_handlers/state_handler.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'package:universal_html/html.dart' as html; @@ -24,8 +21,6 @@ class StudioDetails extends StatefulWidget { } class _StudioDetailsState extends State { - bool _isFullScreen = false; - @override void initState() { final state = context.read(); @@ -34,42 +29,12 @@ class _StudioDetailsState extends State { Duration.zero, () => state.getStudioDetails(widget.pageData['id']), ); - super.initState(); } - Future _changeFullSceen(bool value) async { - if (value) { - await SystemChrome.setEnabledSystemUIMode( - SystemUiMode.manual, - overlays: [], - ); - SystemChrome.setSystemUIOverlayStyle( - const SystemUiOverlayStyle( - systemNavigationBarColor: Colors.black, - ), - ); - await SystemChrome.setPreferredOrientations( - [DeviceOrientation.landscapeLeft], - ); - } else { - await SystemChrome.setEnabledSystemUIMode( - SystemUiMode.manual, - overlays: [SystemUiOverlay.bottom, SystemUiOverlay.top], - ); - await SystemChrome.setPreferredOrientations( - [DeviceOrientation.portraitUp], - ); - DesignConfig.updateSystemUiOverlayStyle(); - } - setState(() { - _isFullScreen = value; - }); - } - @override Widget build(BuildContext context) { - final ds = MediaQuery.of(context).size; + final d = MediaQuery.of(context); return Consumer( builder: (context, state, child) => StateHandler( state: state, @@ -82,66 +47,61 @@ class _StudioDetailsState extends State { ..allowFullscreen = true ..src = Uri.dataFromString( '' + - state.studio.media, + state.studio.iframe!, mimeType: 'text/html', ).toString() ..style.border = 'none', ); return WillPopScope( onWillPop: () async { - if (_isFullScreen) { - await _changeFullSceen(false); - return false; - } if (MediaService.currentPodcast != null) { state.studio = MediaService.currentPodcast!; } return true; }, - child: DidvanScaffold( - padding: EdgeInsets.zero, - appBarData: _isFullScreen - ? null - : AppBarData( - isSmall: true, - title: state.studio.title, + child: SafeArea( + child: Scaffold( + backgroundColor: Theme.of(context).colorScheme.surface, + appBar: PreferredSize( + preferredSize: const Size.fromHeight(56), + child: DidvanAppBar( + appBarData: AppBarData( trailing: BookmarkButton( itemId: state.studio.id, type: 'video', value: state.studio.marked, - onMarkChanged: (value) => widget - .pageData['onMarkChanged'](state.studio.id, value), + onMarkChanged: (value) { + widget.pageData['onMarkChanged']( + state.studio.id, value); + }, gestureSize: 48, ), + isSmall: true, + title: state.studio.title, ), - showSliversFirst: true, - slivers: [ - SliverAppBar( - automaticallyImplyLeading: false, - pinned: true, - elevation: 0, - toolbarHeight: - (_isFullScreen ? ds.height : ds.width * 9 / 16) + - 72 - - MediaQuery.of(context).padding.top, - flexibleSpace: Column( - children: const [ - AspectRatio( - aspectRatio: 16 / 9, - child: HtmlElementView(viewType: 'video'), - ), - DetailsTabBar( - isVideo: true, - ), - ], ), ), - ], - children: [ - StudioDetailsWidget( - onMarkChanged: widget.pageData['onMarkChanged'], + body: SingleChildScrollView( + physics: const NeverScrollableScrollPhysics(), + child: SizedBox( + height: d.size.height - d.padding.top - 56, + child: Column( + children: [ + const AspectRatio( + aspectRatio: 16 / 9, + child: HtmlElementView(viewType: 'video'), + ), + Expanded( + child: StudioDetailsWidget( + onMarkChanged: (id, value) => widget + .pageData['onMarkChanged'](id, value, true), + ), + ), + ], + ), + ), ), - ], + ), ), ); }, diff --git a/lib/views/home/studio/studio_details/studio_details_state.dart b/lib/views/home/studio/studio_details/studio_details_state.dart index bf7f5c8..b9aaf7a 100644 --- a/lib/views/home/studio/studio_details/studio_details_state.dart +++ b/lib/views/home/studio/studio_details/studio_details_state.dart @@ -111,7 +111,7 @@ class StudioDetailsState extends CoreProvier { MediaService.currentPodcast = studio; MediaService.podcastPlaylistArgs = args; await MediaService.handleAudioPlayback( - audioSource: studio.media, + audioSource: studio.link, id: studio.id, isVoiceMessage: false, ); diff --git a/lib/views/home/studio/studio_details/widgets/details_tab_bar.dart b/lib/views/home/studio/studio_details/widgets/details_tab_bar.dart index f5ef4d3..a16754a 100644 --- a/lib/views/home/studio/studio_details/widgets/details_tab_bar.dart +++ b/lib/views/home/studio/studio_details/widgets/details_tab_bar.dart @@ -16,7 +16,7 @@ class DetailsTabBar extends StatelessWidget { @override Widget build(BuildContext context) { - final state = context.read(); + final state = context.watch(); return WillPopScope( onWillPop: () async { state.selectedDetailsIndex = 0; @@ -35,38 +35,32 @@ class DetailsTabBar extends StatelessWidget { ) ], ), - child: FittedBox( - fit: BoxFit.scaleDown, - child: SizedBox( - width: MediaQuery.of(context).size.width, - child: Row( - children: [ - _TabItem( - icon: DidvanIcons.description_solid, - title: 'توضیحات', - onTap: () => state.selectedDetailsIndex = 0, - isSelected: state.selectedDetailsIndex == 0, - isVideo: isVideo, - ), - _TabItem( - icon: DidvanIcons.chats_solid, - title: 'نظرات', - onTap: () { - state.selectedDetailsIndex = 1; - }, - isSelected: state.selectedDetailsIndex == 1, - isVideo: isVideo, - ), - _TabItem( - icon: DidvanIcons.puzzle_solid, - title: 'مطالب مرتبط', - onTap: () => state.selectedDetailsIndex = 2, - isSelected: state.selectedDetailsIndex == 2, - isVideo: isVideo, - ), - ], + child: Row( + children: [ + _TabItem( + icon: DidvanIcons.description_solid, + title: 'توضیحات', + onTap: () => state.selectedDetailsIndex = 0, + isSelected: state.selectedDetailsIndex == 0, + isVideo: isVideo, ), - ), + _TabItem( + icon: DidvanIcons.chats_solid, + title: 'نظرات', + onTap: () { + state.selectedDetailsIndex = 1; + }, + isSelected: state.selectedDetailsIndex == 1, + isVideo: isVideo, + ), + _TabItem( + icon: DidvanIcons.puzzle_solid, + title: 'مطالب مرتبط', + onTap: () => state.selectedDetailsIndex = 2, + isSelected: state.selectedDetailsIndex == 2, + isVideo: isVideo, + ), + ], ), ), ); @@ -106,6 +100,7 @@ class _TabItem extends StatelessWidget { child: Container( color: Colors.transparent, child: Column( + mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( icon, diff --git a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart index bdfc44e..268309f 100644 --- a/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart +++ b/lib/views/home/studio/studio_details/widgets/studio_details_widget.dart @@ -1,4 +1,5 @@ -import 'package:didvan/config/design_config.dart'; +import 'dart:math'; + import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/enums.dart'; @@ -28,38 +29,34 @@ class StudioDetailsWidget extends StatelessWidget { @override Widget build(BuildContext context) { final ds = MediaQuery.of(context).size; + return SafeArea( bottom: true, child: Consumer( builder: (context, state, child) { - bool isVideo = state.studio.media.contains('iframe'); + bool isVideo = state.studio.iframe != null; return Container( + height: max( + ds.height - + ds.width * 9 / 16 - + 72 - + MediaQuery.of(context).padding.top, + 0), color: Theme.of(context).colorScheme.surface, - child: Column( - mainAxisSize: MainAxisSize.min, + child: Stack( children: [ - if (!isVideo) - DetailsTabBar( - isVideo: isVideo, - ), - const SizedBox(height: 16), - ConstrainedBox( - constraints: BoxConstraints( - maxHeight: isVideo - ? double.infinity - : ds.height - - ds.width * 9 / 16 - - 144 - - MediaQuery.of(context).padding.top, - ), + Positioned( + top: 72, + left: 0, + right: 0, + bottom: 0, child: StateHandler( onRetry: () {}, state: state, builder: (context, state) { if (state.selectedDetailsIndex == 0) { return SingleChildScrollView( - physics: const BouncingScrollPhysics(), - padding: const EdgeInsets.symmetric(horizontal: 16), + padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, @@ -128,50 +125,57 @@ class StudioDetailsWidget extends StatelessWidget { if (state.selectedDetailsIndex == 1) { return ChangeNotifierProvider( create: (context) => CommentsState(), - child: Comments( - pageData: { - 'id': state.studio.id, - 'type': 'studio', - 'title': state.studio.title, - 'onCommentsChanged': state.onCommentsChanged, - 'isPage': false, - }, + child: SizedBox( + height: ds.height - + ds.width * 9 / 16 - + 172 - + MediaQuery.of(context).padding.top, + child: Comments( + pageData: { + 'id': state.studio.id, + 'type': 'studio', + 'title': state.studio.title, + 'onCommentsChanged': state.onCommentsChanged, + 'isPage': false, + }, + ), ), ); } - return SingleChildScrollView( - child: Column( - children: [ - if (state.studio.relatedContents.isEmpty) - for (var i = 0; i < 3; i++) - Padding( - padding: const EdgeInsets.only( - bottom: 8, - left: 16, - right: 16, - ), - child: MultitypeOverview.placeholder, - ), - for (var i = 0; - i < state.studio.relatedContents.length; - i++) + return Column( + children: [ + if (state.studio.relatedContents.isEmpty) + for (var i = 0; i < 3; i++) Padding( padding: const EdgeInsets.only( bottom: 8, left: 16, right: 16, ), - child: MultitypeOverview( - item: state.studio.relatedContents[i], - onMarkChanged: (id, value) {}, - ), + child: MultitypeOverview.placeholder, ), - ], - ), + for (var i = 0; + i < state.studio.relatedContents.length; + i++) + Padding( + padding: const EdgeInsets.only( + bottom: 8, + left: 16, + right: 16, + ), + child: MultitypeOverview( + item: state.studio.relatedContents[i], + onMarkChanged: (id, value) {}, + ), + ), + ], ); }, ), ), + DetailsTabBar( + isVideo: isVideo, + ), ], ), ); @@ -202,7 +206,7 @@ class _StudioPreview extends StatelessWidget { }) : super(key: key); String get _previewTitle { - if (studio.media.contains('iframe')) { + if (studio.iframe != null) { return 'ویدئو ${isNext ? 'بعدی' : 'قبلی'} '; } return 'پادکست ${isNext ? 'بعدی' : 'قبلی'} '; diff --git a/lib/views/home/widgets/audio/audio_player_widget.dart b/lib/views/home/widgets/audio/audio_player_widget.dart index 0b95eba..5012e47 100644 --- a/lib/views/home/widgets/audio/audio_player_widget.dart +++ b/lib/views/home/widgets/audio/audio_player_widget.dart @@ -129,7 +129,7 @@ class AudioPlayerWidget extends StatelessWidget { stream: MediaService.audioPlayer.playingStream, builder: (context, snapshot) { return _PlayPouseAnimatedIcon( - audioSource: podcast.media, + audioSource: podcast.link, id: podcast.id, ); }, diff --git a/lib/views/home/widgets/overview/podcast.dart b/lib/views/home/widgets/overview/podcast.dart index 94cd330..24ce673 100644 --- a/lib/views/home/widgets/overview/podcast.dart +++ b/lib/views/home/widgets/overview/podcast.dart @@ -83,7 +83,7 @@ class PodcastOverview extends StatelessWidget { const Spacer(), if (!kIsWeb) ...[ if (state.appState == AppState.idle || - !state.downloadQueue.contains(podcast.media)) + !state.downloadQueue.contains(podcast.link)) DidvanIconButton( gestureSize: 28, color: _isDownloaded @@ -97,11 +97,11 @@ class PodcastOverview extends StatelessWidget { : () => state.download( fileName: 'podcast-${podcast.id}.mp3', isVideo: false, - url: podcast.media!, + url: podcast.link!, ), ), if (state.appState == AppState.busy && - state.downloadQueue.contains(podcast.media)) + state.downloadQueue.contains(podcast.link)) const SizedBox( width: 18, height: 18, diff --git a/lib/views/widgets/didvan/bnb.dart b/lib/views/widgets/didvan/bnb.dart index 28c2d61..bcc6682 100644 --- a/lib/views/widgets/didvan/bnb.dart +++ b/lib/views/widgets/didvan/bnb.dart @@ -248,7 +248,7 @@ class _PlayerNavBar extends StatelessWidget { ); } MediaService.handleAudioPlayback( - audioSource: MediaService.currentPodcast!.media, + audioSource: MediaService.currentPodcast!.link, id: MediaService.currentPodcast!.id, isVoiceMessage: false, ); From 7f615280ace129f1ccd56a20262443d065f801bd Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Tue, 5 Apr 2022 01:23:23 +0430 Subject: [PATCH 48/54] bug fixes --- .../studio_details/studio_details.mobile.dart | 63 ++++++++++--------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/lib/views/home/studio/studio_details/studio_details.mobile.dart b/lib/views/home/studio/studio_details/studio_details.mobile.dart index 5d89fca..2c34596 100644 --- a/lib/views/home/studio/studio_details/studio_details.mobile.dart +++ b/lib/views/home/studio/studio_details/studio_details.mobile.dart @@ -24,6 +24,14 @@ class _StudioDetailsState extends State { @override void initState() { + _betterPlayerController = BetterPlayerController( + const BetterPlayerConfiguration( + aspectRatio: 16 / 9, + showPlaceholderUntilPlay: true, + autoDispose: false, + fullScreenAspectRatio: 16 / 9, + ), + ); final state = context.read(); state.args = widget.pageData['args']; Future.delayed( @@ -48,30 +56,7 @@ class _StudioDetailsState extends State { }, builder: (context, state) { if (_currentlyPlayingId != state.studio.id) { - BetterPlayerDataSource betterPlayerDataSource = - BetterPlayerDataSource( - BetterPlayerDataSourceType.network, - state.studio.link, - ); - _betterPlayerController = BetterPlayerController( - BetterPlayerConfiguration( - aspectRatio: 16 / 9, - showPlaceholderUntilPlay: true, - controlsConfiguration: BetterPlayerControlsConfiguration( - enablePlaybackSpeed: false, - enableSubtitles: false, - enableAudioTracks: false, - progressBarPlayedColor: - Theme.of(context).colorScheme.secondary, - progressBarHandleColor: - Theme.of(context).colorScheme.secondary, - ), - fit: BoxFit.contain, - fullScreenAspectRatio: 16 / 9, - ), - betterPlayerDataSource: betterPlayerDataSource, - ); - _currentlyPlayingId = state.studio.id; + _handleVideoPlayback(state); } return WillPopScope( onWillPop: () async { @@ -127,9 +112,29 @@ class _StudioDetailsState extends State { ); } - // @override - // void dispose() { - // _betterPlayerController.dispose(); - // super.dispose(); - // } + Future _handleVideoPlayback(state) async { + final betterPlayerDataSource = BetterPlayerDataSource( + BetterPlayerDataSourceType.network, + state.studio.link, + ); + await _betterPlayerController.clearCache(); + await _betterPlayerController.setupDataSource(betterPlayerDataSource); + _betterPlayerController.setBetterPlayerControlsConfiguration( + BetterPlayerControlsConfiguration( + enablePlaybackSpeed: false, + enableSubtitles: false, + enableAudioTracks: false, + progressBarPlayedColor: Theme.of(context).colorScheme.secondary, + progressBarHandleColor: Theme.of(context).colorScheme.secondary, + ), + ); + _currentlyPlayingId = state.studio.id; + } + + @override + void dispose() { + _betterPlayerController.pause(); + _betterPlayerController.dispose(); + super.dispose(); + } } From 2836b6f335cff297007587fd5f82f1df44bd4e1e Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Tue, 5 Apr 2022 13:52:21 +0430 Subject: [PATCH 49/54] bug fixes --- lib/views/home/studio/studio_details/studio_details_state.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/views/home/studio/studio_details/studio_details_state.dart b/lib/views/home/studio/studio_details/studio_details_state.dart index b9aaf7a..110c93f 100644 --- a/lib/views/home/studio/studio_details/studio_details_state.dart +++ b/lib/views/home/studio/studio_details/studio_details_state.dart @@ -132,6 +132,8 @@ class StudioDetailsState extends CoreProvier { } }); } + } else { + MediaService.audioPlayer.pause(); } } From 6d55a3cf52f63db6943c2541eca7d296b6b1adb6 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Tue, 5 Apr 2022 22:30:39 +0430 Subject: [PATCH 50/54] new audio player package basic implementaion --- android/app/src/main/AndroidManifest.xml | 24 +++++++--- ios/Podfile.lock | 28 +++++------ ios/Runner/Info.plist | 4 ++ lib/services/app_initalizer.dart | 2 - lib/services/media/media.dart | 46 ++++++++----------- .../home/direct/widgets/audio_widget.dart | 4 +- .../studio_details/studio_details_state.dart | 6 +-- .../widgets/audio/audio_player_widget.dart | 22 ++++----- .../home/widgets/audio/audio_slider.dart | 11 ++--- lib/views/widgets/didvan/bnb.dart | 15 +++--- pubspec.lock | 42 ++++++----------- pubspec.yaml | 2 +- 12 files changed, 95 insertions(+), 111 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 85b3ee9..9f4f7dd 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -4,6 +4,8 @@ + + + android:windowSoftInputMode="adjustResize" + android:name="com.ryanheise.audioservice.AudioServiceActivity" + android:name="com.yalantis.ucrop.UCropActivity" + android:screenOrientation="portrait" + android:theme="@style/Theme.AppCompat.Light.NoActionBar"> + diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 5e2c2df..f1504a2 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,5 +1,7 @@ PODS: - - audio_session (0.0.1): + - assets_audio_player (0.0.1): + - Flutter + - assets_audio_player_web (0.0.1): - Flutter - better_player (0.0.1): - Cache (~> 6.0.0) @@ -83,8 +85,6 @@ PODS: - TOCropViewController (~> 2.6.1) - image_picker (0.0.1): - Flutter - - just_audio (0.0.1): - - Flutter - nanopb (2.30908.0): - nanopb/decode (= 2.30908.0) - nanopb/encode (= 2.30908.0) @@ -111,15 +111,14 @@ PODS: - TOCropViewController (2.6.1) - url_launcher_ios (0.0.1): - Flutter - - video_player_avfoundation (0.0.1): - - Flutter - wakelock (0.0.1): - Flutter - webview_flutter_wkwebview (0.0.1): - Flutter DEPENDENCIES: - - audio_session (from `.symlinks/plugins/audio_session/ios`) + - assets_audio_player (from `.symlinks/plugins/assets_audio_player/ios`) + - assets_audio_player_web (from `.symlinks/plugins/assets_audio_player_web/ios`) - better_player (from `.symlinks/plugins/better_player/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) @@ -128,13 +127,11 @@ DEPENDENCIES: - flutter_vibrate (from `.symlinks/plugins/flutter_vibrate/ios`) - image_cropper (from `.symlinks/plugins/image_cropper/ios`) - image_picker (from `.symlinks/plugins/image_picker/ios`) - - just_audio (from `.symlinks/plugins/just_audio/ios`) - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - record (from `.symlinks/plugins/record/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`) - wakelock (from `.symlinks/plugins/wakelock/ios`) - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`) @@ -158,8 +155,10 @@ SPEC REPOS: - TOCropViewController EXTERNAL SOURCES: - audio_session: - :path: ".symlinks/plugins/audio_session/ios" + assets_audio_player: + :path: ".symlinks/plugins/assets_audio_player/ios" + assets_audio_player_web: + :path: ".symlinks/plugins/assets_audio_player_web/ios" better_player: :path: ".symlinks/plugins/better_player/ios" firebase_core: @@ -176,8 +175,6 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/image_cropper/ios" image_picker: :path: ".symlinks/plugins/image_picker/ios" - just_audio: - :path: ".symlinks/plugins/just_audio/ios" path_provider_ios: :path: ".symlinks/plugins/path_provider_ios/ios" permission_handler_apple: @@ -188,15 +185,14 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/sqflite/ios" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" - video_player_avfoundation: - :path: ".symlinks/plugins/video_player_avfoundation/ios" wakelock: :path: ".symlinks/plugins/wakelock/ios" webview_flutter_wkwebview: :path: ".symlinks/plugins/webview_flutter_wkwebview/ios" SPEC CHECKSUMS: - audio_session: 4f3e461722055d21515cf3261b64c973c062f345 + assets_audio_player: edee322b9cb625571b830b35872ead1a295fd917 + assets_audio_player_web: 19826380c44375761aa0b9053665c1e3fbc3b86b better_player: 2406bfe8175203c7a46fa15f9d778d73b12e1646 Cache: 4ca7e00363fca5455f26534e5607634c820ffc2d Firebase: 7e8fe528c161b9271d365217a74c16aaf834578e @@ -216,7 +212,6 @@ SPEC CHECKSUMS: HLSCachingReverseProxyServer: 59935e1e0244ad7f3375d75b5ef46e8eb26ab181 image_cropper: 60c2789d1f1a78c873235d4319ca0c34a69f2d98 image_picker: 541dcbb3b9cf32d87eacbd957845d8651d6c62c3 - just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce @@ -227,7 +222,6 @@ SPEC CHECKSUMS: sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863 url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de - video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f webview_flutter_wkwebview: 005fbd90c888a42c5690919a1527ecc6649e1162 diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 17f2a3a..d87332a 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -28,6 +28,10 @@ Main FirebaseAppDelegateProxyEnabled + UIBackgroundModes + + audio + NSMicrophoneUsageDescription We need to access to the microphone to record audio file NSPhotoLibraryUsageDescription diff --git a/lib/services/app_initalizer.dart b/lib/services/app_initalizer.dart index 33c0170..749460e 100644 --- a/lib/services/app_initalizer.dart +++ b/lib/services/app_initalizer.dart @@ -1,5 +1,4 @@ import 'package:didvan/models/settings_data.dart'; -import 'package:didvan/services/media/media.dart'; import 'package:didvan/services/storage/storage.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; @@ -15,7 +14,6 @@ class AppInitializer { StorageService.appDocsDir = (await getApplicationDocumentsDirectory()).path; StorageService.appTempsDir = (await getTemporaryDirectory()).path; - MediaService.init(); } } diff --git a/lib/services/media/media.dart b/lib/services/media/media.dart index 5e6d816..e70e2de 100644 --- a/lib/services/media/media.dart +++ b/lib/services/media/media.dart @@ -6,22 +6,15 @@ import 'package:didvan/services/network/request_helper.dart'; import 'package:didvan/services/storage/storage.dart'; import 'package:flutter/foundation.dart'; import 'package:image_picker/image_picker.dart'; -import 'package:just_audio/just_audio.dart'; +import 'package:assets_audio_player/assets_audio_player.dart'; class MediaService { - static final AudioPlayer audioPlayer = AudioPlayer(); + static final audioPlayer = AssetsAudioPlayer(); static String? audioPlayerTag; static StudioDetailsData? currentPodcast; static StudioRequestArgs? podcastPlaylistArgs; - static void init() { - audioPlayer.positionStream.listen((event) { - if (audioPlayer.duration != null && audioPlayer.duration! < event) { - audioPlayer.stop(); - audioPlayer.seek(const Duration(seconds: 0)); - } - }); - } + static Duration? get duration => audioPlayer.current.value?.audio.duration; static Future handleAudioPlayback({ required dynamic audioSource, @@ -37,31 +30,32 @@ class MediaService { isNetworkAudio = false; } if (audioPlayerTag == tag) { - if (audioPlayer.playing) { - await audioPlayer.pause(); - } else { - await audioPlayer.play(); - } - } else { - await audioPlayer.stop(); - audioPlayerTag = tag; - if (isNetworkAudio) { - await audioPlayer.setUrl( + await audioPlayer.playOrPause(); + return; + } + await audioPlayer.stop(); + audioPlayerTag = tag; + if (isNetworkAudio) { + await audioPlayer.open( + Audio.network( isVoiceMessage ? (RequestHelper.baseUrl + audioSource + '?accessToken=${RequestService.token}') : audioSource, + ), + ); + } else { + if (kIsWeb) { + await audioPlayer.open( + Audio.network(audioSource!.replaceAll('%3A', ':')), ); } else { - if (kIsWeb) { - await audioPlayer.setUrl(audioSource!.replaceAll('%3A', ':')); - } else { - await audioPlayer.setFilePath(audioSource); - } + await audioPlayer.open(Audio.file(audioSource)); } - audioPlayer.play(); } + audioPlayer.play(); + audioPlayer.updateCurrentAudioNotification(); } static Future resetAudioPlayer() async { diff --git a/lib/views/home/direct/widgets/audio_widget.dart b/lib/views/home/direct/widgets/audio_widget.dart index 5d99777..5f1c8bd 100644 --- a/lib/views/home/direct/widgets/audio_widget.dart +++ b/lib/views/home/direct/widgets/audio_widget.dart @@ -21,7 +21,7 @@ class AudioWidget extends StatelessWidget { @override Widget build(BuildContext context) { return StreamBuilder( - stream: MediaService.audioPlayer.playingStream, + stream: MediaService.audioPlayer.isPlaying, builder: (context, snapshot) { return Row( children: [ @@ -56,7 +56,7 @@ class _AudioControllerButton extends StatelessWidget { @override Widget build(BuildContext context) { return DidvanIconButton( - icon: MediaService.audioPlayer.playing == true && _nowPlaying + icon: MediaService.audioPlayer.isPlaying.value && _nowPlaying ? DidvanIcons.pause_circle_solid : DidvanIcons.play_circle_solid, color: Theme.of(context).colorScheme.focusedBorder, diff --git a/lib/views/home/studio/studio_details/studio_details_state.dart b/lib/views/home/studio/studio_details/studio_details_state.dart index 110c93f..14187cf 100644 --- a/lib/views/home/studio/studio_details/studio_details_state.dart +++ b/lib/views/home/studio/studio_details/studio_details_state.dart @@ -117,12 +117,12 @@ class StudioDetailsState extends CoreProvier { ); if (nextStudio != null && !_positionListenerActivated) { _positionListenerActivated = true; - MediaService.audioPlayer.positionStream.listen((event) { + MediaService.audioPlayer.currentPosition.listen((event) { if (MediaService.audioPlayerTag?.contains('message') == true) { return; } - final duration = MediaService.audioPlayer.duration ?? - Duration(seconds: studio.duration); + final duration = + MediaService.duration ?? Duration(seconds: studio.duration); if (event.compareTo(duration) > 0 && nextStudio != null) { if (stopOnPodcastEnds) { MediaService.resetAudioPlayer(); diff --git a/lib/views/home/widgets/audio/audio_player_widget.dart b/lib/views/home/widgets/audio/audio_player_widget.dart index 5012e47..78db446 100644 --- a/lib/views/home/widgets/audio/audio_player_widget.dart +++ b/lib/views/home/widgets/audio/audio_player_widget.dart @@ -107,9 +107,9 @@ class AudioPlayerWidget extends StatelessWidget { onPressed: () { MediaService.audioPlayer.seek( Duration( - seconds: - MediaService.audioPlayer.position.inSeconds + - 30, + seconds: MediaService.audioPlayer.currentPosition + .value.inSeconds + + 30, ), ); }, @@ -126,7 +126,7 @@ class AudioPlayerWidget extends StatelessWidget { Expanded( child: Center( child: StreamBuilder( - stream: MediaService.audioPlayer.playingStream, + stream: MediaService.audioPlayer.isPlaying, builder: (context, snapshot) { return _PlayPouseAnimatedIcon( audioSource: podcast.link, @@ -149,7 +149,8 @@ class AudioPlayerWidget extends StatelessWidget { Duration( seconds: max( 0, - MediaService.audioPlayer.position.inSeconds - + MediaService.audioPlayer.currentPosition.value + .inSeconds - 10, ), ), @@ -254,16 +255,15 @@ class AudioPlayerWidget extends StatelessWidget { Padding( padding: const EdgeInsets.symmetric(horizontal: 64), child: DidvanButton( - style: state.timerValue == - MediaService.audioPlayer.duration?.inMinutes && + style: state.timerValue == MediaService.duration?.inMinutes && state.stopOnPodcastEnds ? ButtonStyleMode.primary : ButtonStyleMode.flat, title: 'پایان پادکست', onPressed: () async { - state.timerValue = - MediaService.audioPlayer.duration!.inMinutes - - MediaService.audioPlayer.position.inMinutes; + state.timerValue = MediaService.duration!.inMinutes - + MediaService + .audioPlayer.currentPosition.value.inMinutes; await controller.animateTo( state.timerValue * 10, duration: DesignConfig.lowAnimationDuration, @@ -337,7 +337,7 @@ class __PlayPouseAnimatedIconState extends State<_PlayPouseAnimatedIcon> } void _handleAnimation() { - if (MediaService.audioPlayer.playing) { + if (MediaService.audioPlayer.isPlaying.value) { _animationController.forward(); } else { _animationController.reverse(); diff --git a/lib/views/home/widgets/audio/audio_slider.dart b/lib/views/home/widgets/audio/audio_slider.dart index a65e126..bf8969e 100644 --- a/lib/views/home/widgets/audio/audio_slider.dart +++ b/lib/views/home/widgets/audio/audio_slider.dart @@ -26,7 +26,10 @@ class AudioSlider extends StatelessWidget { child: Directionality( textDirection: TextDirection.ltr, child: StreamBuilder( - stream: _isPlaying ? MediaService.audioPlayer.positionStream : null, + stream: + _isPlaying && MediaService.audioPlayer.currentPosition.hasValue + ? MediaService.audioPlayer.currentPosition + : null, builder: (context, snapshot) => ProgressBar( thumbColor: Theme.of(context).colorScheme.title, progressBarColor: DesignConfig.isDark @@ -34,12 +37,8 @@ class AudioSlider extends StatelessWidget { : Theme.of(context).colorScheme.primary, baseBarColor: Theme.of(context).colorScheme.border, bufferedBarColor: Theme.of(context).colorScheme.splash, - total: MediaService.audioPlayer.duration ?? - Duration(seconds: duration ?? 0), + total: MediaService.duration ?? Duration(seconds: duration ?? 0), progress: snapshot.data ?? Duration.zero, - buffered: _isPlaying - ? MediaService.audioPlayer.bufferedPosition - : Duration.zero, thumbRadius: disableThumb ? 0 : 6, barHeight: 3, timeLabelTextStyle: TextStyle( diff --git a/lib/views/widgets/didvan/bnb.dart b/lib/views/widgets/didvan/bnb.dart index bcc6682..300ed8f 100644 --- a/lib/views/widgets/didvan/bnb.dart +++ b/lib/views/widgets/didvan/bnb.dart @@ -1,3 +1,4 @@ +import 'package:assets_audio_player/assets_audio_player.dart'; import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; @@ -14,7 +15,6 @@ import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:expandable_bottom_sheet/expandable_bottom_sheet.dart'; import 'package:flutter/material.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; -import 'package:just_audio/just_audio.dart'; import 'package:provider/provider.dart'; class DidvanBNB extends StatelessWidget { @@ -106,7 +106,7 @@ class _PlayerNavBar extends StatelessWidget { @override Widget build(BuildContext context) { return StreamBuilder( - stream: MediaService.audioPlayer.playingStream, + stream: MediaService.audioPlayer.isPlaying, builder: (context, snapshot) => GestureDetector( onTap: () => MediaService.currentPodcast == null ? null @@ -195,12 +195,10 @@ class _PlayerNavBar extends StatelessWidget { ], ), ), - StreamBuilder( - stream: MediaService.audioPlayer.playerStateStream, + StreamBuilder( + stream: MediaService.audioPlayer.onReadyToPlay, builder: (context, snapshot) { - final playerState = MediaService - .audioPlayer.playerState.processingState; - if (playerState == ProcessingState.loading || + if (snapshot.data == null || state.appState == AppState.busy) { return Padding( padding: const EdgeInsets.only( @@ -224,8 +222,7 @@ class _PlayerNavBar extends StatelessWidget { }, ), if (state.appState != AppState.busy && - MediaService.audioPlayer.playerState.processingState != - ProcessingState.loading) + snapshot.data != null) Padding( padding: const EdgeInsets.only( left: 12, diff --git a/pubspec.lock b/pubspec.lock index cbb3c93..f08bc28 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,20 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + assets_audio_player: + dependency: "direct main" + description: + name: assets_audio_player + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.4+1" + assets_audio_player_web: + dependency: transitive + description: + name: assets_audio_player_web + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.4+1" async: dependency: transitive description: @@ -8,13 +22,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.8.2" - audio_session: - dependency: transitive - description: - name: audio_session - url: "https://pub.dartlang.org" - source: hosted - version: "0.1.6+1" audio_video_progress_bar: dependency: "direct main" description: @@ -406,27 +413,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.6.3" - just_audio: - dependency: "direct main" - description: - name: just_audio - url: "https://pub.dartlang.org" - source: hosted - version: "0.9.20" - just_audio_platform_interface: - dependency: transitive - description: - name: just_audio_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "4.1.0" - just_audio_web: - dependency: transitive - description: - name: just_audio_web - url: "https://pub.dartlang.org" - source: hosted - version: "0.4.7" lints: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 63202fa..dfc7dfa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -50,7 +50,6 @@ dependencies: flutter_vibrate: ^1.3.0 universal_html: ^2.0.8 record: ^3.0.2 - just_audio: ^0.9.18 record_web: ^0.2.1 persian_datetime_picker: ^2.4.0 persian_number_utility: ^1.1.1 @@ -66,6 +65,7 @@ dependencies: expandable_bottom_sheet: ^1.1.1+1 permission_handler: ^9.2.0 better_player: ^0.0.81 + assets_audio_player: ^3.0.4+1 dev_dependencies: From 7303234c81a096f7852654f1297db20c0c7e933e Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Tue, 5 Apr 2022 23:30:49 +0430 Subject: [PATCH 51/54] manifest cleaned --- android/app/src/main/AndroidManifest.xml | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 9f4f7dd..0e7557a 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -4,8 +4,6 @@ - - - + android:windowSoftInputMode="adjustResize"> From ae560b90a029ead973a12f9c5ea2c8436bf8b0bc Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Wed, 6 Apr 2022 01:33:26 +0430 Subject: [PATCH 52/54] new audio player configured --- lib/services/media/media.dart | 57 ++++++++++++------- .../studio_details/studio_details_state.dart | 17 ++++-- lib/views/widgets/didvan/bnb.dart | 6 +- 3 files changed, 55 insertions(+), 25 deletions(-) diff --git a/lib/services/media/media.dart b/lib/services/media/media.dart index e70e2de..9b6a448 100644 --- a/lib/services/media/media.dart +++ b/lib/services/media/media.dart @@ -1,3 +1,4 @@ +import 'package:didvan/constants/assets.dart'; import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/models/studio_details_data.dart'; import 'package:didvan/providers/media.dart'; @@ -19,12 +20,12 @@ class MediaService { static Future handleAudioPlayback({ required dynamic audioSource, required int id, - bool? isNetworkAudio, + bool isNetworkAudio = true, bool isVoiceMessage = true, + void Function(bool isNext)? onTrackChanged, }) async { String tag; tag = '${isVoiceMessage ? 'message' : 'podcast'}-$id'; - isNetworkAudio ??= audioSource.runtimeType == String; if (!isVoiceMessage && MediaProvider.downloadedItemIds.contains(id)) { audioSource = StorageService.appDocsDir + '/podcasts/podcast-$id.mp3'; isNetworkAudio = false; @@ -35,27 +36,45 @@ class MediaService { } await audioPlayer.stop(); audioPlayerTag = tag; + Audio audio; + String source; if (isNetworkAudio) { - await audioPlayer.open( - Audio.network( - isVoiceMessage - ? (RequestHelper.baseUrl + - audioSource + - '?accessToken=${RequestService.token}') - : audioSource, - ), + if (isVoiceMessage) { + source = RequestHelper.baseUrl + + audioSource + + '?accessToken=${RequestService.token}'; + } else { + source = audioSource; + } + audio = Audio.network( + kIsWeb ? source.replaceAll('%3A', ':') : source, + metas: isVoiceMessage + ? null + : Metas( + artist: 'استودیو دیدوان', + title: currentPodcast!.title, + ), ); } else { - if (kIsWeb) { - await audioPlayer.open( - Audio.network(audioSource!.replaceAll('%3A', ':')), - ); - } else { - await audioPlayer.open(Audio.file(audioSource)); - } + audio = Audio.file( + audioSource, + metas: isVoiceMessage + ? null + : Metas( + artist: 'استودیو دیدوان', + title: currentPodcast!.title, + ), + ); } - audioPlayer.play(); - audioPlayer.updateCurrentAudioNotification(); + await audioPlayer.open( + audio, + showNotification: !isVoiceMessage, + notificationSettings: NotificationSettings( + customStopAction: (_) => resetAudioPlayer(), + customNextAction: (_) => onTrackChanged?.call(true), + customPrevAction: (_) => onTrackChanged?.call(false), + ), + ); } static Future resetAudioPlayer() async { diff --git a/lib/views/home/studio/studio_details/studio_details_state.dart b/lib/views/home/studio/studio_details/studio_details_state.dart index 14187cf..e4f85bd 100644 --- a/lib/views/home/studio/studio_details/studio_details_state.dart +++ b/lib/views/home/studio/studio_details/studio_details_state.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:developer'; import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; @@ -38,7 +39,7 @@ class StudioDetailsState extends CoreProvier { int id, { StudioRequestArgs? args, bool? isForward, - bool forceFetch = false, + bool fetchOnly = false, }) async { if (args != null) { this.args = args; @@ -48,7 +49,7 @@ class StudioDetailsState extends CoreProvier { } if (MediaService.currentPodcast?.id == id && this.args.type == 'podcast' && - !forceFetch) { + !fetchOnly) { return; } _selectedDetailsIndex = 0; @@ -67,7 +68,8 @@ class StudioDetailsState extends CoreProvier { } if (isForward == null) { if (this.args.type == 'podcast') { - MediaService.audioPlayerTag = 'podcast'; + MediaService.audioPlayerTag = + 'podcast-${MediaService.currentPodcast?.id ?? ''}'; } appState = AppState.busy; } else { @@ -91,7 +93,7 @@ class StudioDetailsState extends CoreProvier { if (result['prevStudio'].isNotEmpty && this.args.page != 0) { prevStudio = StudioDetailsData.fromJson(result['prevStudio']); } - if (isForward == null && !forceFetch) { + if (isForward == null && !fetchOnly) { await _handlePodcastPlayback(studio); } alongSideState = AppState.idle; @@ -114,6 +116,13 @@ class StudioDetailsState extends CoreProvier { audioSource: studio.link, id: studio.id, isVoiceMessage: false, + onTrackChanged: (isNext) { + if (isNext && nextStudio != null) { + getStudioDetails(nextStudio!.id); + } else if (!isNext && prevStudio != null) { + getStudioDetails(prevStudio!.id); + } + }, ); if (nextStudio != null && !_positionListenerActivated) { _positionListenerActivated = true; diff --git a/lib/views/widgets/didvan/bnb.dart b/lib/views/widgets/didvan/bnb.dart index 300ed8f..3fb052c 100644 --- a/lib/views/widgets/didvan/bnb.dart +++ b/lib/views/widgets/didvan/bnb.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:assets_audio_player/assets_audio_player.dart'; import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; @@ -241,7 +243,7 @@ class _PlayerNavBar extends StatelessWidget { state.getStudioDetails( MediaService.currentPodcast!.id, args: state.podcastArgs, - forceFetch: true, + fetchOnly: true, ); } MediaService.handleAudioPlayback( @@ -270,7 +272,7 @@ class _PlayerNavBar extends StatelessWidget { detailsState.getStudioDetails( MediaService.currentPodcast!.id, args: detailsState.podcastArgs, - forceFetch: true, + fetchOnly: true, ); } final state = context.read(); From 9773a514c30eea65fa2cac4b93f01ab473ba6c34 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Wed, 6 Apr 2022 14:16:36 +0430 Subject: [PATCH 53/54] code cleaning --- lib/services/media/media.dart | 1 - lib/views/home/studio/studio_details/studio_details_state.dart | 1 - lib/views/widgets/didvan/bnb.dart | 2 -- 3 files changed, 4 deletions(-) diff --git a/lib/services/media/media.dart b/lib/services/media/media.dart index 9b6a448..a783e23 100644 --- a/lib/services/media/media.dart +++ b/lib/services/media/media.dart @@ -1,4 +1,3 @@ -import 'package:didvan/constants/assets.dart'; import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/models/studio_details_data.dart'; import 'package:didvan/providers/media.dart'; diff --git a/lib/views/home/studio/studio_details/studio_details_state.dart b/lib/views/home/studio/studio_details/studio_details_state.dart index e4f85bd..1d4e994 100644 --- a/lib/views/home/studio/studio_details/studio_details_state.dart +++ b/lib/views/home/studio/studio_details/studio_details_state.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:developer'; import 'package:didvan/models/enums.dart'; import 'package:didvan/models/overview_data.dart'; diff --git a/lib/views/widgets/didvan/bnb.dart b/lib/views/widgets/didvan/bnb.dart index 3fb052c..a89de8f 100644 --- a/lib/views/widgets/didvan/bnb.dart +++ b/lib/views/widgets/didvan/bnb.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:assets_audio_player/assets_audio_player.dart'; import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; From b23d9e5f98ecf9fc0f66550932adfdcece7a2d56 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Fri, 8 Apr 2022 15:45:39 +0430 Subject: [PATCH 54/54] bug fixes --- .../home/studio/studio_details/widgets/details_tab_bar.dart | 2 +- lib/views/home/widgets/audio/audio_player_widget.dart | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/views/home/studio/studio_details/widgets/details_tab_bar.dart b/lib/views/home/studio/studio_details/widgets/details_tab_bar.dart index a16754a..ba160e7 100644 --- a/lib/views/home/studio/studio_details/widgets/details_tab_bar.dart +++ b/lib/views/home/studio/studio_details/widgets/details_tab_bar.dart @@ -89,7 +89,7 @@ class _TabItem extends StatelessWidget { } return Theme.of(context).colorScheme.focusedBorder; } - return Theme.of(context).colorScheme.border; + return Theme.of(context).colorScheme.hint; } @override diff --git a/lib/views/home/widgets/audio/audio_player_widget.dart b/lib/views/home/widgets/audio/audio_player_widget.dart index 78db446..79d1aff 100644 --- a/lib/views/home/widgets/audio/audio_player_widget.dart +++ b/lib/views/home/widgets/audio/audio_player_widget.dart @@ -285,6 +285,11 @@ class AudioPlayerWidget extends StatelessWidget { timerValue--; if (timerValue == 0) { MediaService.audioPlayer.stop(); + state.stopOnPodcastEnds = false; + state.timer?.cancel(); + state.timer = null; + state.timerValue = 10; + state.update(); } }, );