import 'dart:async'; 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.dart'; import 'package:didvan/services/media/media.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; class StudioDetailsState extends CoreProvier { late StudioDetailsData studio; bool isStudioLoaded = false; StudioDetailsData? nextStudio; StudioDetailsData? prevStudio; late int initialIndex; StudioRequestArgs? args; StudioRequestArgs? podcastArgs; final List relatedQueue = []; AppState alongSideState = AppState.idle; int _trackingTimerCounter = 0; Timer? _trackingTimer; // BEGIN: ADD THIS LINE StreamSubscription? _positionSubscription; // END: ADD THIS LINE int _selectedDetailsIndex = 0; Timer? timer; int timerValue = 10; bool stopOnPodcastEnds = false; int get selectedDetailsIndex => _selectedDetailsIndex; set selectedDetailsIndex(int value) { _selectedDetailsIndex = value; if (value == 2) { getRelatedContents(); } notifyListeners(); } Future getStudioDetails( int id, { StudioRequestArgs? args, bool? isForward, bool fetchOnly = false, }) async { try { if (args != null) { this.args = args; } if (this.args?.type == 'podcast') { podcastArgs = this.args; } if (MediaService.currentPodcast?.id == id && this.args?.type == 'podcast' && !fetchOnly) { return; } _selectedDetailsIndex = 0; if (isForward != null) { if (isForward) { prevStudio = studio; studio = nextStudio!; nextStudio = null; } else { nextStudio = studio; studio = prevStudio!; prevStudio = null; } isStudioLoaded = true; notifyListeners(); _handlePodcastPlayback(studio); } if (isForward == null) { if (this.args?.type == 'podcast') { MediaService.audioPlayerTag = 'podcast-${MediaService.currentPodcast?.id ?? ''}'; } appState = AppState.busy; } else { alongSideState = AppState.busy; notifyListeners(); } final service = RequestService( RequestHelper.studioDetails( id, this.args ?? const StudioRequestArgs(page: 0), ), ); await service.httpGet(); nextStudio = null; prevStudio = null; if (stopOnPodcastEnds) { timerValue = 10; } stopOnPodcastEnds = false; if (service.isSuccess) { if (args?.type == 'video') { handleTracking(id: id, sendRequest: false); } final result = service.result; studio = StudioDetailsData.fromJson(result['studio']); isStudioLoaded = true; if (args?.page == 0) { initialIndex = 0; appState = AppState.idle; notifyListeners(); return; } if (result['nextStudio'].isNotEmpty && this.args?.page != 0) { nextStudio = StudioDetailsData.fromJson(result['nextStudio']); } if (result['prevStudio'].isNotEmpty && this.args?.page != 0) { prevStudio = StudioDetailsData.fromJson(result['prevStudio']); } if (isForward == null && !fetchOnly) { await _handlePodcastPlayback(studio); } alongSideState = AppState.idle; appState = AppState.idle; notifyListeners(); return; } if (isForward == null) { appState = AppState.failed; } else { alongSideState = AppState.failed; notifyListeners(); } } catch (e) { update(); appState = AppState.idle; } } Future _handlePodcastPlayback(StudioDetailsData studio) async { _positionSubscription?.cancel(); if (args?.type == 'podcast') { MediaService.currentPodcast = studio; MediaService.podcastPlaylistArgs = args; await MediaService.handleAudioPlayback( 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) { _positionSubscription = MediaService.audioPlayer.positionStream.listen((event) { if (this.args?.type != 'podcast' || MediaService.audioPlayerTag?.contains('message') == true) { return; } final duration = MediaService.duration ?? Duration(seconds: this.studio.duration); if (event.compareTo(duration) > 0 && this.nextStudio != null) { if (this.stopOnPodcastEnds) { MediaService.resetAudioPlayer(); return; } this.getStudioDetails(this.nextStudio!.id, isForward: true); } }); } } else { MediaService.audioPlayer.pause(); } } Future getRelatedContents() async { if (studio.relatedContents.isNotEmpty) return; relatedQueue.add(studio.id); final service = RequestService(RequestHelper.tag( ids: studio.tags.map((tag) => tag.id).toList(), itemId: studio.id, type: args?.type, )); await service.httpGet(); if (service.isSuccess) { final relateds = service.result['contents']; for (var i = 0; i < relateds.length; i++) { studio.relatedContents.add(OverviewData.fromJson(relateds[i])); } if (relateds.isEmpty) { studio.relatedContentsIsEmpty = true; } } else { studio.relatedContentsIsEmpty = true; } notifyListeners(); } void onCommentsChanged(int count) { studio.comments = count; notifyListeners(); } Future handleTracking({ required int id, bool sendRequest = true, }) async { if (!sendRequest) { _trackingTimerCounter = 0; _trackingTimer = Timer.periodic(const Duration(seconds: 1), (timer) { _trackingTimerCounter++; notifyListeners(); }); return; } final service = RequestService( RequestHelper.tracking(id, 'studio'), body: { 'sec': _trackingTimerCounter, }, ); service.put(); _trackingTimerCounter = 0; _trackingTimer?.cancel(); } @override void dispose() { _trackingTimer?.cancel(); _positionSubscription?.cancel(); super.dispose(); } }