From d869f543388747e13310363afd107c92791d9409 Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Fri, 25 Mar 2022 19:23:37 +0430 Subject: [PATCH] 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: