From 57d437c1a2a3a95ffe2f97cd4ae07f24315e2ae2 Mon Sep 17 00:00:00 2001 From: MSOB7YY Date: Wed, 17 Jul 2024 02:01:06 +0300 Subject: [PATCH] =?UTF-8?q?chore:=20=F0=9D=93=BC=F0=9D=93=B8=F0=9D=93=B6?= =?UTF-8?q?=F0=9D=93=AE=20=F0=9D=93=BB=F0=9D=93=AE=F0=9D=93=AF=F0=9D=93=B2?= =?UTF-8?q?=F0=9D=93=B7=F0=9D=93=AE=F0=9D=93=B6=F0=9D=93=AE=F0=9D=93=B7?= =?UTF-8?q?=F0=9D=93=BD=F0=9D=93=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/base/audio_handler.dart | 8 ++-- lib/base/ports_provider.dart | 2 + lib/controller/thumbnail_manager.dart | 23 ++++++++--- lib/core/functions.dart | 14 ++++--- lib/ui/widgets/custom_widgets.dart | 2 +- .../info_controllers/yt_history_linker.dart | 17 ++++++-- lib/youtube/functions/yt_playlist_utils.dart | 2 +- .../user/youtube_account_manage_page.dart | 2 +- .../youtube_main_page_fetcher_acc_base.dart | 40 ++++++++++++++----- lib/youtube/pages/yt_playlist_subpage.dart | 7 +--- .../widgets/yt_download_task_item_card.dart | 4 +- pubspec.yaml | 2 +- 12 files changed, 83 insertions(+), 40 deletions(-) diff --git a/lib/base/audio_handler.dart b/lib/base/audio_handler.dart index 5329f153..066c79bb 100644 --- a/lib/base/audio_handler.dart +++ b/lib/base/audio_handler.dart @@ -857,8 +857,8 @@ class NamidaAudioVideoHandler extends BasicAudioHandler { File? _ytNotificationVideoThumbnail; /// Shows error if [marked] is not true. - void _onVideoMarkWatchResultError(bool? marked) { - if (marked != true) { + void _onVideoMarkWatchResultError(YTMarkVideoWatchedResult marked) { + if (marked == YTMarkVideoWatchedResult.addedAsPending) { snackyy(message: 'Failed to mark video as watched.', top: false, isError: true); } } @@ -1036,13 +1036,13 @@ class NamidaAudioVideoHandler extends BasicAudioHandler { if (checkInterrupted()) return; - Completer? markedAsWatched; + Completer? markedAsWatched; // only if was playing if (okaySetFromCache() && (streamsResult != null || !ConnectivityController.inst.hasConnection)) { // -- allow when no connection bcz this function won't try again with no connection, // -- so we force call here and let `markVideoWatched` do the job when there is proper connection. - markedAsWatched = Completer(); + markedAsWatched = Completer(); markedAsWatched.complete(YoutubeInfoController.history.markVideoWatched(videoId: item.id, streamResult: streamsResult)); } diff --git a/lib/base/ports_provider.dart b/lib/base/ports_provider.dart index 0de157f1..ea888013 100644 --- a/lib/base/ports_provider.dart +++ b/lib/base/ports_provider.dart @@ -20,6 +20,8 @@ class IsolateFunctionReturnBuild { } mixin PortsProvider { + bool get isInitialized => _isInitialized ?? false; + Completer? _portCompleter; ReceivePort? _recievePort; StreamSubscription? _streamSub; diff --git a/lib/controller/thumbnail_manager.dart b/lib/controller/thumbnail_manager.dart index a353b9f7..a9605c09 100644 --- a/lib/controller/thumbnail_manager.dart +++ b/lib/controller/thumbnail_manager.dart @@ -194,6 +194,7 @@ class ThumbnailManager { class _YTThumbnailDownloadManager with PortsProvider { final _downloadCompleters = ?>{}; // item id + final _requestsCountForId = {}; // item id final _shouldRetry = {}; // item id final _notFoundThumbnails = {}; // item id @@ -213,8 +214,12 @@ class _YTThumbnailDownloadManager with PortsProvider { if (onNotFound != null) onNotFound(); return null; } + + _requestsCountForId.update(id, (value) => value++, ifAbsent: () => 1); + if (forceRequest == false && _downloadCompleters[id] != null) { final res = await _downloadCompleters[id]!.future; + _requestsCountForId.update(id, (value) => value--); if (res != null || _shouldRetry[id] != true) { return res; } @@ -231,11 +236,13 @@ class _YTThumbnailDownloadManager with PortsProvider { 'destinationFile': destinationFile, 'symlinkId': symlinkId, }; - await initialize(); + if (!isInitialized) await initialize(); await sendPort(p); final res = await _downloadCompleters[id]?.future; - if (_notFoundThumbnails[id] == true) { + _requestsCountForId.update(id, (value) => value--); + + if (res == null && _notFoundThumbnails[id] == true) { if (onNotFound != null) onNotFound(); return null; } @@ -244,9 +251,13 @@ class _YTThumbnailDownloadManager with PortsProvider { Future stopDownload({required String? id}) async { if (id == null) return; - _onFileFinish(id, null, null, true); - final p = {'id': id, 'stop': true}; - await sendPort(p); + final otherActiveRequests = _requestsCountForId[id]; + if (otherActiveRequests == null || otherActiveRequests <= 1) { + // -- only close if active requests only 1 + _onFileFinish(id, null, null, true); + final p = {'id': id, 'stop': true}; + await sendPort(p); + } } static Future _prepareDownloadResources(SendPort sendPort) async { @@ -428,8 +439,8 @@ class _YTThumbnailDownloadManager with PortsProvider { void _onFileFinish(String itemId, File? downloadedFile, bool? notfound, bool aborted) { if (notfound != null) _notFoundThumbnails[itemId] = notfound; - _downloadCompleters[itemId]?.completeIfWasnt(downloadedFile); _shouldRetry[itemId] = aborted; + _downloadCompleters[itemId]?.completeIfWasnt(downloadedFile); } } diff --git a/lib/core/functions.dart b/lib/core/functions.dart index c0f2e29a..4983c541 100644 --- a/lib/core/functions.dart +++ b/lib/core/functions.dart @@ -604,7 +604,7 @@ Future showCalendarDialog({ ); } -Future showNamidaBottomSheetWithTextField({ +Future showNamidaBottomSheetWithTextField({ required BuildContext context, bool isScrollControlled = true, bool useRootNavigator = true, @@ -625,6 +625,8 @@ Future showNamidaBottomSheetWithTextField({ final focusNode = FocusNode(); focusNode.requestFocus(); + String? finalText; + await Future.delayed(Duration.zero); // delay bcz sometimes doesnt show await showModalBottomSheet( // ignore: use_build_context_synchronously @@ -674,8 +676,11 @@ Future showNamidaBottomSheetWithTextField({ ), onTap: () async { if (formKey.currentState!.validate()) { - final canPop = await onButtonTap(controller.text); - if (canPop && context.mounted) context.safePop(); + final didAgree = await onButtonTap(controller.text); + if (didAgree) { + finalText = controller.text; + if (context.mounted) context.safePop(); + } } }, ), @@ -688,9 +693,8 @@ Future showNamidaBottomSheetWithTextField({ ); }, ); - final t = controller.text; controller.disposeAfterAnimation(also: focusNode.dispose); - return t; + return finalText; } // Returns a [0-1] scale representing how much similar both are. diff --git a/lib/ui/widgets/custom_widgets.dart b/lib/ui/widgets/custom_widgets.dart index a2f05b7e..70599489 100644 --- a/lib/ui/widgets/custom_widgets.dart +++ b/lib/ui/widgets/custom_widgets.dart @@ -277,7 +277,7 @@ class CustomListTile extends StatelessWidget { maxLines: subtitle != null ? 4 : 5, overflow: TextOverflow.ellipsis, ), - subtitle: subtitle != null + subtitle: subtitle?.isNotEmpty == true ? Text( subtitle!, style: context.theme.textTheme.displaySmall, diff --git a/lib/youtube/controller/info_controllers/yt_history_linker.dart b/lib/youtube/controller/info_controllers/yt_history_linker.dart index ab873691..44c560ea 100644 --- a/lib/youtube/controller/info_controllers/yt_history_linker.dart +++ b/lib/youtube/controller/info_controllers/yt_history_linker.dart @@ -139,7 +139,9 @@ class _YoutubeHistoryLinker { _pendingRequestsCompleter = null; } - Future markVideoWatched({required String videoId, required VideoStreamsResult? streamResult, bool errorOnMissingParam = true}) async { + Future markVideoWatched({required String videoId, required VideoStreamsResult? streamResult, bool errorOnMissingParam = true}) async { + if (_dbOpenedAccId == null) return YTMarkVideoWatchedResult.noAccount; // no acc signed in + if (_hasPendingRequests) { executePendingRequests(); } @@ -167,10 +169,13 @@ class _YoutubeHistoryLinker { } } if (added) { - return added; + return YTMarkVideoWatchedResult.marked; } else { + // -- the handling of executing pending requests is different. + // -- even if `added` is false due to missing params/etc, it will + // -- properly get removed while exeuting pending. _addPendingRequest(videoId: videoId, streamResult: streamResult); - return null; + return YTMarkVideoWatchedResult.addedAsPending; } } @@ -183,3 +188,9 @@ class _YoutubeHistoryLinker { return cache.read(); } } + +enum YTMarkVideoWatchedResult { + noAccount, + marked, + addedAsPending, +} diff --git a/lib/youtube/functions/yt_playlist_utils.dart b/lib/youtube/functions/yt_playlist_utils.dart index f29f85f3..7de2abd6 100644 --- a/lib/youtube/functions/yt_playlist_utils.dart +++ b/lib/youtube/functions/yt_playlist_utils.dart @@ -53,7 +53,7 @@ extension YoutubePlaylistShare on YoutubePlaylist { return deleted; } - Future showRenamePlaylistSheet({ + Future showRenamePlaylistSheet({ required BuildContext context, required String playlistName, }) async { diff --git a/lib/youtube/pages/user/youtube_account_manage_page.dart b/lib/youtube/pages/user/youtube_account_manage_page.dart index 16b3d4f6..dc2ec4d3 100644 --- a/lib/youtube/pages/user/youtube_account_manage_page.dart +++ b/lib/youtube/pages/user/youtube_account_manage_page.dart @@ -26,7 +26,7 @@ part 'youtube_manage_subscription_page.dart'; class YoutubeAccountManagePage extends StatelessWidget with NamidaRouteWidget { @override - RouteType get route => RouteType.YOUTUBE_USER_MANAGE_SUBSCRIPTION_SUBPAGE; + RouteType get route => RouteType.YOUTUBE_USER_MANAGE_ACCOUNT_SUBPAGE; const YoutubeAccountManagePage({super.key}); diff --git a/lib/youtube/pages/youtube_main_page_fetcher_acc_base.dart b/lib/youtube/pages/youtube_main_page_fetcher_acc_base.dart index 9cf4454e..7066ca32 100644 --- a/lib/youtube/pages/youtube_main_page_fetcher_acc_base.dart +++ b/lib/youtube/pages/youtube_main_page_fetcher_acc_base.dart @@ -39,6 +39,7 @@ class YoutubeMainPageFetcherAccBase, T extends final bool isHorizontal; final double? horizontalHeight; final double topPadding; + final double? bottomPadding; final Future Function()? onPullToRefresh; final bool enablePullToRefresh; final void Function(W? result)? onListUpdated; @@ -58,6 +59,7 @@ class YoutubeMainPageFetcherAccBase, T extends this.isHorizontal = false, this.horizontalHeight, this.topPadding = 24.0, + this.bottomPadding, this.onPullToRefresh, this.enablePullToRefresh = true, this.onListUpdated, @@ -101,17 +103,19 @@ class _YoutubePageState, T extends MapSerializa ); } - @override - void initState() { - super.initState(); - + void _onInit({bool forceRequest = false}) async { bool needNewRequest = false; - final lastFetchedTime = _resultsFetchTime[W]; - if (_hasConnection) { - if (lastFetchedTime == null) { - needNewRequest = true; - } else if (lastFetchedTime.difference(DateTime.now()).abs() > const Duration(seconds: 180)) { - needNewRequest = true; + + if (forceRequest) { + needNewRequest = true; + } else { + final lastFetchedTime = _resultsFetchTime[W]; + if (_hasConnection) { + if (lastFetchedTime == null) { + needNewRequest = true; + } else if (lastFetchedTime.difference(DateTime.now()).abs() > const Duration(seconds: 180)) { + needNewRequest = true; + } } } @@ -129,12 +133,24 @@ class _YoutubePageState, T extends MapSerializa } else { _fetchFeed(); } + } + + void _onAccChanged() { + _onInit(forceRequest: true); + } + + @override + void initState() { + super.initState(); + _onInit(); + YoutubeAccountController.current.addOnAccountChanged(_onAccChanged); if (widget.onListUpdated != null) _currentFeed.addListener(_onListUpdated); } @override void dispose() { if (widget.onListUpdated != null) _currentFeed.removeListener(_onListUpdated); + YoutubeAccountController.current.removeOnAccountChanged(_onAccChanged); _controller.dispose(); _isLoadingCurrentFeed.close(); @@ -165,6 +181,7 @@ class _YoutubePageState, T extends MapSerializa _resultsFetchTime[W] = DateTime.now(); if (val != null) { _currentFeed.value = val; + _lastFetchWasCached.value = false; } else { _lastFetchWasCached.value = true; } @@ -227,7 +244,7 @@ class _YoutubePageState, T extends MapSerializa ); } - final pagePadding = EdgeInsets.only(top: widget.topPadding, bottom: Dimensions.inst.globalBottomPaddingTotalR); + final pagePadding = EdgeInsets.only(top: widget.topPadding, bottom: widget.bottomPadding ?? Dimensions.inst.globalBottomPaddingTotalR); final EdgeInsets firstPadding; final EdgeInsets lastPadding; @@ -256,6 +273,7 @@ class _YoutubePageState, T extends MapSerializa padding: pagePadding, child: Column( children: [ + if (widget.pageHeader != null) widget.pageHeader!, Align( alignment: Alignment.centerLeft, child: header, diff --git a/lib/youtube/pages/yt_playlist_subpage.dart b/lib/youtube/pages/yt_playlist_subpage.dart index 2ba369cf..eb1c8fd4 100644 --- a/lib/youtube/pages/yt_playlist_subpage.dart +++ b/lib/youtube/pages/yt_playlist_subpage.dart @@ -280,11 +280,8 @@ class _YTNormalPlaylistSubpageState extends State { title: lang.RENAME_PLAYLIST, onTap: () async { final newName = await playlist.showRenamePlaylistSheet(context: context, playlistName: playlistCurrentName); - if (context.mounted) { - refreshState(() { - playlistCurrentName = newName; - }); - } + if (newName == null) return; + refreshState(() => playlistCurrentName = newName); }, ), NamidaPopupItem( diff --git a/lib/youtube/widgets/yt_download_task_item_card.dart b/lib/youtube/widgets/yt_download_task_item_card.dart index 7a41b597..9334e14d 100644 --- a/lib/youtube/widgets/yt_download_task_item_card.dart +++ b/lib/youtube/widgets/yt_download_task_item_card.dart @@ -422,12 +422,12 @@ class YTDownloadTaskItemCard extends StatelessWidget { ); } - Future _onRenameIconTap({ + Future _onRenameIconTap({ required BuildContext context, required YoutubeItemDownloadConfig config, required DownloadTaskGroupName groupName, }) async { - return await showNamidaBottomSheetWithTextField( + await showNamidaBottomSheetWithTextField( context: context, initalControllerText: config.filename.filename, title: lang.RENAME, diff --git a/pubspec.yaml b/pubspec.yaml index 94cf916b..51439691 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: namida description: A Beautiful and Feature-rich Music Player, With YouTube & Video Support Built in Flutter publish_to: "none" -version: 3.3.5-beta+240716218 +version: 3.3.7-beta+240716229 environment: sdk: ">=3.4.0 <4.0.0"