bug fixes
This commit is contained in:
parent
873c1f6692
commit
4df58d093a
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:bot_toast/bot_toast.dart';
|
import 'package:bot_toast/bot_toast.dart';
|
||||||
import 'package:didvan/config/theme_data.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/theme_provider.dart';
|
||||||
import 'package:didvan/providers/user_provider.dart';
|
import 'package:didvan/providers/user_provider.dart';
|
||||||
import 'package:didvan/routes/route_generator.dart';
|
import 'package:didvan/routes/route_generator.dart';
|
||||||
|
|
@ -21,6 +22,9 @@ class Didvan extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MultiProvider(
|
return MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
|
ChangeNotifierProvider<MediaProvider>(
|
||||||
|
create: (context) => MediaProvider(),
|
||||||
|
),
|
||||||
ChangeNotifierProvider<UserProvider>(
|
ChangeNotifierProvider<UserProvider>(
|
||||||
create: (context) => UserProvider(),
|
create: (context) => UserProvider(),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -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<int> downloadedItemIds = [];
|
||||||
|
final List<String> downloadQueue = [];
|
||||||
|
|
||||||
|
Future<void> 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<void> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import 'package:didvan/models/requests/studio.dart';
|
import 'package:didvan/models/requests/studio.dart';
|
||||||
import 'package:didvan/models/studio_details_data.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.dart';
|
||||||
import 'package:didvan/services/network/request_helper.dart';
|
import 'package:didvan/services/network/request_helper.dart';
|
||||||
|
import 'package:didvan/services/storage/storage.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
import 'package:just_audio/just_audio.dart';
|
import 'package:just_audio/just_audio.dart';
|
||||||
|
|
@ -30,6 +32,10 @@ class MediaService {
|
||||||
String tag;
|
String tag;
|
||||||
tag = '${isVoiceMessage ? 'message' : 'podcast'}-$id';
|
tag = '${isVoiceMessage ? 'message' : 'podcast'}-$id';
|
||||||
isNetworkAudio ??= audioSource.runtimeType == String;
|
isNetworkAudio ??= audioSource.runtimeType == String;
|
||||||
|
if (!isVoiceMessage && MediaProvider.downloadedItemIds.contains(id)) {
|
||||||
|
audioSource = StorageService.appDocsDir + '/podcasts/podcast-$id.mp3';
|
||||||
|
isNetworkAudio = false;
|
||||||
|
}
|
||||||
if (audioPlayerTag == tag) {
|
if (audioPlayerTag == tag) {
|
||||||
if (audioPlayer.playing) {
|
if (audioPlayer.playing) {
|
||||||
await audioPlayer.pause();
|
await audioPlayer.pause();
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@ class RequestService {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> download(String fileName, String subDirectory) async {
|
Future<void> download(String fileName, String subDirectory) async {
|
||||||
Permission.storage.request();
|
await Permission.storage.request();
|
||||||
final response = await http.get(Uri.parse(url));
|
final response = await http.get(Uri.parse(url));
|
||||||
StorageService.createFile(
|
StorageService.createFile(
|
||||||
bytes: response.bodyBytes,
|
bytes: response.bodyBytes,
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ class StorageService {
|
||||||
await dir.create(recursive: true);
|
await dir.create(recursive: true);
|
||||||
}
|
}
|
||||||
final file = await io.File(
|
final file = await io.File(
|
||||||
appDocsDir + '/$subDirectory/podcast-$name.mp3',
|
appDocsDir + '/$subDirectory/$name',
|
||||||
).create(recursive: true);
|
).create(recursive: true);
|
||||||
await file.writeAsBytes(bytes);
|
await file.writeAsBytes(bytes);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import 'dart:io';
|
||||||
import 'package:didvan/config/design_config.dart';
|
import 'package:didvan/config/design_config.dart';
|
||||||
import 'package:didvan/config/theme_data.dart';
|
import 'package:didvan/config/theme_data.dart';
|
||||||
import 'package:didvan/models/view/app_bar_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/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/details_tab_bar.dart';
|
||||||
import 'package:didvan/views/home/studio/studio_details/widgets/studio_details_widget.dart';
|
import 'package:didvan/views/home/studio/studio_details/widgets/studio_details_widget.dart';
|
||||||
|
|
@ -93,6 +94,9 @@ class _StudioDetailsState extends State<StudioDetails> {
|
||||||
await _changeFullSceen(false);
|
await _changeFullSceen(false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (MediaService.currentPodcast != null) {
|
||||||
|
state.studio = MediaService.currentPodcast!;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
child: DidvanScaffold(
|
child: DidvanScaffold(
|
||||||
|
|
@ -205,7 +209,6 @@ class _StudioDetailsState extends State<StudioDetails> {
|
||||||
children: [
|
children: [
|
||||||
StudioDetailsWidget(
|
StudioDetailsWidget(
|
||||||
scrollController: _scrollController,
|
scrollController: _scrollController,
|
||||||
studio: state.studio,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import 'dart:ui' as ui;
|
||||||
|
|
||||||
import 'package:didvan/config/design_config.dart';
|
import 'package:didvan/config/design_config.dart';
|
||||||
import 'package:didvan/models/view/app_bar_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/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/details_tab_bar.dart';
|
||||||
import 'package:didvan/views/home/studio/studio_details/widgets/studio_details_widget.dart';
|
import 'package:didvan/views/home/studio/studio_details/widgets/studio_details_widget.dart';
|
||||||
|
|
@ -93,6 +94,9 @@ class _StudioDetailsState extends State<StudioDetails> {
|
||||||
await _changeFullSceen(false);
|
await _changeFullSceen(false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (MediaService.currentPodcast != null) {
|
||||||
|
state.studio = MediaService.currentPodcast!;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
child: DidvanScaffold(
|
child: DidvanScaffold(
|
||||||
|
|
@ -144,7 +148,6 @@ class _StudioDetailsState extends State<StudioDetails> {
|
||||||
children: [
|
children: [
|
||||||
StudioDetailsWidget(
|
StudioDetailsWidget(
|
||||||
scrollController: _scrollController,
|
scrollController: _scrollController,
|
||||||
studio: state.studio,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import 'package:didvan/models/overview_data.dart';
|
||||||
import 'package:didvan/models/requests/studio.dart';
|
import 'package:didvan/models/requests/studio.dart';
|
||||||
import 'package:didvan/models/studio_details_data.dart';
|
import 'package:didvan/models/studio_details_data.dart';
|
||||||
import 'package:didvan/providers/core_provider.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/media/media.dart';
|
||||||
import 'package:didvan/services/network/request.dart';
|
import 'package:didvan/services/network/request.dart';
|
||||||
import 'package:didvan/services/network/request_helper.dart';
|
import 'package:didvan/services/network/request_helper.dart';
|
||||||
|
|
@ -17,9 +18,9 @@ class StudioDetailsState extends CoreProvier {
|
||||||
StudioDetailsData? prevStudio;
|
StudioDetailsData? prevStudio;
|
||||||
late int initialIndex;
|
late int initialIndex;
|
||||||
late StudioRequestArgs args;
|
late StudioRequestArgs args;
|
||||||
|
StudioRequestArgs? podcastArgs;
|
||||||
final List<int> relatedQueue = [];
|
final List<int> relatedQueue = [];
|
||||||
bool _positionListenerActivated = false;
|
bool _positionListenerActivated = false;
|
||||||
final List<int> downloadedFileIds = [];
|
|
||||||
|
|
||||||
int _selectedDetailsIndex = 0;
|
int _selectedDetailsIndex = 0;
|
||||||
Timer? timer;
|
Timer? timer;
|
||||||
|
|
@ -39,15 +40,19 @@ class StudioDetailsState extends CoreProvier {
|
||||||
int id, {
|
int id, {
|
||||||
StudioRequestArgs? args,
|
StudioRequestArgs? args,
|
||||||
bool? isForward,
|
bool? isForward,
|
||||||
|
bool forceFetch = false,
|
||||||
}) async {
|
}) async {
|
||||||
if (args != null) {
|
if (args != null) {
|
||||||
this.args = args;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getDownloadsList();
|
|
||||||
_selectedDetailsIndex = 0;
|
_selectedDetailsIndex = 0;
|
||||||
if (isForward != null) {
|
if (isForward != null) {
|
||||||
if (isForward) {
|
if (isForward) {
|
||||||
|
|
@ -85,7 +90,7 @@ class StudioDetailsState extends CoreProvier {
|
||||||
if (result['prevStudio'].isNotEmpty && this.args.page != 0) {
|
if (result['prevStudio'].isNotEmpty && this.args.page != 0) {
|
||||||
prevStudio = StudioDetailsData.fromJson(result['prevStudio']);
|
prevStudio = StudioDetailsData.fromJson(result['prevStudio']);
|
||||||
}
|
}
|
||||||
if (isForward == null) {
|
if (isForward == null && !forceFetch) {
|
||||||
await _handlePodcastPlayback(studio);
|
await _handlePodcastPlayback(studio);
|
||||||
}
|
}
|
||||||
appState = AppState.idle;
|
appState = AppState.idle;
|
||||||
|
|
@ -100,14 +105,10 @@ class StudioDetailsState extends CoreProvier {
|
||||||
if (args.type == 'podcast') {
|
if (args.type == 'podcast') {
|
||||||
MediaService.currentPodcast = studio;
|
MediaService.currentPodcast = studio;
|
||||||
MediaService.podcastPlaylistArgs = args;
|
MediaService.podcastPlaylistArgs = args;
|
||||||
final downloaded = downloadedFileIds.contains(studio.id);
|
|
||||||
await MediaService.handleAudioPlayback(
|
await MediaService.handleAudioPlayback(
|
||||||
audioSource: downloaded
|
audioSource: studio.media,
|
||||||
? StorageService.appDocsDir + '/podcasts/podcast-${studio.id}.mp3'
|
|
||||||
: studio.media,
|
|
||||||
id: studio.id,
|
id: studio.id,
|
||||||
isVoiceMessage: false,
|
isVoiceMessage: false,
|
||||||
isNetworkAudio: !downloaded,
|
|
||||||
);
|
);
|
||||||
if (nextStudio != null && !_positionListenerActivated) {
|
if (nextStudio != null && !_positionListenerActivated) {
|
||||||
_positionListenerActivated = true;
|
_positionListenerActivated = true;
|
||||||
|
|
@ -129,25 +130,6 @@ class StudioDetailsState extends CoreProvier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _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<void> getRelatedContents() async {
|
Future<void> getRelatedContents() async {
|
||||||
if (studio.relatedContents.isNotEmpty) return;
|
if (studio.relatedContents.isNotEmpty) return;
|
||||||
relatedQueue.add(studio.id);
|
relatedQueue.add(studio.id);
|
||||||
|
|
|
||||||
|
|
@ -15,129 +15,131 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class StudioDetailsWidget extends StatelessWidget {
|
class StudioDetailsWidget extends StatelessWidget {
|
||||||
final StudioDetailsData studio;
|
|
||||||
final ScrollController? scrollController;
|
final ScrollController? scrollController;
|
||||||
final VoidCallback? onCommentsTabSelected;
|
final VoidCallback? onCommentsTabSelected;
|
||||||
const StudioDetailsWidget({
|
const StudioDetailsWidget({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.studio,
|
|
||||||
this.onCommentsTabSelected,
|
this.onCommentsTabSelected,
|
||||||
this.scrollController,
|
this.scrollController,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
bool get _isVideo => studio.media.contains('iframe');
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ds = MediaQuery.of(context).size;
|
final ds = MediaQuery.of(context).size;
|
||||||
return Consumer<StudioDetailsState>(
|
return Consumer<StudioDetailsState>(
|
||||||
builder: (context, state, child) => Container(
|
builder: (context, state, child) {
|
||||||
color: Theme.of(context).colorScheme.surface,
|
bool isVideo = state.studio.media.contains('iframe');
|
||||||
child: Column(
|
return Container(
|
||||||
mainAxisSize: MainAxisSize.min,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
children: [
|
child: Column(
|
||||||
if (!_isVideo)
|
mainAxisSize: MainAxisSize.min,
|
||||||
DetailsTabBar(
|
children: [
|
||||||
isVideo: _isVideo,
|
if (!isVideo)
|
||||||
onCommentsTabSelected: onCommentsTabSelected ?? () {},
|
DetailsTabBar(
|
||||||
),
|
isVideo: isVideo,
|
||||||
const SizedBox(height: 16),
|
onCommentsTabSelected: onCommentsTabSelected ?? () {},
|
||||||
StateHandler<StudioDetailsState>(
|
),
|
||||||
onRetry: () {},
|
const SizedBox(height: 16),
|
||||||
state: state,
|
StateHandler<StudioDetailsState>(
|
||||||
builder: (context, state) {
|
onRetry: () {},
|
||||||
if (state.selectedDetailsIndex == 0) {
|
state: state,
|
||||||
return SingleChildScrollView(
|
builder: (context, state) {
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
if (state.selectedDetailsIndex == 0) {
|
||||||
child: Column(
|
return SingleChildScrollView(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Column(
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
DidvanText(state.studio.description),
|
mainAxisSize: MainAxisSize.min,
|
||||||
if (studio.tags.isNotEmpty) const SizedBox(height: 20),
|
children: [
|
||||||
Wrap(
|
DidvanText(state.studio.description),
|
||||||
spacing: 8,
|
if (state.studio.tags.isNotEmpty)
|
||||||
runSpacing: 8,
|
const SizedBox(height: 20),
|
||||||
children: [
|
Wrap(
|
||||||
for (var i = 0; i < studio.tags.length; i++)
|
spacing: 8,
|
||||||
TagItem(tag: studio.tags[i]),
|
runSpacing: 8,
|
||||||
],
|
children: [
|
||||||
),
|
for (var i = 0; i < state.studio.tags.length; i++)
|
||||||
const SizedBox(height: 20),
|
TagItem(tag: state.studio.tags[i]),
|
||||||
Row(
|
],
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
),
|
||||||
children: [
|
const SizedBox(height: 20),
|
||||||
const SizedBox(),
|
Row(
|
||||||
if (state.nextStudio != null)
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
_StudioPreview(
|
children: [
|
||||||
isNext: true,
|
const SizedBox(),
|
||||||
studio: state.nextStudio!,
|
if (state.nextStudio != null)
|
||||||
scrollController: scrollController,
|
_StudioPreview(
|
||||||
),
|
isNext: true,
|
||||||
if (state.prevStudio != null)
|
studio: state.nextStudio!,
|
||||||
_StudioPreview(
|
scrollController: scrollController,
|
||||||
isNext: false,
|
),
|
||||||
studio: state.prevStudio!,
|
if (state.prevStudio != null)
|
||||||
scrollController: scrollController,
|
_StudioPreview(
|
||||||
),
|
isNext: false,
|
||||||
const SizedBox(),
|
studio: state.prevStudio!,
|
||||||
],
|
scrollController: scrollController,
|
||||||
)
|
),
|
||||||
],
|
const SizedBox(),
|
||||||
),
|
],
|
||||||
);
|
)
|
||||||
}
|
],
|
||||||
if (state.selectedDetailsIndex == 1) {
|
|
||||||
return ChangeNotifierProvider<CommentsState>(
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
}
|
||||||
}
|
if (state.selectedDetailsIndex == 1) {
|
||||||
return Column(
|
return ChangeNotifierProvider<CommentsState>(
|
||||||
children: [
|
create: (context) => CommentsState(),
|
||||||
if (studio.relatedContents.isEmpty)
|
child: SizedBox(
|
||||||
for (var i = 0; i < 3; i++)
|
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(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
bottom: 8,
|
bottom: 8,
|
||||||
left: 16,
|
left: 16,
|
||||||
right: 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) {},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:didvan/models/enums.dart';
|
import 'package:didvan/models/enums.dart';
|
||||||
import 'package:didvan/models/overview_data.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/providers/user_provider.dart';
|
||||||
import 'package:didvan/services/network/request.dart';
|
import 'package:didvan/services/network/request.dart';
|
||||||
import 'package:didvan/services/network/request_helper.dart';
|
import 'package:didvan/services/network/request_helper.dart';
|
||||||
import 'package:didvan/services/storage/storage.dart';
|
|
||||||
|
|
||||||
class StudioState extends CoreProvier {
|
class StudioState extends CoreProvier {
|
||||||
final List<OverviewData> studios = [];
|
final List<OverviewData> studios = [];
|
||||||
final List<SliderData> sliders = [];
|
final List<SliderData> sliders = [];
|
||||||
final List<int> downloadedFileIds = [];
|
|
||||||
final List<String> downloadQueue = [];
|
|
||||||
|
|
||||||
AppState downloadState = AppState.idle;
|
|
||||||
|
|
||||||
String search = '';
|
String search = '';
|
||||||
String lastSearch = '';
|
String lastSearch = '';
|
||||||
|
|
@ -37,29 +31,9 @@ class StudioState extends CoreProvier {
|
||||||
_videosSelected = value;
|
_videosSelected = value;
|
||||||
selectedSortTypeIndex = 0;
|
selectedSortTypeIndex = 0;
|
||||||
_getSliders();
|
_getSliders();
|
||||||
getDownloadsList();
|
|
||||||
getStudios(page: page);
|
getStudios(page: page);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> 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 {
|
String get order {
|
||||||
if (selectedSortTypeIndex == 0 || selectedSortTypeIndex == 1) return 'date';
|
if (selectedSortTypeIndex == 0 || selectedSortTypeIndex == 1) return 'date';
|
||||||
if (selectedSortTypeIndex == 2) return 'view';
|
if (selectedSortTypeIndex == 2) return 'view';
|
||||||
|
|
@ -83,7 +57,6 @@ class StudioState extends CoreProvier {
|
||||||
lastSearch = '';
|
lastSearch = '';
|
||||||
_videosSelected = true;
|
_videosSelected = true;
|
||||||
selectedSortTypeIndex = 0;
|
selectedSortTypeIndex = 0;
|
||||||
getDownloadsList();
|
|
||||||
Future.delayed(Duration.zero, () {
|
Future.delayed(Duration.zero, () {
|
||||||
_getSliders();
|
_getSliders();
|
||||||
getStudios(page: 1);
|
getStudios(page: 1);
|
||||||
|
|
@ -148,15 +121,4 @@ class StudioState extends CoreProvier {
|
||||||
studios.firstWhere((radar) => radar.id == id).comments = count;
|
studios.firstWhere((radar) => radar.id == id).comments = count;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> 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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,10 @@ class _StudioTypeButton extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: onTap,
|
onTap: () {
|
||||||
|
onTap();
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import 'package:didvan/constants/app_icons.dart';
|
||||||
import 'package:didvan/models/enums.dart';
|
import 'package:didvan/models/enums.dart';
|
||||||
import 'package:didvan/models/studio_details_data.dart';
|
import 'package:didvan/models/studio_details_data.dart';
|
||||||
import 'package:didvan/models/view/action_sheet_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/services/media/media.dart';
|
||||||
import 'package:didvan/utils/action_sheet.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_details/studio_details_state.dart';
|
||||||
|
|
@ -346,10 +347,6 @@ class __PlayPouseAnimatedIconState extends State<_PlayPouseAnimatedIcon>
|
||||||
audioSource: widget.audioSource,
|
audioSource: widget.audioSource,
|
||||||
isVoiceMessage: false,
|
isVoiceMessage: false,
|
||||||
id: widget.id,
|
id: widget.id,
|
||||||
isNetworkAudio: !context
|
|
||||||
.read<StudioDetailsState>()
|
|
||||||
.downloadedFileIds
|
|
||||||
.contains(widget.id),
|
|
||||||
);
|
);
|
||||||
_handleAnimation();
|
_handleAnimation();
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,10 @@ import 'package:didvan/constants/app_icons.dart';
|
||||||
import 'package:didvan/models/enums.dart';
|
import 'package:didvan/models/enums.dart';
|
||||||
import 'package:didvan/models/overview_data.dart';
|
import 'package:didvan/models/overview_data.dart';
|
||||||
import 'package:didvan/models/requests/studio.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/utils/date_time.dart';
|
||||||
import 'package:didvan/views/home/studio/studio_details/studio_details_state.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/bookmark_button.dart';
|
||||||
import 'package:didvan/views/home/widgets/duration_widget.dart';
|
import 'package:didvan/views/home/widgets/duration_widget.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/card.dart';
|
import 'package:didvan/views/widgets/didvan/card.dart';
|
||||||
|
|
@ -21,19 +22,18 @@ import 'package:provider/provider.dart';
|
||||||
class PodcastOverview extends StatelessWidget {
|
class PodcastOverview extends StatelessWidget {
|
||||||
final OverviewData podcast;
|
final OverviewData podcast;
|
||||||
final void Function(int id, bool value) onMarkChanged;
|
final void Function(int id, bool value) onMarkChanged;
|
||||||
final StudioRequestArgs? studioRequestArgs;
|
final StudioRequestArgs studioRequestArgs;
|
||||||
final bool hasUnmarkConfirmation;
|
final bool hasUnmarkConfirmation;
|
||||||
const PodcastOverview({
|
const PodcastOverview({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.podcast,
|
required this.podcast,
|
||||||
required this.onMarkChanged,
|
required this.onMarkChanged,
|
||||||
this.studioRequestArgs,
|
required this.studioRequestArgs,
|
||||||
this.hasUnmarkConfirmation = false,
|
this.hasUnmarkConfirmation = false,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final state = context.read<StudioState>();
|
|
||||||
return DidvanCard(
|
return DidvanCard(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context
|
context
|
||||||
|
|
@ -77,52 +77,57 @@ class PodcastOverview extends StatelessWidget {
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
const DidvanDivider(verticalPadding: 8),
|
const DidvanDivider(verticalPadding: 8),
|
||||||
Row(
|
Consumer<MediaProvider>(
|
||||||
children: [
|
builder: (context, state, child) => Row(
|
||||||
DurationWidget(duration: podcast.duration!),
|
children: [
|
||||||
const Spacer(),
|
DurationWidget(duration: podcast.duration!),
|
||||||
if (!kIsWeb) ...[
|
const Spacer(),
|
||||||
if (state.downloadState == AppState.idle ||
|
if (!kIsWeb) ...[
|
||||||
!state.downloadQueue.contains(podcast.media))
|
if (state.appState == AppState.idle ||
|
||||||
DidvanIconButton(
|
!state.downloadQueue.contains(podcast.media))
|
||||||
gestureSize: 28,
|
DidvanIconButton(
|
||||||
color: _isDownloaded(state)
|
gestureSize: 28,
|
||||||
? Theme.of(context).colorScheme.primary
|
color: _isDownloaded
|
||||||
: null,
|
? Theme.of(context).colorScheme.primary
|
||||||
icon: _isDownloaded(state)
|
: null,
|
||||||
? DidvanIcons.download_solid
|
icon: _isDownloaded
|
||||||
: DidvanIcons.download_regular,
|
? DidvanIcons.download_solid
|
||||||
onPressed: _isDownloaded(state)
|
: DidvanIcons.download_regular,
|
||||||
? () {}
|
onPressed: _isDownloaded
|
||||||
: () => state.download(
|
? () {}
|
||||||
podcast.media!, podcast.id.toString()),
|
: () => state.download(
|
||||||
),
|
fileName: 'podcast-${podcast.id}.mp3',
|
||||||
if (state.downloadState == AppState.busy &&
|
isVideo: false,
|
||||||
state.downloadQueue.contains(podcast.media))
|
url: podcast.media!,
|
||||||
const SizedBox(
|
),
|
||||||
width: 18,
|
|
||||||
height: 18,
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
strokeWidth: 2,
|
|
||||||
),
|
),
|
||||||
),
|
if (state.appState == AppState.busy &&
|
||||||
const SizedBox(width: 16),
|
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) {
|
bool get _isDownloaded {
|
||||||
return state.downloadedFileIds.contains(podcast.id);
|
return MediaProvider.downloadedItemIds.contains(podcast.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Widget get placeholder => DidvanCard(
|
static Widget get placeholder => DidvanCard(
|
||||||
|
|
|
||||||
|
|
@ -102,13 +102,6 @@ class RadarOverview extends StatelessWidget {
|
||||||
const DidvanDivider(),
|
const DidvanDivider(),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
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()),
|
if (radar.comments != 0) DidvanText(radar.comments.toString()),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
DidvanIconButton(
|
DidvanIconButton(
|
||||||
|
|
@ -125,10 +118,17 @@ class RadarOverview extends StatelessWidget {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
// const SizedBox(width: 16),
|
||||||
// const DidvanText('10'),
|
// const DidvanText('10'),
|
||||||
// const SizedBox(width: 4),
|
// const SizedBox(width: 4),
|
||||||
// const Icon(DidvanIcons.evaluation_regular),
|
// const Icon(DidvanIcons.evaluation_regular),
|
||||||
|
const Spacer(),
|
||||||
|
BookmarkButton(
|
||||||
|
gestureSize: 32,
|
||||||
|
value: radar.marked,
|
||||||
|
onMarkChanged: (value) => onMarkChanged(radar.id, value),
|
||||||
|
askForConfirmation: hasUnmarkConfirmation,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import 'dart:developer';
|
||||||
|
|
||||||
import 'package:didvan/config/design_config.dart';
|
import 'package:didvan/config/design_config.dart';
|
||||||
import 'package:didvan/main.dart';
|
import 'package:didvan/main.dart';
|
||||||
|
import 'package:didvan/providers/media.dart';
|
||||||
import 'package:didvan/providers/server_data_provider.dart';
|
import 'package:didvan/providers/server_data_provider.dart';
|
||||||
import 'package:didvan/providers/theme_provider.dart';
|
import 'package:didvan/providers/theme_provider.dart';
|
||||||
import 'package:didvan/providers/user_provider.dart';
|
import 'package:didvan/providers/user_provider.dart';
|
||||||
|
|
@ -109,6 +110,7 @@ class _SplashState extends State<Splash> {
|
||||||
final String? token = await userProvider.setAndGetToken();
|
final String? token = await userProvider.setAndGetToken();
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
log(token);
|
log(token);
|
||||||
|
context.read<MediaProvider>().getDownloadsList();
|
||||||
RequestService.token = token;
|
RequestService.token = token;
|
||||||
final result = await userProvider.getUserInfo();
|
final result = await userProvider.getUserInfo();
|
||||||
if (!result) {
|
if (!result) {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import 'package:didvan/config/design_config.dart';
|
||||||
import 'package:didvan/config/theme_data.dart';
|
import 'package:didvan/config/theme_data.dart';
|
||||||
import 'package:didvan/constants/app_icons.dart';
|
import 'package:didvan/constants/app_icons.dart';
|
||||||
import 'package:didvan/models/enums.dart';
|
import 'package:didvan/models/enums.dart';
|
||||||
|
import 'package:didvan/providers/media.dart';
|
||||||
import 'package:didvan/services/media/media.dart';
|
import 'package:didvan/services/media/media.dart';
|
||||||
import 'package:didvan/services/storage/storage.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/studio_details_state.dart';
|
||||||
|
|
@ -26,6 +27,73 @@ class DidvanBNB extends StatelessWidget {
|
||||||
{Key? key, required this.currentTabIndex, required this.onTabChanged})
|
{Key? key, required this.currentTabIndex, required this.onTabChanged})
|
||||||
: super(key: key);
|
: 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) =>
|
bool _enablePlayerController(StudioDetailsState state) =>
|
||||||
MediaService.currentPodcast != null ||
|
MediaService.currentPodcast != null ||
|
||||||
(MediaService.audioPlayerTag?.contains('podcast') ?? false);
|
(MediaService.audioPlayerTag?.contains('podcast') ?? false);
|
||||||
|
|
@ -33,227 +101,170 @@ class DidvanBNB extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return StreamBuilder<bool>(
|
return StreamBuilder<bool>(
|
||||||
stream: MediaService.audioPlayer.playingStream,
|
stream: MediaService.audioPlayer.playingStream,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) => GestureDetector(
|
||||||
return Stack(
|
onTap: () => MediaService.currentPodcast == null
|
||||||
children: [
|
? null
|
||||||
GestureDetector(
|
: _showPlayerBottomSheet(context),
|
||||||
onTap: () => MediaService.currentPodcast == null
|
child: Consumer<StudioDetailsState>(
|
||||||
? null
|
builder: (context, state, child) => AnimatedContainer(
|
||||||
: _showPlayerBottomSheet(context),
|
padding: const EdgeInsets.only(top: 12),
|
||||||
child: Consumer<StudioDetailsState>(
|
duration: DesignConfig.lowAnimationDuration,
|
||||||
builder: (context, state, child) => AnimatedContainer(
|
height: _enablePlayerController(state) ? 128 : 72,
|
||||||
padding: const EdgeInsets.only(top: 12),
|
decoration: BoxDecoration(
|
||||||
duration: DesignConfig.lowAnimationDuration,
|
color: DesignConfig.isDark
|
||||||
height: _enablePlayerController(state) ? 128 : 72,
|
? Theme.of(context).colorScheme.focused
|
||||||
decoration: BoxDecoration(
|
: Theme.of(context).colorScheme.navigation,
|
||||||
color: DesignConfig.isDark
|
borderRadius: const BorderRadius.vertical(
|
||||||
? Theme.of(context).colorScheme.focused
|
top: Radius.circular(16),
|
||||||
: Theme.of(context).colorScheme.navigation,
|
),
|
||||||
borderRadius: const BorderRadius.vertical(
|
),
|
||||||
top: Radius.circular(16),
|
alignment: Alignment.topCenter,
|
||||||
),
|
child: !_enablePlayerController(state)
|
||||||
),
|
? const SizedBox()
|
||||||
alignment: Alignment.topCenter,
|
: MediaService.currentPodcast == null
|
||||||
child: !_enablePlayerController(state)
|
? SizedBox(
|
||||||
? const SizedBox()
|
height: 32,
|
||||||
: MediaService.currentPodcast == null
|
child: Center(
|
||||||
? SizedBox(
|
child: SpinKitThreeBounce(
|
||||||
height: 32,
|
size: 18,
|
||||||
child: Center(
|
color: DesignConfig.isDark
|
||||||
child: SpinKitThreeBounce(
|
? Theme.of(context).colorScheme.title
|
||||||
size: 18,
|
: 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
|
color: DesignConfig.isDark
|
||||||
? null
|
? null
|
||||||
: Theme.of(context)
|
: Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.secondCTA,
|
.secondCTA,
|
||||||
),
|
),
|
||||||
),
|
AudioSlider(
|
||||||
)
|
disableThumb: true,
|
||||||
: SizedBox(
|
tag: MediaService.audioPlayerTag!,
|
||||||
height: 56,
|
),
|
||||||
child: Row(
|
],
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
),
|
||||||
children: [
|
),
|
||||||
Padding(
|
StreamBuilder<PlayerState>(
|
||||||
padding: const EdgeInsets.only(
|
stream:
|
||||||
right: 12,
|
MediaService.audioPlayer.playerStateStream,
|
||||||
left: 16,
|
builder: (context, snapshot) {
|
||||||
),
|
final playerState = MediaService
|
||||||
child: DidvanIconButton(
|
.audioPlayer.playerState.processingState;
|
||||||
icon: DidvanIcons.close_regular,
|
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
|
color: DesignConfig.isDark
|
||||||
? null
|
? Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.title
|
||||||
: Theme.of(context)
|
: Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.secondCTA,
|
.secondCTA,
|
||||||
gestureSize: 28,
|
|
||||||
onPressed:
|
|
||||||
MediaService.resetAudioPlayer,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SkeletonImage(
|
);
|
||||||
imageUrl:
|
}
|
||||||
MediaService.currentPodcast!.image,
|
return const SizedBox();
|
||||||
width: 32,
|
},
|
||||||
height: 32,
|
),
|
||||||
),
|
if (state.appState != AppState.busy &&
|
||||||
const SizedBox(width: 16),
|
MediaService.audioPlayer.playerState
|
||||||
Expanded(
|
.processingState !=
|
||||||
child: Column(
|
ProcessingState.loading)
|
||||||
crossAxisAlignment:
|
Padding(
|
||||||
CrossAxisAlignment.start,
|
padding: const EdgeInsets.only(
|
||||||
children: [
|
left: 12,
|
||||||
DidvanText(
|
right: 16,
|
||||||
MediaService.currentPodcast!.title,
|
),
|
||||||
color: DesignConfig.isDark
|
child: DidvanIconButton(
|
||||||
? null
|
gestureSize: 28,
|
||||||
: Theme.of(context)
|
color: DesignConfig.isDark
|
||||||
.colorScheme
|
? null
|
||||||
.secondCTA,
|
: Theme.of(context).colorScheme.secondCTA,
|
||||||
),
|
icon: snapshot.data!
|
||||||
AudioSlider(
|
? DidvanIcons.pause_solid
|
||||||
disableThumb: true,
|
: DidvanIcons.play_solid,
|
||||||
tag: MediaService.audioPlayerTag!,
|
onPressed: () {
|
||||||
),
|
if (state.args.type == 'video') {
|
||||||
],
|
state.getStudioDetails(
|
||||||
),
|
MediaService.currentPodcast!.id,
|
||||||
),
|
args: state.podcastArgs,
|
||||||
StreamBuilder<PlayerState>(
|
forceFetch: true,
|
||||||
stream: MediaService
|
);
|
||||||
.audioPlayer.playerStateStream,
|
}
|
||||||
builder: (context, snapshot) {
|
MediaService.handleAudioPlayback(
|
||||||
if (state.appState == AppState.busy ||
|
audioSource:
|
||||||
MediaService.audioPlayer.playerState
|
MediaService.currentPodcast!.media,
|
||||||
.processingState ==
|
id: MediaService.currentPodcast!.id,
|
||||||
ProcessingState.loading) {
|
isVoiceMessage: false,
|
||||||
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,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
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) {
|
void _showPlayerBottomSheet(BuildContext context) {
|
||||||
final sheetKey = GlobalKey<ExpandableBottomSheetState>();
|
final sheetKey = GlobalKey<ExpandableBottomSheetState>();
|
||||||
bool isExpanded = false;
|
bool isExpanded = false;
|
||||||
final detailsState = context.read<StudioDetailsState>();
|
final detailsState = context.read<StudioDetailsState>();
|
||||||
|
if (detailsState.args.type == 'video') {
|
||||||
|
detailsState.getStudioDetails(
|
||||||
|
MediaService.currentPodcast!.id,
|
||||||
|
args: detailsState.podcastArgs,
|
||||||
|
forceFetch: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
final state = context.read<StudioState>();
|
final state = context.read<StudioState>();
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
|
|
@ -288,23 +299,18 @@ class DidvanBNB extends StatelessWidget {
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
StatefulBuilder(
|
DidvanIconButton(
|
||||||
builder: (context, setState) => DidvanIconButton(
|
size: 32,
|
||||||
size: 32,
|
icon: DidvanIcons.angle_up_regular,
|
||||||
icon: isExpanded
|
onPressed: () {
|
||||||
? DidvanIcons.angle_down_regular
|
if (!isExpanded) {
|
||||||
: DidvanIcons.angle_up_regular,
|
sheetKey.currentState?.expand();
|
||||||
onPressed: () {
|
isExpanded = true;
|
||||||
if (!isExpanded) {
|
} else {
|
||||||
sheetKey.currentState?.expand();
|
isExpanded = false;
|
||||||
isExpanded = true;
|
sheetKey.currentState?.contract();
|
||||||
} else {
|
}
|
||||||
isExpanded = false;
|
},
|
||||||
sheetKey.currentState?.contract();
|
|
||||||
}
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
],
|
],
|
||||||
|
|
@ -322,7 +328,6 @@ class DidvanBNB extends StatelessWidget {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: StudioDetailsWidget(
|
: StudioDetailsWidget(
|
||||||
studio: detailsState.studio,
|
|
||||||
onCommentsTabSelected: () {
|
onCommentsTabSelected: () {
|
||||||
Future.delayed(
|
Future.delayed(
|
||||||
const Duration(milliseconds: 100),
|
const Duration(milliseconds: 100),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue