From 024599cd6e4a300d7cde685401c482a5ba594717 Mon Sep 17 00:00:00 2001 From: appdevelpo <56633229+appdevelpo@users.noreply.github.com> Date: Thu, 23 Nov 2023 17:28:06 +0800 Subject: [PATCH 01/23] add function for extension --- lib/controllers/watch/comic_controller.dart | 42 +++++++++++++- lib/data/services/extension_service.dart | 61 ++++++++++++++++++++- lib/models/extension.dart | 18 +++++- lib/models/extension.g.dart | 18 ++++++ 4 files changed, 136 insertions(+), 3 deletions(-) diff --git a/lib/controllers/watch/comic_controller.dart b/lib/controllers/watch/comic_controller.dart index dc593195..b7fa8a2d 100644 --- a/lib/controllers/watch/comic_controller.dart +++ b/lib/controllers/watch/comic_controller.dart @@ -4,6 +4,7 @@ import 'package:miru_app/models/index.dart'; import 'package:miru_app/controllers/watch/reader_controller.dart'; import 'package:miru_app/data/services/database_service.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; +import 'dart:async'; class ComicController extends ReaderController { ComicController({ @@ -19,7 +20,7 @@ class ComicController extends ReaderController { final readType = MangaReadMode.standard.obs; // 当前页码 final currentPage = 0.obs; - + bool timerCancel = false; final pageController = PageController().obs; final itemPositionsListener = ItemPositionsListener.create(); final itemScrollController = ItemScrollController(); @@ -38,6 +39,7 @@ class ComicController extends ReaderController { final pos = itemPositionsListener.itemPositions.value.first; currentPage.value = pos.index; }); + _pageUpdate(); ever(readType, (callback) { _jumpPage(currentPage.value); // 保存设置 @@ -70,6 +72,31 @@ class ComicController extends ReaderController { super.onInit(); } + _pageUpdate() async { + int curPage = currentPage.value; + Timer(const Duration(seconds: 2), () { + //set update timer + Timer.periodic(const Duration(milliseconds: 600), (timer) { + debugPrint('$curPage'); + if (curPage >= super.watchData.value!.urls.length - 1 || timerCancel) { + timer.cancel(); + } + curPage++; + + if (super.watchData.value!.urls[curPage] == "") { + _getPage(curPage); + } + }); + }); + } + + _getPage(int page) async { + await Future.delayed(const Duration(seconds: 1)); + + final updatePage = await runtime.updatePages(page) as ExtensionUpdatePages; + super.watchData.value!.urls[page] = updatePage.url; + } + _initSetting() async { readType.value = await DatabaseService.getMnagaReaderType(super.detailUrl); } @@ -81,6 +108,18 @@ class ComicController extends ReaderController { index: page, ); } + int curPage = currentPage.value; + Timer.periodic(const Duration(milliseconds: 600), (timer) { + debugPrint('$curPage'); + if (curPage == -1 || timerCancel) { + timer.cancel(); + } + curPage--; + + if (super.watchData.value!.urls[curPage] == "") { + _getPage(curPage); + } + }); return; } if (pageController.value.hasClients) { @@ -133,6 +172,7 @@ class ComicController extends ReaderController { ); } pageController.value.dispose(); + timerCancel = true; super.onClose(); } } diff --git a/lib/data/services/extension_service.dart b/lib/data/services/extension_service.dart index c1fd87af..d6815b54 100644 --- a/lib/data/services/extension_service.dart +++ b/lib/data/services/extension_service.dart @@ -69,7 +69,26 @@ class ExtensionService { )) .data; }); - + // request with headers + runtime.onMessage('RequestWithHeader', (dynamic args) async { + ExtensionUtils.addLog( + extension, + ExtensionLogLevel.info, + "RequestWithHeader: ${args[0]} , ${args[1]}", + ); + _cuurentRequestUrl = args[0]; + Response response = await _dio.request( + args[0], + data: args[1]['data'], + queryParameters: args[1]['queryParameters'] ?? {}, + options: Options( + headers: args[1]['headers'] ?? {}, + method: args[1]['method'] ?? 'get', + ), + ); + Map headers = response.headers.map; + return {'data': response.data, 'headers': headers}; + }); // 设置 runtime.onMessage('registerSetting', (dynamic args) async { args[0]['package'] = extension.package; @@ -295,6 +314,22 @@ class ExtensionService { return res; } } + async RequestWithHeader(url, options) { + options = options || {}; + options.headers = options.headers || {}; + const miruUrl = options.headers["Miru-Url"] || "${extension.webSite}"; + options.method = options.method || "get"; + const res = await sendMessage( + "RequestWithHeader", + JSON.stringify([miruUrl + url, options]) + ); + console.log(res) + try { + return JSON.parse(res); + } catch (e) { + return res; + } + } querySelector(content, selector) { return new Element(content, selector); } @@ -327,6 +362,9 @@ class ExtensionService { } createFilter(filter){ throw new Error("not implement createFilter"); + } + updatePages(){ + } detail(url) { throw new Error("not implement detail"); @@ -503,6 +541,11 @@ class ExtensionService { result.headers ??= { "Referer": _cuurentRequestUrl, }; + if (result.pages == null) { + return result; + } + result.urls.addAll( + List.filled(result.pages! - result.urls.length, "")); return result; default: return ExtensionFikushonWatch.fromJson(data); @@ -510,6 +553,22 @@ class ExtensionService { }); } + Future updatePages(int page) async { + return _runExtension(() async { + final jsResult = await runtime.handlePromise( + await runtime + .evaluateAsync('stringify(()=>extenstion.updatePages("$page"))'), + ); + final data = jsonDecode(jsResult.stringResult); + + final result = ExtensionUpdatePages.fromJson(data); + result.headers ??= { + "Referer": _cuurentRequestUrl, + }; + return result; + }); + } + Future checkUpdate(url) async { return _runExtension(() async { final jsResult = await runtime.handlePromise( diff --git a/lib/models/extension.dart b/lib/models/extension.dart index 1a959364..05e8e606 100644 --- a/lib/models/extension.dart +++ b/lib/models/extension.dart @@ -186,11 +186,12 @@ class ExtensionMangaWatch { ExtensionMangaWatch({ required this.urls, this.headers, + this.pages, }); final List urls; late Map? headers; - + final int? pages; factory ExtensionMangaWatch.fromJson(Map json) => _$ExtensionMangaWatchFromJson(json); @@ -214,6 +215,21 @@ class ExtensionFikushonWatch { Map toJson() => _$ExtensionFikushonWatchToJson(this); } +@JsonSerializable() +class ExtensionUpdatePages { + late Map? headers; + final String url; + ExtensionUpdatePages({ + required this.url, + this.headers, + }); + + factory ExtensionUpdatePages.fromJson(Map json) => + _$ExtensionUpdatePagesFromJson(json); + + Map toJson() => _$ExtensionUpdatePagesToJson(this); +} + @JsonSerializable() class ExtensionLog { ExtensionLog({ diff --git a/lib/models/extension.g.dart b/lib/models/extension.g.dart index 98ba3920..dd819fdc 100644 --- a/lib/models/extension.g.dart +++ b/lib/models/extension.g.dart @@ -184,6 +184,7 @@ ExtensionMangaWatch _$ExtensionMangaWatchFromJson(Map json) => headers: (json['headers'] as Map?)?.map( (k, e) => MapEntry(k, e as String), ), + pages: json['pages'] as int?, ); Map _$ExtensionMangaWatchToJson( @@ -191,6 +192,7 @@ Map _$ExtensionMangaWatchToJson( { 'urls': instance.urls, 'headers': instance.headers, + 'pages': instance.pages, }; ExtensionFikushonWatch _$ExtensionFikushonWatchFromJson( @@ -210,6 +212,22 @@ Map _$ExtensionFikushonWatchToJson( 'subtitle': instance.subtitle, }; +ExtensionUpdatePages _$ExtensionUpdatePagesFromJson( + Map json) => + ExtensionUpdatePages( + url: json['url'] as String, + headers: (json['headers'] as Map?)?.map( + (k, e) => MapEntry(k, e as String), + ), + ); + +Map _$ExtensionUpdatePagesToJson( + ExtensionUpdatePages instance) => + { + 'headers': instance.headers, + 'url': instance.url, + }; + ExtensionLog _$ExtensionLogFromJson(Map json) => ExtensionLog( extension: Extension.fromJson(json['extension'] as Map), content: json['content'] as String, From 0b232f5aae07f0d010a72f35f6d0ca2cad9fd2a4 Mon Sep 17 00:00:00 2001 From: appdevelpo <56633229+appdevelpo@users.noreply.github.com> Date: Mon, 27 Nov 2023 18:58:58 +0800 Subject: [PATCH 02/23] add skipping inverval configuration for Desktop --- assets/i18n/en.json | 3 ++ lib/controllers/watch/video_controller.dart | 45 ++++++++++++++++++++- lib/utils/miru_storage.dart | 2 + lib/views/pages/settings_page.dart | 20 +++++++++ 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/assets/i18n/en.json b/assets/i18n/en.json index 54b3a33b..48226f16 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -99,6 +99,9 @@ "language-subtitle": "Change the language of the software", "extension-log": "Extension Log Window", "extension-log-subtitle": "Used for debugging extensions", + "skip-interval": "Skip Interval", + "skip-interval-subtitle-desktop":"press key I to seek ahead and key J to seek behind configured interval (miliseconds)", + "skip-interval-subtitle-mobile":"double tap right side of the player to seek ahead and double tap left side of the player to seek behind configured interval(miliseconds)", "about": "About", "official-site": "Official Website", "official-site-training": "Visit", diff --git a/lib/controllers/watch/video_controller.dart b/lib/controllers/watch/video_controller.dart index a5184249..b0f26a72 100644 --- a/lib/controllers/watch/video_controller.dart +++ b/lib/controllers/watch/video_controller.dart @@ -28,6 +28,7 @@ import 'package:window_manager/window_manager.dart'; import 'package:path/path.dart' as path; import 'package:fluent_ui/fluent_ui.dart' as fluent; import 'package:crypto/crypto.dart'; +import 'package:miru_app/utils/miru_storage.dart'; class VideoPlayerController extends GetxController { final String title; @@ -53,6 +54,7 @@ class VideoPlayerController extends GetxController { final isFullScreen = false.obs; late final index = playIndex.obs; final subtitles = [].obs; + final keyboardShortcuts = {}; final selectedSubtitle = 0.obs; // 是否已经自动跳转到上次播放进度 @@ -69,7 +71,6 @@ class VideoPlayerController extends GetxController { final currentTorrentFile = ''.obs; String _torrenHash = ""; - // 复制当前 context @override @@ -98,7 +99,6 @@ class VideoPlayerController extends GetxController { isOpenSidebar.value = false; } }); - // 切换字幕 ever(selectedSubtitle, (callback) { if (callback == -1) { @@ -190,6 +190,47 @@ class VideoPlayerController extends GetxController { }); super.onInit(); + final skipInterval = + int.parse(MiruStorage.getSetting(SettingKey.skipInterval)); + keyboardShortcuts.addAll({ + const SingleActivator(LogicalKeyboardKey.mediaPlay): () => player.play(), + const SingleActivator(LogicalKeyboardKey.mediaPause): () => + player.pause(), + const SingleActivator(LogicalKeyboardKey.mediaPlayPause): () => + player.playOrPause(), + const SingleActivator(LogicalKeyboardKey.mediaTrackNext): () => + player.next(), + const SingleActivator(LogicalKeyboardKey.mediaTrackPrevious): () => + player.previous(), + const SingleActivator(LogicalKeyboardKey.space): () => + player.playOrPause(), + const SingleActivator(LogicalKeyboardKey.keyJ): () { + final rate = + player.state.position - Duration(milliseconds: skipInterval); + player.seek(rate); + }, + const SingleActivator(LogicalKeyboardKey.keyI): () { + final rate = + player.state.position + Duration(milliseconds: skipInterval); + player.seek(rate); + }, + const SingleActivator(LogicalKeyboardKey.arrowLeft): () { + final rate = player.state.position - const Duration(seconds: 2); + player.seek(rate); + }, + const SingleActivator(LogicalKeyboardKey.arrowRight): () { + final rate = player.state.position + const Duration(seconds: 2); + player.seek(rate); + }, + const SingleActivator(LogicalKeyboardKey.arrowUp): () { + final volume = player.state.volume + 5.0; + player.setVolume(volume.clamp(0.0, 100.0)); + }, + const SingleActivator(LogicalKeyboardKey.arrowDown): () { + final volume = player.state.volume - 5.0; + player.setVolume(volume.clamp(0.0, 100.0)); + }, + }); } play() async { diff --git a/lib/utils/miru_storage.dart b/lib/utils/miru_storage.dart index ea574027..2a354cee 100644 --- a/lib/utils/miru_storage.dart +++ b/lib/utils/miru_storage.dart @@ -113,6 +113,7 @@ class MiruStorage { await _initSetting(SettingKey.enableNSFW, false); await _initSetting(SettingKey.videoPlayer, 'built-in'); await _initSetting(SettingKey.listMode, "grid"); + await _initSetting(SettingKey.skipInterval, "10000"); } static _initSetting(String key, dynamic value) async { @@ -141,4 +142,5 @@ class SettingKey { static String videoPlayer = 'VideoPlayer'; static String databaseVersion = 'DatabaseVersion'; static String listMode = 'ListMode'; + static String skipInterval = 'SkipInterval'; } diff --git a/lib/views/pages/settings_page.dart b/lib/views/pages/settings_page.dart index 61ee589c..29ccd026 100644 --- a/lib/views/pages/settings_page.dart +++ b/lib/views/pages/settings_page.dart @@ -295,6 +295,26 @@ class _SettingsPageState extends State { }, ), const SizedBox(height: 8), + if (!Platform.isAndroid) + SettingsIntpuTile( + icon: const PlatformWidget( + androidWidget: Icon(Icons.fast_forward_rounded), + desktopWidget: Icon(fluent.FluentIcons.fast_forward, size: 24), + ), + title: 'settings.skip-interval'.i18n, + buildSubtitle: () { + if (!Platform.isAndroid) { + return 'settings.skip-interval-subtitle-desktop'.i18n; + } + return 'settings.skip-interval-subtitle-mobile'.i18n; + }, + onChanged: (value) { + MiruStorage.setSetting(SettingKey.skipInterval, value); + Get.find().onRefresh(); + }, + text: MiruStorage.getSetting(SettingKey.skipInterval), + ), + const SizedBox(height: 8), ListTitle(title: 'settings.about'.i18n), const SizedBox(height: 8), SettingsTile( From d39fe3b812643bd560739a43900dbf72319bb588 Mon Sep 17 00:00:00 2001 From: appdevelpo <56633229+appdevelpo@users.noreply.github.com> Date: Fri, 1 Dec 2023 17:58:52 +0800 Subject: [PATCH 03/23] add features add zooming and page indicator for comic . doubletap to activate menu. update dependency and add Extended_Image for zooming in common mode. rename CacheNetWorkImage -> CacheNetWorkImagePic --- lib/controllers/watch/comic_controller.dart | 33 +- lib/views/pages/detail_page.dart | 8 +- .../extension/extension_settings_page.dart | 4 +- .../watch/reader/comic/comic_reader.dart | 28 +- .../reader/comic/comic_reader_content.dart | 335 ++++++++++++++-- .../watch/video/video_player_content.dart | 1 + lib/views/widgets/cache_network_image.dart | 41 +- lib/views/widgets/cover.dart | 2 +- .../detail/detail_appbar_flexible_space.dart | 2 +- .../widgets/detail/detail_extension_tile.dart | 2 +- lib/views/widgets/detail/detail_overview.dart | 2 +- .../widgets/extension/extension_card.dart | 4 +- .../widgets/extension/extension_tile.dart | 4 +- lib/views/widgets/log/extension_log_tile.dart | 2 +- lib/views/widgets/watch/comic_view.dart | 71 ++++ .../widgets/watch/control_panel_footer.dart | 2 +- macos/Flutter/GeneratedPluginRegistrant.swift | 2 +- pubspec.lock | 360 ++++++++---------- pubspec.yaml | 14 +- windows/flutter/CMakeLists.txt | 7 +- 20 files changed, 632 insertions(+), 292 deletions(-) create mode 100644 lib/views/widgets/watch/comic_view.dart diff --git a/lib/controllers/watch/comic_controller.dart b/lib/controllers/watch/comic_controller.dart index b7fa8a2d..bff80c8f 100644 --- a/lib/controllers/watch/comic_controller.dart +++ b/lib/controllers/watch/comic_controller.dart @@ -5,6 +5,7 @@ import 'package:miru_app/controllers/watch/reader_controller.dart'; import 'package:miru_app/data/services/database_service.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'dart:async'; +import 'package:extended_image/extended_image.dart'; class ComicController extends ReaderController { ComicController({ @@ -21,10 +22,11 @@ class ComicController extends ReaderController { // 当前页码 final currentPage = 0.obs; bool timerCancel = false; - final pageController = PageController().obs; + final pageController = ExtendedPageController().obs; final itemPositionsListener = ItemPositionsListener.create(); final itemScrollController = ItemScrollController(); final scrollOffsetController = ScrollOffsetController(); + final scrolloffsetListener = ScrollOffsetListener.create(); // 是否已经恢复上次阅读 final isRecover = false.obs; @@ -77,8 +79,8 @@ class ComicController extends ReaderController { Timer(const Duration(seconds: 2), () { //set update timer Timer.periodic(const Duration(milliseconds: 600), (timer) { - debugPrint('$curPage'); - if (curPage >= super.watchData.value!.urls.length - 1 || timerCancel) { + // debugPrint('$curPage'); + if (curPage >= super.watchData.value!.urls.length - 2 || timerCancel) { timer.cancel(); } curPage++; @@ -101,6 +103,11 @@ class ComicController extends ReaderController { readType.value = await DatabaseService.getMnagaReaderType(super.detailUrl); } + double mapValue(double value) { + double mappedValue = ((value - 0) * (1 - (-1))) / (2.5 - 0) + (-1); + return mappedValue; + } + _jumpPage(int page) { if (readType.value == MangaReadMode.webTonn) { if (itemScrollController.isAttached) { @@ -110,15 +117,15 @@ class ComicController extends ReaderController { } int curPage = currentPage.value; Timer.periodic(const Duration(milliseconds: 600), (timer) { - debugPrint('$curPage'); - if (curPage == -1 || timerCancel) { + // debugPrint('$curPage'); + if (curPage == 0 || timerCancel) { timer.cancel(); } - curPage--; if (super.watchData.value!.urls[curPage] == "") { _getPage(curPage); } + curPage--; }); return; } @@ -126,7 +133,7 @@ class ComicController extends ReaderController { pageController.value.jumpToPage(page); return; } - pageController.value = PageController(initialPage: page); + pageController.value = ExtendedPageController(initialPage: page); } // 下一页 @@ -154,13 +161,20 @@ class ComicController extends ReaderController { ); } else { scrollOffsetController.animateScroll( - duration: const Duration(milliseconds: 100), - curve: Curves.ease, + duration: const Duration(milliseconds: 10), + curve: Curves.linear, offset: -200.0, ); } } + // void scrollWithOffset(double offset) { + // scrollOffsetController.animateScroll( + // duration: const Duration(milliseconds: 100), + // curve: Curves.ease, + // offset: offset); + // } + @override void onClose() { if (super.watchData.value != null) { @@ -172,6 +186,7 @@ class ComicController extends ReaderController { ); } pageController.value.dispose(); + timerCancel = true; super.onClose(); } diff --git a/lib/views/pages/detail_page.dart b/lib/views/pages/detail_page.dart index e20fb5e3..4db4bd99 100644 --- a/lib/views/pages/detail_page.dart +++ b/lib/views/pages/detail_page.dart @@ -185,7 +185,7 @@ class _DetailPageState extends State { shape: BoxShape.circle, ), clipBehavior: Clip.antiAlias, - child: CacheNetWorkImage( + child: CacheNetWorkImagePic( url, width: 50, height: 50, @@ -284,7 +284,7 @@ class _DetailPageState extends State { decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), ), - child: CacheNetWorkImage( + child: CacheNetWorkImagePic( c.detail?.cover ?? '', headers: c.detail?.headers, ), @@ -377,7 +377,7 @@ class _DetailPageState extends State { ), clipBehavior: Clip.antiAlias, margin: const EdgeInsets.only(right: 8), - child: CacheNetWorkImage( + child: CacheNetWorkImagePic( url, height: 200, ), @@ -432,7 +432,7 @@ class _DetailPageState extends State { shape: BoxShape.circle, ), clipBehavior: Clip.antiAlias, - child: CacheNetWorkImage( + child: CacheNetWorkImagePic( url ?? '', width: 100, height: 100, diff --git a/lib/views/pages/extension/extension_settings_page.dart b/lib/views/pages/extension/extension_settings_page.dart index 8a86f65f..b7ba990a 100644 --- a/lib/views/pages/extension/extension_settings_page.dart +++ b/lib/views/pages/extension/extension_settings_page.dart @@ -144,7 +144,7 @@ class _ExtensionSettingsPageState extends State { borderRadius: BorderRadius.circular(10), ), clipBehavior: Clip.antiAlias, - child: CacheNetWorkImage( + child: CacheNetWorkImagePic( extension.icon ?? '', fit: BoxFit.contain, ), @@ -308,7 +308,7 @@ class _ExtensionSettingsPageState extends State { borderRadius: BorderRadius.circular(10), ), clipBehavior: Clip.antiAlias, - child: CacheNetWorkImage( + child: CacheNetWorkImagePic( extension.icon ?? '', fit: BoxFit.contain, ), diff --git a/lib/views/pages/watch/reader/comic/comic_reader.dart b/lib/views/pages/watch/reader/comic/comic_reader.dart index 688b79e0..ee5ee035 100644 --- a/lib/views/pages/watch/reader/comic/comic_reader.dart +++ b/lib/views/pages/watch/reader/comic/comic_reader.dart @@ -4,8 +4,10 @@ import 'package:miru_app/models/extension.dart'; import 'package:miru_app/views/pages/watch/reader/comic/comic_reader_content.dart'; import 'package:miru_app/views/pages/watch/reader/comic/comic_reader_settings.dart'; import 'package:miru_app/controllers/watch/comic_controller.dart'; -import 'package:miru_app/views/widgets/watch/reader_view.dart'; +// import 'package:miru_app/views/pages/watch/reader/comic/comic_zoom.dart'; +import 'package:miru_app/views/widgets/watch/comic_view.dart'; import 'package:miru_app/data/services/extension_service.dart'; +import 'package:miru_app/views/widgets/platform_widget.dart'; import 'package:window_manager/window_manager.dart'; class ComicReader extends StatefulWidget { @@ -58,12 +60,22 @@ class _ComicReaderState extends State { @override Widget build(BuildContext context) { - return ReaderView( - widget.title, - content: DragToMoveArea( - child: ComicReaderContent(widget.title), - ), - buildSettings: (context) => ComicReaderSettings(widget.title), - ); + return PlatformBuildWidget( + androidBuilder: (context) { + return ReaderView( + widget.title, + content: Center( + child: ComicReaderContent(widget.title), + ), + buildSettings: (context) => ComicReaderSettings(widget.title), + ); + }, + desktopBuilder: (context) => ReaderView( + widget.title, + content: DragToMoveArea( + child: ComicReaderContent(widget.title), + ), + buildSettings: (context) => ComicReaderSettings(widget.title), + )); } } diff --git a/lib/views/pages/watch/reader/comic/comic_reader_content.dart b/lib/views/pages/watch/reader/comic/comic_reader_content.dart index b97d02f2..bf2589ab 100644 --- a/lib/views/pages/watch/reader/comic/comic_reader_content.dart +++ b/lib/views/pages/watch/reader/comic/comic_reader_content.dart @@ -12,6 +12,7 @@ import 'package:miru_app/views/widgets/cache_network_image.dart'; import 'package:miru_app/views/widgets/platform_widget.dart'; import 'package:miru_app/views/widgets/progress.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; +import 'package:extended_image/extended_image.dart'; class ComicReaderContent extends StatefulWidget { const ComicReaderContent(this.tag, {Key? key}) : super(key: key); @@ -23,6 +24,12 @@ class ComicReaderContent extends StatefulWidget { class _ComicReaderContentState extends State { late final _c = Get.find(tag: widget.tag); + final zoomScale = 1.0.obs; + bool isZoomed = false; + + TransformationController transformationController = + TransformationController(); + final double minScaleValue = 1.0; _buildContent() { late Color backgroundColor; if (Platform.isAndroid) { @@ -89,50 +96,298 @@ class _ComicReaderContentState extends State { final images = _c.watchData.value!.urls; final readerType = _c.readType.value; final cuurentPage = _c.currentPage.value; - if (readerType == MangaReadMode.webTonn) { - return ScrollablePositionedList.builder( - padding: EdgeInsets.symmetric( - horizontal: viewPadding, + //zooming is inspired by: https://github.com/flutter/flutter/issues/86531 + + return Stack(children: [ + InteractiveViewer( + minScale: minScaleValue, + // maxScale: 2.0, + transformationController: transformationController, + onInteractionEnd: (ScaleEndDetails endDetails) { + setState(() { + isZoomed = false; + }); + }, + onInteractionUpdate: (x) { + double correctScaleValue = + transformationController.value.getMaxScaleOnAxis(); + if (correctScaleValue > minScaleValue) ; + if (x.scale == correctScaleValue) { + setState(() { + isZoomed = false; + }); + } + setState(() { + isZoomed = true; + }); + debugPrint("${x.scale}"); + }, + child: ScrollablePositionedList.builder( + padding: EdgeInsets.symmetric( + horizontal: viewPadding, + ), + initialScrollIndex: cuurentPage, + itemScrollController: _c.itemScrollController, + itemPositionsListener: _c.itemPositionsListener, + scrollOffsetController: _c.scrollOffsetController, + scrollOffsetListener: _c.scrolloffsetListener, + physics: isZoomed + ? const NeverScrollableScrollPhysics() + : const ScrollPhysics(), + itemBuilder: (context, index) { + final url = images[index]; + return Obx( + () => CacheNetWorkImageComic( + url, + fit: BoxFit.cover, + headers: _c.watchData.value?.headers, + ), + ); + }, + itemCount: images.length, + ), ), - initialScrollIndex: cuurentPage, - itemScrollController: _c.itemScrollController, - itemPositionsListener: _c.itemPositionsListener, - scrollOffsetController: _c.scrollOffsetController, - itemBuilder: (context, index) { - final url = images[index]; - return CacheNetWorkImage( - url, - fit: BoxFit.fitWidth, - headers: _c.watchData.value?.headers, - ); - }, - itemCount: images.length, - ); + Positioned.fill( + top: 120, + bottom: 120, + child: GestureDetector( + onTapDown: (TapDownDetails details) { + final xPos = details.globalPosition.dx; + if (xPos > Get.width * 4 / 5) { + return _c.nextPage(); + } + if (xPos < Get.width / 5) { + return _c.previousPage(); + } + }, + )), + Container( + alignment: Alignment.bottomCenter, + margin: const EdgeInsets.only(bottom: 40), + child: Container( + decoration: BoxDecoration( + color: Colors.blue, + borderRadius: BorderRadius.circular(40), + ), + alignment: Alignment.center, + width: 200, + height: 30, + child: Text( + "${cuurentPage + 1}/${images.length}", + style: const TextStyle( + color: Colors.white, fontSize: 15), + ))) + ]); + + //works but lots of bugs + // return Stack(children: [ + // Obx(() => Transform.scale( + // scale: zoomScale.value, + // child: Transform.translate( + // offset: Offset(x.value, y.value), + // child: ScrollablePositionedList.builder( + // padding: EdgeInsets.symmetric( + // horizontal: viewPadding, + // ), + // initialScrollIndex: cuurentPage, + // itemScrollController: _c.itemScrollController, + // itemPositionsListener: _c.itemPositionsListener, + // scrollOffsetController: _c.scrollOffsetController, + // // physics: const BouncingScrollPhysics(), + // itemBuilder: (context, index) { + // final url = images[index]; + // return Obx( + // () => CacheNetWorkImagePic( + // url, + // fit: BoxFit.cover, + // headers: _c.watchData.value?.headers, + // ), + // ); + // }, + // itemCount: images.length, + // )))), + // Positioned.fill( + // top: 120, + // bottom: 120, + // child: GestureDetector( + // onPanStart: (DragStartDetails detail) { + // initPointer = detail.globalPosition; + // panStart = true; + // debugPrint("start"); + // }, + // onTapDown: (TapDownDetails details) { + // final xPos = details.globalPosition.dx; + // if (xPos > Get.width * 4 / 5) { + // return _c.nextPage(); + // } + // if (xPos < Get.width / 5) { + // return _c.previousPage(); + // } + // }, + // onPanUpdate: (DragUpdateDetails detail) { + // final dx = detail.delta.dx; + // final dy = detail.delta.dy; + // final innerProduct = prevdx * dx + prevdy * dy; + // //zooming event + // if (innerProduct < 0 && innerProduct >= -1.0) { + // Offset pos = detail.globalPosition; + // if (panStart) { + // scaleRange = (pos - initPointer).distance; + // panStart = false; + // prevdist = (pos - initPointer).distanceSquared; + // return; + // } + // if (dx > prevdx) { + // pointer1 = pos; + // } else { + // pointer2 = pos; + // } + // prevPointer = pos; + // // debugPrint("${(pointer1 + pointer2) / 2}"); + // // debugPrint("$maxWidth"); + // final pointerDistance = + // (pointer1 - pointer2).distanceSquared; + // if (pointerDistance < maxWidth * 60) { + // // debugPrint("unzoom"); + // zoomScale.value -= 0.04; + // // return; + // } else { + // // debugPrint("zoom"); + // zoomScale.value += 0.04; + // } + // debugPrint("${zoomScale.value}"); + + // // debugPrint("$pointer1,$pointer2"); + // // debugPrint("$pointerDistance"); + // // debugPrint("${(pointerDistance / scaleRange)}"); + // // final double scale = + // // (pointerDistance - maxWidth) / (scaleRange); + // // debugPrint("$scale"); + // // double s = scale * 2 / 10000 + 1; + // // debugPrint("$s"); + // zoomScale.value = zoomScale.value.clamp(1, 3); + // // debugPrint("$zoomScale"); + // } else { + // x.value += dx; + // // y.value += dy; + // } + + // prevdy = dy; + // prevdx = dx; + // //fixing the conflict between scrolling and gesture widget + // if (dy > 0) { + // _c.scrollWithOffset( + // -innerProduct * zoomScale.value * 3); + // return; + // } + // _c.scrollWithOffset(innerProduct * zoomScale.value * 3); + // }, + // )), + // Container( + // alignment: Alignment.bottomCenter, + // margin: const EdgeInsets.only(bottom: 40), + // child: Container( + // decoration: BoxDecoration( + // color: Colors.blue, + // borderRadius: BorderRadius.circular(40), + // ), + // alignment: Alignment.center, + // width: 200, + // height: 30, + // child: Text( + // "${cuurentPage + 1}/${images.length}", + // style: TextStyle(color: Colors.white, fontSize: 15), + // )), + // ), + // ]); } +//common mode and left to right mode + return Stack(children: [ + ExtendedImageGesturePageView.builder( + reverse: readerType == MangaReadMode.rightToLeft, + onPageChanged: (index) { + _c.currentPage.value = index; + }, + scrollDirection: Axis.horizontal, + controller: _c.pageController.value, + itemBuilder: (BuildContext context, int index) { + final url = images[index]; + return Container( + padding: EdgeInsets.symmetric( + horizontal: viewPadding, + ), + child: ExtendedImage.network( + url, + mode: ExtendedImageMode.gesture, + key: ValueKey(url), + fit: BoxFit.contain, + headers: _c.watchData.value?.headers, + ), + ); + }), + Positioned.fill( + top: 120, + bottom: 120, + child: GestureDetector( + onTapDown: (TapDownDetails details) { + final xPos = details.globalPosition.dx; + if (xPos > Get.width * 4 / 5) { + if (readerType == MangaReadMode.rightToLeft) { + return _c.previousPage(); + } + return _c.nextPage(); + } + if (xPos < Get.width / 5) { + if (readerType == MangaReadMode.rightToLeft) { + return _c.nextPage(); + } + return _c.previousPage(); + } + }, + )), + Container( + alignment: Alignment.bottomCenter, + margin: const EdgeInsets.only(bottom: 40), + child: Container( + decoration: BoxDecoration( + color: Colors.blue, + borderRadius: BorderRadius.circular(40), + ), + alignment: Alignment.center, + width: 200, + height: 30, + child: Text( + "${cuurentPage + 1}/${images.length}", + style: const TextStyle(color: Colors.white, fontSize: 15), + )), + ), + ]); - return PageView.builder( - reverse: readerType == MangaReadMode.rightToLeft, - controller: _c.pageController.value, - onPageChanged: (index) { - _c.currentPage.value = index; - }, - itemBuilder: (context, index) { - final url = images[index]; - return Container( - padding: EdgeInsets.symmetric( - horizontal: viewPadding, - ), - child: CacheNetWorkImage( - url, - key: ValueKey(url), - fit: BoxFit.contain, - headers: _c.watchData.value?.headers, - ), - ); - }, - itemCount: images.length, - ); + //old code + + // PageView.builder( + // reverse: readerType == MangaReadMode.rightToLeft, + // controller: _c.pageController.value, + // onPageChanged: (index) { + // _c.currentPage.value = index; + // }, + // itemBuilder: (context, index) { + // final url = images[index]; + // return Container( + // padding: EdgeInsets.symmetric( + // horizontal: viewPadding, + // ), + // child: CacheNetWorkImagePic( + // url, + // key: ValueKey(url), + // fit: BoxFit.contain, + // headers: _c.watchData.value?.headers, + // ), + // ); + // }, + // itemCount: images.length, + // ); }); })), ), diff --git a/lib/views/pages/watch/video/video_player_content.dart b/lib/views/pages/watch/video/video_player_content.dart index 54facb58..a1ad98ea 100644 --- a/lib/views/pages/watch/video/video_player_content.dart +++ b/lib/views/pages/watch/video/video_player_content.dart @@ -52,6 +52,7 @@ class _VideoPlayerContenState extends State { return MaterialDesktopVideoControlsTheme( normal: MaterialDesktopVideoControlsThemeData( toggleFullscreenOnDoublePress: false, + keyboardShortcuts: _c.keyboardShortcuts, topButtonBar: [ Obx( () => Expanded( diff --git a/lib/views/widgets/cache_network_image.dart b/lib/views/widgets/cache_network_image.dart index c31d09a2..b87bf662 100644 --- a/lib/views/widgets/cache_network_image.dart +++ b/lib/views/widgets/cache_network_image.dart @@ -2,8 +2,45 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:fluent_ui/fluent_ui.dart' as fluent; import 'package:flutter/material.dart'; -class CacheNetWorkImage extends StatelessWidget { - const CacheNetWorkImage( +class CacheNetWorkImagePic extends StatelessWidget { + const CacheNetWorkImagePic( + this.url, { + Key? key, + this.fit = BoxFit.cover, + this.width, + this.height, + this.fallback, + this.headers, + }) : super(key: key); + final String url; + final BoxFit fit; + final double? width; + final double? height; + final Widget? fallback; + final Map? headers; + + _errorBuild() { + if (fallback != null) { + return fallback!; + } + return const Center(child: Icon(fluent.FluentIcons.error)); + } + + @override + Widget build(BuildContext context) { + return CachedNetworkImage( + imageUrl: url, + httpHeaders: headers, + fit: fit, + width: width, + height: height, + errorWidget: (context, url, error) => _errorBuild(), + ); + } +} + +class CacheNetWorkImageComic extends StatelessWidget { + const CacheNetWorkImageComic( this.url, { Key? key, this.fit = BoxFit.cover, diff --git a/lib/views/widgets/cover.dart b/lib/views/widgets/cover.dart index 30ed9e68..a563c10a 100644 --- a/lib/views/widgets/cover.dart +++ b/lib/views/widgets/cover.dart @@ -16,7 +16,7 @@ class Cover extends StatelessWidget { @override Widget build(BuildContext context) { if (url != null) { - return CacheNetWorkImage( + return CacheNetWorkImagePic( url!, width: double.infinity, height: double.infinity, diff --git a/lib/views/widgets/detail/detail_appbar_flexible_space.dart b/lib/views/widgets/detail/detail_appbar_flexible_space.dart index 831748c5..6042d6e1 100644 --- a/lib/views/widgets/detail/detail_appbar_flexible_space.dart +++ b/lib/views/widgets/detail/detail_appbar_flexible_space.dart @@ -105,7 +105,7 @@ class _DetailAppbarflexibleSpaceState extends State { width: 100, child: c.isLoading.value ? const Center(child: CircularProgressIndicator()) - : CacheNetWorkImage( + : CacheNetWorkImagePic( c.data.value?.cover ?? '', fit: BoxFit.cover, headers: c.detail?.headers, diff --git a/lib/views/widgets/detail/detail_extension_tile.dart b/lib/views/widgets/detail/detail_extension_tile.dart index aa80d342..6e56b62f 100644 --- a/lib/views/widgets/detail/detail_extension_tile.dart +++ b/lib/views/widgets/detail/detail_extension_tile.dart @@ -35,7 +35,7 @@ class DetailExtensionTile extends StatelessWidget { shape: BoxShape.circle, ), clipBehavior: Clip.antiAlias, - child: CacheNetWorkImage( + child: CacheNetWorkImagePic( c.extension!.icon!, width: 20, ), diff --git a/lib/views/widgets/detail/detail_overview.dart b/lib/views/widgets/detail/detail_overview.dart index 6712021e..e4e9537e 100644 --- a/lib/views/widgets/detail/detail_overview.dart +++ b/lib/views/widgets/detail/detail_overview.dart @@ -58,7 +58,7 @@ class DetailOverView extends StatelessWidget { ), clipBehavior: Clip.antiAlias, margin: const EdgeInsets.only(right: 8), - child: CacheNetWorkImage( + child: CacheNetWorkImagePic( url, height: 160, ), diff --git a/lib/views/widgets/extension/extension_card.dart b/lib/views/widgets/extension/extension_card.dart index c8f116d4..549064f0 100644 --- a/lib/views/widgets/extension/extension_card.dart +++ b/lib/views/widgets/extension/extension_card.dart @@ -73,7 +73,7 @@ class _ExtensionCardState extends State { leading: SizedBox( width: 35, height: 35, - child: CacheNetWorkImage( + child: CacheNetWorkImagePic( icon, fit: BoxFit.contain, fallback: const Icon(Icons.extension), @@ -162,7 +162,7 @@ class _ExtensionCardState extends State { borderRadius: BorderRadius.circular(8), ), clipBehavior: Clip.antiAlias, - child: CacheNetWorkImage( + child: CacheNetWorkImagePic( icon, width: 64, height: 64, diff --git a/lib/views/widgets/extension/extension_tile.dart b/lib/views/widgets/extension/extension_tile.dart index f8ee5942..2e502612 100644 --- a/lib/views/widgets/extension/extension_tile.dart +++ b/lib/views/widgets/extension/extension_tile.dart @@ -29,7 +29,7 @@ class _ExtensionTileState extends State { leading: SizedBox( width: 35, height: 35, - child: CacheNetWorkImage( + child: CacheNetWorkImagePic( widget.extension.icon ?? '', key: ValueKey(widget.extension.icon), fit: BoxFit.contain, @@ -96,7 +96,7 @@ class _ExtensionTileState extends State { child: SizedBox( width: 45, height: 45, - child: CacheNetWorkImage( + child: CacheNetWorkImagePic( widget.extension.icon ?? '', key: ValueKey(widget.extension.icon), fit: BoxFit.contain, diff --git a/lib/views/widgets/log/extension_log_tile.dart b/lib/views/widgets/log/extension_log_tile.dart index f8a7062e..8ef563e6 100644 --- a/lib/views/widgets/log/extension_log_tile.dart +++ b/lib/views/widgets/log/extension_log_tile.dart @@ -42,7 +42,7 @@ class ExtensionLogTile extends StatelessWidget { shape: BoxShape.circle, ), clipBehavior: Clip.antiAlias, - child: CacheNetWorkImage( + child: CacheNetWorkImagePic( log.extension.icon!, width: 32, height: 32, diff --git a/lib/views/widgets/watch/comic_view.dart b/lib/views/widgets/watch/comic_view.dart new file mode 100644 index 00000000..3bc9efe3 --- /dev/null +++ b/lib/views/widgets/watch/comic_view.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:miru_app/views/widgets/watch/control_panel_footer.dart'; +import 'package:miru_app/views/widgets/watch/control_panel_header.dart'; +import 'package:miru_app/controllers/watch/reader_controller.dart'; + +class ReaderView extends StatelessWidget { + const ReaderView( + this.tag, { + Key? key, + required this.content, + required this.buildSettings, + }) : super(key: key); + final String tag; + final Widget content; + final Widget Function(BuildContext context) buildSettings; + + @override + Widget build(BuildContext context) { + final c = Get.find(tag: tag); + return Obx( + () => Stack( + children: [ + MouseRegion( + onHover: (event) { + if (event.position.dy < 60) { + c.showControlPanel(); + } + if (event.position.dy > Get.height - 60) { + c.showControlPanel(); + } + }, + child: content, + ), + + // 点击中间显示控制面板 + if (c.error.value.isEmpty) + Positioned( + top: 120, + bottom: 120, + left: 0, + right: 0, + child: GestureDetector( + onDoubleTap: () { + // 中间点击的话 将不会定时关闭 + c.isShowControlPanel.value = !c.isShowControlPanel.value; + }, + ), + ), + + if (c.isShowControlPanel.value) ...[ + // 顶部控制 + Positioned( + child: ControlPanelHeader( + tag, + buildSettings: buildSettings, + ), + ), + // 底部控制 + Positioned( + right: 0, + left: 0, + bottom: 0, + child: ControlPanelFooter(tag), + ), + ], + ], + ), + ); + } +} diff --git a/lib/views/widgets/watch/control_panel_footer.dart b/lib/views/widgets/watch/control_panel_footer.dart index 74100e9b..2ae0bb27 100644 --- a/lib/views/widgets/watch/control_panel_footer.dart +++ b/lib/views/widgets/watch/control_panel_footer.dart @@ -19,7 +19,7 @@ class ControlPanelFooter extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 20), decoration: BoxDecoration( color: Platform.isAndroid - ? Theme.of(context).colorScheme.background + ? Theme.of(context).colorScheme.background.withOpacity(0.5) : Colors.transparent, borderRadius: const BorderRadius.only( topLeft: Radius.circular(40), diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 967b5887..f71d65fb 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -29,7 +29,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { IsarFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "IsarFlutterLibsPlugin")) MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin")) MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin")) - FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) + FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin")) ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 8bfb787a..dced9361 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -21,18 +21,18 @@ packages: dependency: "direct main" description: name: android_intent_plus - sha256: f72ae20bb37108694f442e7ae6acbd28b453ca62ce86842f6787b784355abfe6 + sha256: e1c62bb41c90e15083b7fb84dc327fe90396cc9c1445b55ff1082144fabfb4d9 url: "https://pub.dev" source: hosted - version: "4.0.2" + version: "4.0.3" archive: dependency: transitive description: name: archive - sha256: "20071638cbe4e5964a427cfa0e86dce55d060bc7d82d56f3554095d7239a8765" + sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b" url: "https://pub.dev" source: hosted - version: "3.4.2" + version: "3.4.9" args: dependency: transitive description: @@ -85,18 +85,18 @@ packages: dependency: transitive description: name: build_daemon - sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.0.1" build_resolvers: dependency: transitive description: name: build_resolvers - sha256: d912852cce27c9e80a93603db721c267716894462e7033165178b91138587972 + sha256: "64e12b0521812d1684b1917bc80945625391cb9bdd4312536b1d69dcb6133ed8" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.1" build_runner: dependency: "direct dev" description: @@ -109,10 +109,10 @@ packages: dependency: transitive description: name: build_runner_core - sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41" + sha256: c9e32d21dd6626b5c163d48b037ce906bbe428bc23ab77bcd77bb21e593b6185 url: "https://pub.dev" source: hosted - version: "7.2.10" + version: "7.2.11" built_collection: dependency: transitive description: @@ -125,34 +125,34 @@ packages: dependency: transitive description: name: built_value - sha256: a8de5955205b4d1dbbbc267daddf2178bd737e4bab8987c04a500478c9651e74 + sha256: "69acb7007eb2a31dc901512bfe0f7b767168be34cb734835d54c070bfa74c1b2" url: "https://pub.dev" source: hosted - version: "8.6.3" + version: "8.8.0" cached_network_image: dependency: "direct main" description: name: cached_network_image - sha256: fd3d0dc1d451f9a252b32d95d3f0c3c487bc41a75eba2e6097cb0b9c71491b15 + sha256: f98972704692ba679db144261172a8e20feb145636c617af0eb4022132a6797f url: "https://pub.dev" source: hosted - version: "3.2.3" + version: "3.3.0" cached_network_image_platform_interface: dependency: transitive description: name: cached_network_image_platform_interface - sha256: bb2b8403b4ccdc60ef5f25c70dead1f3d32d24b9d6117cfc087f496b178594a7 + sha256: "56aa42a7a01e3c9db8456d9f3f999931f1e05535b5a424271e9a38cabf066613" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "3.0.0" cached_network_image_web: dependency: transitive description: name: cached_network_image_web - sha256: b8eb814ebfcb4dea049680f8c1ffb2df399e4d03bf7a352c775e26fa06e02fa0 + sha256: "759b9a9f8f6ccbb66c185df805fac107f05730b1dab9c64626d1008cca532257" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.1.0" characters: dependency: transitive description: @@ -189,18 +189,18 @@ packages: dependency: transitive description: name: code_builder - sha256: "315a598c7fbe77f22de1c9da7cfd6fd21816312f16ffa124453b4fc679e540f1" + sha256: b2151ce26a06171005b379ecff6e08d34c470180ffe16b8e14b6d52be292b55f url: "https://pub.dev" source: hosted - version: "4.6.0" + version: "4.8.0" collection: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" colorize: dependency: transitive description: @@ -225,14 +225,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.8" - coverage: - dependency: transitive - description: - name: coverage - sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097" - url: "https://pub.dev" - source: hosted - version: "1.6.3" crypto: dependency: "direct main" description: @@ -269,10 +261,10 @@ packages: dependency: transitive description: name: dbus - sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" + sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" url: "https://pub.dev" source: hosted - version: "0.7.8" + version: "0.7.10" desktop_multi_window: dependency: "direct main" description: @@ -293,10 +285,10 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: "86add5ef97215562d2e090535b0a16f197902b10c369c558a100e74ea06e8659" + sha256: "0042cb3b2a76413ea5f8a2b40cec2a33e01d0c937e91f0f7c211fde4f7739ba6" url: "https://pub.dev" source: hosted - version: "9.0.3" + version: "9.1.1" device_info_plus_platform_interface: dependency: transitive description: @@ -309,26 +301,26 @@ packages: dependency: "direct main" description: name: dio - sha256: "417e2a6f9d83ab396ec38ff4ea5da6c254da71e4db765ad737a42af6930140b7" + sha256: "01870acd87986f768e0c09cc4d7a19a59d814af7b34cbeb0b437d2c33bdfea4c" url: "https://pub.dev" source: hosted - version: "5.3.3" + version: "5.3.4" dio_cookie_manager: dependency: "direct main" description: name: dio_cookie_manager - sha256: "884b1099cba70a83751969d318084577c29de148600ffb7d4796f752d2cade02" + sha256: e79498b0f632897ff0c28d6e8178b4bc6e9087412401f618c31fa0904ace050d url: "https://pub.dev" source: hosted - version: "3.1.0+1" + version: "3.1.1" easy_refresh: dependency: "direct main" description: name: easy_refresh - sha256: "77b025ea49f27b5ebc5eef40a6678be52564c293bd97ce91a4088d6646478329" + sha256: a3b33b60a97e4931c3923e8b80e2f2869a080001c8742213d484c37db0d15164 url: "https://pub.dev" source: hosted - version: "3.3.2+1" + version: "3.3.2+4" equatable: dependency: transitive description: @@ -341,10 +333,26 @@ packages: dependency: transitive description: name: expressions - sha256: "75ca4343f9f8a38087bea130cf51395d737d87c6947cc19cbb8fb2732cae1a27" + sha256: "0a3a207dc4697bbb0e17c731c7f10f2df6f88d9dbb99ac83c4168a10b57e7a20" url: "https://pub.dev" source: hosted - version: "0.2.5" + version: "0.2.5+1" + extended_image: + dependency: "direct main" + description: + name: extended_image + sha256: d7f091d068fcac7246c4b22a84b8dac59a62e04d29a5c172710c696e67a22f94 + url: "https://pub.dev" + source: hosted + version: "8.2.0" + extended_image_library: + dependency: transitive + description: + name: extended_image_library + sha256: "9b55fc5ebc65fad984de66b8f177a1bef2a84d79203c9c213f75ff83c2c29edd" + url: "https://pub.dev" + source: hosted + version: "4.0.1" fake_async: dependency: transitive description: @@ -373,10 +381,10 @@ packages: dependency: "direct main" description: name: file_picker - sha256: be325344c1f3070354a1d84a231a1ba75ea85d413774ec4bdf444c023342e030 + sha256: "4e42aacde3b993c5947467ab640882c56947d9d27342a5b6f2895b23956954a6" url: "https://pub.dev" source: hosted - version: "5.5.0" + version: "6.1.1" fixnum: dependency: transitive description: @@ -389,10 +397,10 @@ packages: dependency: "direct main" description: name: fluent_ui - sha256: "43e95034b12a8285a78703b5995bb4d2e02a28750a1a363044c150533bc7f027" + sha256: "460af0e4fe46e81818e7244e011d3e64febcc4ca00ff8ff994e5ae8ad9f14fa4" url: "https://pub.dev" source: hosted - version: "4.7.6" + version: "4.8.1" flutter: dependency: "direct main" description: flutter @@ -402,18 +410,10 @@ packages: dependency: "direct main" description: name: flutter_animate - sha256: "62f346340a96192070e31e3f2a1bd30a28530f1fe8be978821e06cd56b74d6d2" - url: "https://pub.dev" - source: hosted - version: "4.2.0+1" - flutter_blurhash: - dependency: transitive - description: - name: flutter_blurhash - sha256: "05001537bd3fac7644fa6558b09ec8c0a3f2eba78c0765f88912882b1331a5c6" + sha256: "1dbc1aabfb8ec1e9d9feed2b675c21fb6b0a11f99be53ec3bc0f1901af6a8eb7" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "4.3.0" flutter_cache_manager: dependency: transitive description: @@ -426,10 +426,10 @@ packages: dependency: "direct main" description: name: flutter_code_editor - sha256: d4f94719d3f4dc3f40b0b80c980e3b550069c413542b6c0521bff8267f57eefb + sha256: "56673b62f2d844c1a2b0cf43a4495be0d6123111de5f33887781d2d3b39a77b6" url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.3.1" flutter_highlight: dependency: "direct main" description: @@ -442,34 +442,34 @@ packages: dependency: "direct main" description: name: flutter_i18n - sha256: b71fe887697686368c93e4d2257ecdb3c35fe38686a6fbf3902d6355c3f20363 + sha256: c7e9752c5dea38963bed26513c25a6e5db70c110fc2c68b84694aafa37b29636 url: "https://pub.dev" source: hosted - version: "0.33.0" + version: "0.34.0" flutter_inappwebview: dependency: "direct main" description: name: flutter_inappwebview - sha256: f73505c792cf083d5566e1a94002311be497d984b5607f25be36d685cf6361cf + sha256: d198297060d116b94048301ee6749cd2e7d03c1f2689783f52d210a6b7aba350 url: "https://pub.dev" source: hosted - version: "5.7.2+3" + version: "5.8.0" flutter_js: dependency: "direct main" description: name: flutter_js - sha256: c4e0ff2cac3378e34eb0cafb58089377a7722d3531cdf9fcd5a53b5afc66c2dc + sha256: "5bf5db354fe78fe24cb90a5fa6b4423d38712440c88e3445c3dc88bc134c452f" url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.8.0" flutter_lints: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.1" flutter_localizations: dependency: transitive description: flutter @@ -479,18 +479,18 @@ packages: dependency: "direct main" description: name: flutter_markdown - sha256: a10979814c5f4ddbe2b6143fba25d927599e21e3ba65b3862995960606fae78f + sha256: "35108526a233cc0755664d445f8a6b4b61e6f8fe993b3658b80b4a26827fc196" url: "https://pub.dev" source: hosted - version: "0.6.17+3" + version: "0.6.18+2" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: f185ac890306b5779ecbd611f52502d8d4d63d27703ef73161ca0407e815f02c + sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da url: "https://pub.dev" source: hosted - version: "2.0.16" + version: "2.0.17" flutter_test: dependency: "direct dev" description: flutter @@ -529,10 +529,10 @@ packages: dependency: "direct main" description: name: go_router - sha256: a07c781bf55bf11ae85133338e4850f0b4e33e261c44a66c750fc707d65d8393 + sha256: c247a4f76071c3b97bb5ae8912968870d5565644801c5e09f3bc961b4d874895 url: "https://pub.dev" source: hosted - version: "11.1.2" + version: "12.1.1" graphs: dependency: transitive description: @@ -577,10 +577,18 @@ packages: dependency: transitive description: name: http - sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" + sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + http_client_helper: + dependency: transitive + description: + name: http_client_helper + sha256: "8a9127650734da86b5c73760de2b404494c968a3fd55602045ffec789dac3cb1" url: "https://pub.dev" source: hosted - version: "0.13.6" + version: "3.0.0" http_multi_server: dependency: transitive description: @@ -601,10 +609,10 @@ packages: dependency: transitive description: name: image - sha256: "6e703d5e2f8c63fb31a77753915c1ec8baebde8088844e0d29f71b8f0b108888" + sha256: "028f61960d56f26414eb616b48b04eb37d700cbe477b7fb09bf1d7ce57fd9271" url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "4.1.3" intl: dependency: transitive description: @@ -681,10 +689,10 @@ packages: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" logging: dependency: transitive description: @@ -729,18 +737,18 @@ packages: dependency: "direct main" description: name: media_kit - sha256: "1283b500341d41f033478706204a2b4ae2612e9b331c934bc4fad8c4bb869f6d" + sha256: "3289062540e3b8b9746e5c50d95bd78a9289826b7227e253dff806d002b9e67a" url: "https://pub.dev" source: hosted - version: "1.1.8+2" + version: "1.1.10+1" media_kit_libs_android_video: dependency: "direct main" description: name: media_kit_libs_android_video - sha256: a7ef60926ac528e2fabe9ee7084e648e385422a881ba914c978a7a81e6595dee + sha256: "9dd8012572e4aff47516e55f2597998f0a378e3d588d0fad0ca1f11a53ae090c" url: "https://pub.dev" source: hosted - version: "1.3.5" + version: "1.3.6" media_kit_libs_linux: dependency: "direct main" description: @@ -777,18 +785,18 @@ packages: dependency: "direct main" description: name: media_kit_video - sha256: b1a427f0540c5f052dfab73e4b76a5eb8efa7ebb5d83179cb23fc3932afc315a + sha256: c048d11a19e379aebbe810647636e3fc6d18374637e2ae12def4ff8a4b99a882 url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.4" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" mime: dependency: transitive description: @@ -801,26 +809,18 @@ packages: dependency: transitive description: name: mocktail - sha256: "80a996cd9a69284b3dc521ce185ffe9150cde69767c2d3a0720147d93c0cef53" + sha256: bac151b31e4ed78bd59ab89aa4c0928f297b1180186d5daf03734519e5f596c1 url: "https://pub.dev" source: hosted - version: "0.3.0" - node_preamble: - dependency: transitive - description: - name: node_preamble - sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" - url: "https://pub.dev" - source: hosted - version: "2.0.2" + version: "1.0.1" octo_image: dependency: transitive description: name: octo_image - sha256: "107f3ed1330006a3bea63615e81cf637433f5135a52466c7caa0e7152bca9143" + sha256: "45b40f99622f11901238e18d48f5f12ea36426d8eced9f4cbf58479c7aa2430d" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "2.0.0" package_config: dependency: transitive description: @@ -833,10 +833,10 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: "6ff267fcd9d48cb61c8df74a82680e8b82e940231bb5f68356672fde0397334a" + sha256: "88bc797f44a94814f2213db1c9bd5badebafdfb8290ca9f78d4b9ee2a3db4d79" url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "5.0.1" package_info_plus_platform_interface: dependency: transitive description: @@ -889,10 +889,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" + sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.1" path_provider_foundation: dependency: transitive description: @@ -937,18 +937,18 @@ packages: dependency: transitive description: name: platform - sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 + sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59" url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.3" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.7" pointycastle: dependency: transitive description: @@ -1081,10 +1081,10 @@ packages: dependency: transitive description: name: scroll_pos - sha256: "4246bff3afc779d87cdf650a67d42d67ae71b23ff020d14592e6b89e28a7f9cc" + sha256: cebf602b2dd939de6832bb902ffefb574608d1b84f420b82b381a4007d3c1e1b url: "https://pub.dev" source: hosted - version: "0.4.0" + version: "0.5.0" scrollable_positioned_list: dependency: "direct main" description: @@ -1101,22 +1101,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.1" - shelf_packages_handler: - dependency: transitive - description: - name: shelf_packages_handler - sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" - url: "https://pub.dev" - source: hosted - version: "3.0.2" - shelf_static: - dependency: transitive - description: - name: shelf_static - sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e - url: "https://pub.dev" - source: hosted - version: "1.1.2" shelf_web_socket: dependency: transitive description: @@ -1146,22 +1130,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.4" - source_map_stack_trace: - dependency: transitive - description: - name: source_map_stack_trace - sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - source_maps: - dependency: transitive - description: - name: source_maps - sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" - url: "https://pub.dev" - source: hosted - version: "0.10.12" source_span: dependency: transitive description: @@ -1170,6 +1138,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" sqflite: dependency: transitive description: @@ -1182,26 +1158,26 @@ packages: dependency: transitive description: name: sqflite_common - sha256: "1b92f368f44b0dee2425bb861cfa17b6f6cf3961f762ff6f941d20b33355660a" + sha256: bb4738f15b23352822f4c42a531677e5c6f522e079461fd240ead29d8d8a54a6 url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.5.0+2" stack_trace: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -1242,30 +1218,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" - test: - dependency: transitive - description: - name: test - sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46" - url: "https://pub.dev" - source: hosted - version: "1.24.3" test_api: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" - url: "https://pub.dev" - source: hosted - version: "0.6.0" - test_core: - dependency: transitive - description: - name: test_core - sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.5.3" + version: "0.6.1" time: dependency: transitive description: @@ -1342,74 +1302,74 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27" + sha256: b1c9e98774adf8820c96fbc7ae3601231d324a7d5ebd8babe27b6dfac91357ba url: "https://pub.dev" source: hosted - version: "6.1.14" + version: "6.2.1" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: b04af59516ab45762b2ca6da40fa830d72d0f6045cd97744450b73493fa76330 + sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def" url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "6.2.0" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "7c65021d5dee51813d652357bc65b8dd4a6177082a9966bc8ba6ee477baa795f" + sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3 url: "https://pub.dev" source: hosted - version: "6.1.5" + version: "6.2.1" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: b651aad005e0cb06a01dbd84b428a301916dc75f0e7ea6165f80057fee2d8e8e + sha256: "9f2d390e096fdbe1e6e6256f97851e51afc2d9c423d3432f1d6a02a8a9a8b9fd" url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.1.0" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: b55486791f666e62e0e8ff825e58a023fd6b1f71c49926483f1128d3bbd8fe88 + sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 url: "https://pub.dev" source: hosted - version: "3.0.7" + version: "3.1.0" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: "95465b39f83bfe95fcb9d174829d6476216f2d548b79c38ab2506e0458787618" + sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50" url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.2.0" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "2942294a500b4fa0b918685aff406773ba0a4cd34b7f42198742a94083020ce5" + sha256: "138bd45b3a456dcfafc46d1a146787424f8d2edfbf2809c9324361e58f851cf7" url: "https://pub.dev" source: hosted - version: "2.0.20" + version: "2.2.1" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "95fef3129dc7cfaba2bc3d5ba2e16063bb561fc6d78e63eee16162bc70029069" + sha256: "7754a1ad30ee896b265f8d14078b0513a4dba28d358eabb9d5f339886f4a1adc" url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.1.0" uuid: dependency: transitive description: name: uuid - sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" + sha256: df5a4d8f22ee4ccd77f8839ac7cb274ebc11ef9adcce8b92be14b797fe889921 url: "https://pub.dev" source: hosted - version: "3.0.7" + version: "4.2.1" vector_math: dependency: transitive description: @@ -1418,14 +1378,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 - url: "https://pub.dev" - source: hosted - version: "11.10.0" volume_controller: dependency: transitive description: @@ -1438,10 +1390,10 @@ packages: dependency: transitive description: name: wakelock_plus - sha256: aac3f3258f01781ec9212df94eecef1eb9ba9350e106728def405baa096ba413 + sha256: f268ca2116db22e57577fb99d52515a24bdc1d570f12ac18bb762361d43b043d url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.4" wakelock_plus_platform_interface: dependency: transitive description: @@ -1462,10 +1414,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" web_socket_channel: dependency: transitive description: @@ -1474,22 +1426,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.0" - webkit_inspection_protocol: - dependency: transitive - description: - name: webkit_inspection_protocol - sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" - url: "https://pub.dev" - source: hosted - version: "1.2.1" win32: dependency: transitive description: name: win32 - sha256: c97defd418eef4ec88c0d1652cdce84b9f7b63dd7198e266d06ac1710d527067 + sha256: "7c99c0e1e2fa190b48d25c81ca5e42036d5cac81430ef249027d97b0935c553f" url: "https://pub.dev" source: hosted - version: "5.0.8" + version: "5.1.0" win32_registry: dependency: transitive description: @@ -1526,10 +1470,10 @@ packages: dependency: transitive description: name: xml2json - sha256: c8cb35b83cce879c2ea86951fd257f4e765b0030a0298b35cf94f2b3d0f32095 + sha256: ffb6dc0132a59b2d10d1716aea34ff53fb0e5b73f46a347d302a1a993ea57675 url: "https://pub.dev" source: hosted - version: "5.3.6" + version: "6.2.1" xpath_selector: dependency: transitive description: @@ -1563,5 +1507,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.1.0 <4.0.0" - flutter: ">=3.13.0" + dart: ">=3.2.0 <4.0.0" + flutter: ">=3.16.0" diff --git a/pubspec.yaml b/pubspec.yaml index 2ed5dcbd..1f8344f2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -18,11 +18,11 @@ dependencies: flutter_animate: ^4.1.1+1 flutter_code_editor: ^0.3.0 flutter_highlight: ^0.7.0 - flutter_i18n: ^0.33.0 - flutter_js: ^0.7.2 + flutter_i18n: ^0.34.0 + flutter_js: ^0.8.0 flutter_markdown: ^0.6.17+1 get: ^4.6.5 - go_router: ^11.1.2 + go_router: ^12.1.1 highlight: ^0.7.0 hive: ^2.2.3 hive_flutter: ^1.1.0 @@ -36,11 +36,11 @@ dependencies: media_kit_libs_windows_video: ^1.0.9 media_kit_native_event_loop: ^1.0.8 media_kit_video: ^1.2.1 - package_info_plus: ^4.1.0 + package_info_plus: ^5.0.1 palette_generator: ^0.3.3+2 path: ^1.8.3 path_provider: ^2.1.0 - file_picker: ^5.3.3 + file_picker: ^6.1.1 screenshot: ^2.1.0 scrollable_positioned_list: ^0.3.8 tmdb_api: ^2.1.5 @@ -55,12 +55,12 @@ dependencies: device_info_plus: ^9.0.3 android_intent_plus: ^4.0.2 crypto: ^3.0.3 - + extended_image: ^8.2.0 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.2 + flutter_lints: ^3.0.1 isar_generator: ^3.1.0+1 build_runner: ^2.4.5 json_serializable: ^6.7.0 diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt index 930d2071..903f4899 100644 --- a/windows/flutter/CMakeLists.txt +++ b/windows/flutter/CMakeLists.txt @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -92,7 +97,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS From 27b20df1b911d3256574b5a0cc3e01fd409ef7a3 Mon Sep 17 00:00:00 2001 From: appdevelpo <56633229+appdevelpo@users.noreply.github.com> Date: Sat, 2 Dec 2023 01:12:58 +0800 Subject: [PATCH 04/23] reverse button add reverse button for android --- lib/views/widgets/detail/detail_episodes.dart | 62 ++++++++++++------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/lib/views/widgets/detail/detail_episodes.dart b/lib/views/widgets/detail/detail_episodes.dart index 4077b77d..dfe603bf 100644 --- a/lib/views/widgets/detail/detail_episodes.dart +++ b/lib/views/widgets/detail/detail_episodes.dart @@ -27,35 +27,50 @@ class _DetailEpisodesState extends State { List>? dropdownItems; late List episodes = []; late String listMode = MiruStorage.getSetting(SettingKey.listMode); - + bool isRevered = false; Widget _buildAndroidEpisodes(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // select 选择框 if (episodes.isNotEmpty) - Container( - margin: const EdgeInsets.only(left: 8, top: 5, right: 8), - padding: - const EdgeInsets.only(left: 20, right: 20, top: 5, bottom: 5), - decoration: BoxDecoration( - // 背景颜色为 primaryContainer - color: Theme.of(context).colorScheme.primaryContainer, - borderRadius: const BorderRadius.all(Radius.circular(10))), - child: DropdownButton( - // 内容为 primary 颜色 - style: TextStyle(color: Theme.of(context).colorScheme.primary), - isExpanded: true, - underline: const SizedBox(), - value: c.selectEpGroup.value, - items: dropdownItems, - onChanged: (value) { - setState(() { - c.selectEpGroup.value = value!; - }); - }, - ), - ), + SizedBox( + child: Row(children: [ + IconButton( + onPressed: () { + setState(() { + isRevered = !isRevered; + }); + }, + icon: isRevered + ? const Icon(Icons.keyboard_double_arrow_up_rounded) + : const Icon(Icons.keyboard_double_arrow_down_rounded)), + Expanded( + child: Container( + margin: const EdgeInsets.only(left: 8, top: 5, right: 8), + padding: const EdgeInsets.only( + left: 20, right: 20, top: 5, bottom: 5), + decoration: BoxDecoration( + // 背景颜色为 primaryContainer + color: Theme.of(context).colorScheme.primaryContainer, + borderRadius: + const BorderRadius.all(Radius.circular(10))), + child: Expanded( + child: DropdownButton( + // 内容为 primary 颜色 + style: TextStyle( + color: Theme.of(context).colorScheme.primary), + isExpanded: true, + underline: const SizedBox(), + value: c.selectEpGroup.value, + items: dropdownItems, + onChanged: (value) { + setState(() { + c.selectEpGroup.value = value!; + }); + }, + )))) + ])), if (episodes.isNotEmpty) Container( margin: const EdgeInsets.only(left: 16, top: 10, bottom: 10), @@ -75,6 +90,7 @@ class _DetailEpisodesState extends State { ), Expanded( child: ListView.builder( + reverse: isRevered, padding: const EdgeInsets.all(0), itemCount: episodes.isEmpty ? 0 From 13286ee560559035cfbd65d739a18226742878ff Mon Sep 17 00:00:00 2001 From: appdevelpo <56633229+appdevelpo@users.noreply.github.com> Date: Sat, 2 Dec 2023 01:33:43 +0800 Subject: [PATCH 05/23] reverse in detail add reverse button in detail for desktop --- lib/views/widgets/detail/detail_episodes.dart | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/lib/views/widgets/detail/detail_episodes.dart b/lib/views/widgets/detail/detail_episodes.dart index dfe603bf..e4496c66 100644 --- a/lib/views/widgets/detail/detail_episodes.dart +++ b/lib/views/widgets/detail/detail_episodes.dart @@ -125,19 +125,32 @@ class _DetailEpisodesState extends State { Widget cardTile(Widget child) { return CardTile( title: episodesString, - leading: fluent.IconButton( - icon: Icon( - listMode == "grid" - ? fluent.FluentIcons.view_list - : fluent.FluentIcons.grid_view_medium, + leading: Row(children: [ + fluent.IconButton( + icon: Icon( + listMode == "grid" + ? fluent.FluentIcons.view_list + : fluent.FluentIcons.grid_view_medium, + ), + onPressed: () { + setState(() { + listMode == "grid" ? listMode = "list" : listMode = "grid"; + MiruStorage.setSetting(SettingKey.listMode, listMode); + }); + }, ), - onPressed: () { - setState(() { - listMode == "grid" ? listMode = "list" : listMode = "grid"; - MiruStorage.setSetting(SettingKey.listMode, listMode); - }); - }, - ), + fluent.IconButton( + icon: isRevered + ? const Icon(fluent.FluentIcons.sort_lines_ascending) + : const Icon(fluent.FluentIcons.sort_lines), + onPressed: () { + setState(() { + isRevered = !isRevered; + // MiruStorage.setSetting(SettingKey.listMode, listMode); + }); + }, + ) + ]), trailing: Row( children: [ const DetailContinuePlay(), @@ -167,6 +180,7 @@ class _DetailEpisodesState extends State { LayoutBuilder( builder: (context, constraints) { return GridView.builder( + reverse: isRevered, shrinkWrap: true, itemCount: episodes.isEmpty ? 0 @@ -201,6 +215,7 @@ class _DetailEpisodesState extends State { return cardTile( ListView.builder( shrinkWrap: true, + reverse: isRevered, padding: const EdgeInsets.all(0), itemCount: episodes.isEmpty ? 0 : episodes[c.selectEpGroup.value].urls.length, From f4850b49105768e7beee7a823b5c562dc92515b6 Mon Sep 17 00:00:00 2001 From: appdevelpo <56633229+appdevelpo@users.noreply.github.com> Date: Sat, 2 Dec 2023 02:21:33 +0800 Subject: [PATCH 06/23] bug fix --- lib/views/widgets/detail/detail_episodes.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/views/widgets/detail/detail_episodes.dart b/lib/views/widgets/detail/detail_episodes.dart index e4496c66..4a4bf6ca 100644 --- a/lib/views/widgets/detail/detail_episodes.dart +++ b/lib/views/widgets/detail/detail_episodes.dart @@ -46,6 +46,7 @@ class _DetailEpisodesState extends State { ? const Icon(Icons.keyboard_double_arrow_up_rounded) : const Icon(Icons.keyboard_double_arrow_down_rounded)), Expanded( + flex: 1, child: Container( margin: const EdgeInsets.only(left: 8, top: 5, right: 8), padding: const EdgeInsets.only( @@ -55,8 +56,7 @@ class _DetailEpisodesState extends State { color: Theme.of(context).colorScheme.primaryContainer, borderRadius: const BorderRadius.all(Radius.circular(10))), - child: Expanded( - child: DropdownButton( + child: DropdownButton( // 内容为 primary 颜色 style: TextStyle( color: Theme.of(context).colorScheme.primary), @@ -69,7 +69,7 @@ class _DetailEpisodesState extends State { c.selectEpGroup.value = value!; }); }, - )))) + ))) ])), if (episodes.isNotEmpty) Container( From 4b6b41a3f3a4499bd7d323460793a8d7d397f7ea Mon Sep 17 00:00:00 2001 From: appdevelpo <56633229+appdevelpo@users.noreply.github.com> Date: Sat, 2 Dec 2023 16:46:03 +0800 Subject: [PATCH 07/23] update adding configurable skipping button :I,J,left arrow,right arrow migrate flutter version 3.13.0->3.16.1 stable --- .github/workflows/prbuild.yml | 4 +- assets/i18n/en.json | 3 +- lib/controllers/watch/video_controller.dart | 26 +++-- lib/utils/miru_storage.dart | 8 ++ lib/views/pages/settings_page.dart | 95 +++++++++++++++---- .../widgets/settings_numberbox_button.dart | 75 +++++++++++++++ 6 files changed, 183 insertions(+), 28 deletions(-) create mode 100644 lib/views/widgets/settings_numberbox_button.dart diff --git a/.github/workflows/prbuild.yml b/.github/workflows/prbuild.yml index e1208a0d..cf066329 100644 --- a/.github/workflows/prbuild.yml +++ b/.github/workflows/prbuild.yml @@ -23,7 +23,7 @@ jobs: - name: Flutter action uses: subosito/flutter-action@v2 with: - flutter-version: 3.13.0 + flutter-version: 3.16.1 channel: stable - name: Decode keystore run: | @@ -64,7 +64,7 @@ jobs: - uses: actions/checkout@v3 - uses: subosito/flutter-action@v2 with: - flutter-version: 3.13.0 + flutter-version: 3.16.1 channel: stable - name: Install project dependencies run: flutter pub get diff --git a/assets/i18n/en.json b/assets/i18n/en.json index 48226f16..8cce097a 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -100,8 +100,7 @@ "extension-log": "Extension Log Window", "extension-log-subtitle": "Used for debugging extensions", "skip-interval": "Skip Interval", - "skip-interval-subtitle-desktop":"press key I to seek ahead and key J to seek behind configured interval (miliseconds)", - "skip-interval-subtitle-mobile":"double tap right side of the player to seek ahead and double tap left side of the player to seek behind configured interval(miliseconds)", + "skip-interval-subtitle": "Skipping interval for builtin video player", "about": "About", "official-site": "Official Website", "official-site-training": "Visit", diff --git a/lib/controllers/watch/video_controller.dart b/lib/controllers/watch/video_controller.dart index b0f26a72..1c39fec5 100644 --- a/lib/controllers/watch/video_controller.dart +++ b/lib/controllers/watch/video_controller.dart @@ -190,8 +190,6 @@ class VideoPlayerController extends GetxController { }); super.onInit(); - final skipInterval = - int.parse(MiruStorage.getSetting(SettingKey.skipInterval)); keyboardShortcuts.addAll({ const SingleActivator(LogicalKeyboardKey.mediaPlay): () => player.play(), const SingleActivator(LogicalKeyboardKey.mediaPause): () => @@ -205,21 +203,33 @@ class VideoPlayerController extends GetxController { const SingleActivator(LogicalKeyboardKey.space): () => player.playOrPause(), const SingleActivator(LogicalKeyboardKey.keyJ): () { - final rate = - player.state.position - Duration(milliseconds: skipInterval); + final rate = player.state.position + + Duration( + milliseconds: + (MiruStorage.getSetting(SettingKey.keyJ) * 1000).toInt()); player.seek(rate); }, const SingleActivator(LogicalKeyboardKey.keyI): () { - final rate = - player.state.position + Duration(milliseconds: skipInterval); + final rate = player.state.position + + Duration( + milliseconds: + (MiruStorage.getSetting(SettingKey.keyI) * 1000).toInt()); player.seek(rate); }, const SingleActivator(LogicalKeyboardKey.arrowLeft): () { - final rate = player.state.position - const Duration(seconds: 2); + final rate = player.state.position + + Duration( + milliseconds: + (MiruStorage.getSetting(SettingKey.arrowLeft) * 1000) + .toInt()); player.seek(rate); }, const SingleActivator(LogicalKeyboardKey.arrowRight): () { - final rate = player.state.position + const Duration(seconds: 2); + final rate = player.state.position + + Duration( + milliseconds: + (MiruStorage.getSetting(SettingKey.arrowRight) * 1000) + .toInt()); player.seek(rate); }, const SingleActivator(LogicalKeyboardKey.arrowUp): () { diff --git a/lib/utils/miru_storage.dart b/lib/utils/miru_storage.dart index 2a354cee..5a1a2ee1 100644 --- a/lib/utils/miru_storage.dart +++ b/lib/utils/miru_storage.dart @@ -114,6 +114,10 @@ class MiruStorage { await _initSetting(SettingKey.videoPlayer, 'built-in'); await _initSetting(SettingKey.listMode, "grid"); await _initSetting(SettingKey.skipInterval, "10000"); + await _initSetting(SettingKey.keyI, 10.0); + await _initSetting(SettingKey.keyJ, -10.0); + await _initSetting(SettingKey.arrowLeft, -2.0); + await _initSetting(SettingKey.arrowRight, 2.0); } static _initSetting(String key, dynamic value) async { @@ -143,4 +147,8 @@ class SettingKey { static String databaseVersion = 'DatabaseVersion'; static String listMode = 'ListMode'; static String skipInterval = 'SkipInterval'; + static String keyI = 'KeyI'; + static String keyJ = 'KeyJ'; + static String arrowLeft = 'arrowleft'; + static String arrowRight = 'arrowright'; } diff --git a/lib/views/pages/settings_page.dart b/lib/views/pages/settings_page.dart index 29ccd026..0b3c50fe 100644 --- a/lib/views/pages/settings_page.dart +++ b/lib/views/pages/settings_page.dart @@ -12,6 +12,7 @@ import 'package:miru_app/controllers/settings_controller.dart'; import 'package:miru_app/views/widgets/settings_input_tile.dart'; import 'package:miru_app/views/widgets/settings_radios_tile.dart'; import 'package:miru_app/views/widgets/settings_switch_tile.dart'; +import 'package:miru_app/views/widgets/settings_numberbox_button.dart'; import 'package:miru_app/views/widgets/settings_tile.dart'; import 'package:miru_app/utils/i18n.dart'; import 'package:miru_app/utils/miru_storage.dart'; @@ -296,23 +297,85 @@ class _SettingsPageState extends State { ), const SizedBox(height: 8), if (!Platform.isAndroid) - SettingsIntpuTile( - icon: const PlatformWidget( - androidWidget: Icon(Icons.fast_forward_rounded), - desktopWidget: Icon(fluent.FluentIcons.fast_forward, size: 24), + fluent.Expander( + header: Row( + children: [ + Icon(fluent.FluentIcons.keyboard_classic, size: 24), + const SizedBox(width: 16), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("settings.skip-interval".i18n), + const SizedBox(height: 2), + Text( + "settings.skip-interval-subtitle".i18n, + style: const TextStyle(fontSize: 12), + ) + ], + ), + const Spacer(), + const SizedBox(), + ], + ), + content: Column( + children: [ + Row(children: [ + Expanded( + child: SettingNumboxButton( + title: "key I", + button1text: "1s", + button2text: "0.1s", + onChanged: (value) { + MiruStorage.setSetting(SettingKey.keyI, value ??= -10.0); + }, + numberBoxvalue: + MiruStorage.getSetting(SettingKey.keyI) ?? -10.0, + )), + const SizedBox(width: 30), + Expanded( + child: SettingNumboxButton( + title: "key J", + button1text: "1s", + button2text: "0.1s", + onChanged: (value) { + MiruStorage.setSetting(SettingKey.keyJ, value ??= 10.0); + }, + numberBoxvalue: + MiruStorage.getSetting(SettingKey.keyJ) ?? 10.0, + )) + ]), + const SizedBox(height: 8), + Row(children: [ + Expanded( + child: SettingNumboxButton( + title: "arrow left", + icon: const Icon(fluent.FluentIcons.chevron_left_med), + button1text: "1s", + button2text: "0.1s", + numberBoxvalue: + MiruStorage.getSetting(SettingKey.arrowLeft) ?? 10.0, + onChanged: (value) { + MiruStorage.setSetting( + SettingKey.arrowLeft, value ??= -2.0); + }, + )), + const SizedBox(width: 30), + Expanded( + child: SettingNumboxButton( + title: "arrow right", + icon: const Icon(fluent.FluentIcons.chevron_right_med), + button1text: "1s", + button2text: "0.1s", + onChanged: (value) { + MiruStorage.setSetting( + SettingKey.arrowRight, value ??= 2); + }, + numberBoxvalue: + MiruStorage.getSetting(SettingKey.arrowRight) ?? 10.0, + )) + ]) + ], ), - title: 'settings.skip-interval'.i18n, - buildSubtitle: () { - if (!Platform.isAndroid) { - return 'settings.skip-interval-subtitle-desktop'.i18n; - } - return 'settings.skip-interval-subtitle-mobile'.i18n; - }, - onChanged: (value) { - MiruStorage.setSetting(SettingKey.skipInterval, value); - Get.find().onRefresh(); - }, - text: MiruStorage.getSetting(SettingKey.skipInterval), ), const SizedBox(height: 8), ListTitle(title: 'settings.about'.i18n), diff --git a/lib/views/widgets/settings_numberbox_button.dart b/lib/views/widgets/settings_numberbox_button.dart new file mode 100644 index 00000000..1dadd812 --- /dev/null +++ b/lib/views/widgets/settings_numberbox_button.dart @@ -0,0 +1,75 @@ +import 'package:fluent_ui/fluent_ui.dart' as fluent; +import 'package:flutter/material.dart'; +import 'package:miru_app/utils/i18n.dart'; +// import 'package:miru_app/views/widgets/platform_widget.dart'; +import 'package:miru_app/views/widgets/settings_tile.dart'; + +class SettingNumboxButton extends fluent.StatefulWidget { + const SettingNumboxButton({ + Key? key, + this.icon, + required this.title, + required this.onChanged, + required this.button1text, + required this.button2text, + required this.numberBoxvalue, + }) : super(key: key); + final Widget? icon; + final String title; + final String button1text; + final String button2text; + final void Function(double?)? onChanged; + final double numberBoxvalue; + @override + fluent.State createState() => _SettingsIntpuTileState(); +} + +class _SettingsIntpuTileState extends fluent.State { + bool buttonSwitch = false; + Widget _buildDesktop(BuildContext context) { + return fluent.Tooltip( + message: widget.title, + child: fluent.Card( + child: Row( + children: [ + SizedBox( + width: 60, + height: 24, + child: widget.icon ?? Text(widget.title), + ), + Expanded( + child: SizedBox( + child: fluent.NumberBox( + value: widget.numberBoxvalue, + onChanged: widget.onChanged, + smallChange: buttonSwitch ? 0.1 : 1.0, + mode: fluent.SpinButtonPlacementMode.inline, + ), + )), + if (!buttonSwitch) + fluent.Button( + child: Text(widget.button1text), + onPressed: () { + setState(() { + buttonSwitch = true; + }); + }, + ) + else + fluent.FilledButton( + child: Text(widget.button2text), + onPressed: () { + setState(() { + buttonSwitch = false; + }); + }, + ) + ], + ))); + } + + @override + Widget build(BuildContext context) { + return _buildDesktop(context); + } +} From 905aef97b132765a7761b033fd79ed116af061a2 Mon Sep 17 00:00:00 2001 From: appdevelpo <56633229+appdevelpo@users.noreply.github.com> Date: Sat, 2 Dec 2023 17:25:24 +0800 Subject: [PATCH 08/23] Update CMakeLists.txt trying rollback cmake file --- windows/flutter/CMakeLists.txt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt index 903f4899..16a40eba 100644 --- a/windows/flutter/CMakeLists.txt +++ b/windows/flutter/CMakeLists.txt @@ -10,10 +10,10 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") -# Set fallback configurations for older versions of the flutter tool. -if (NOT DEFINED FLUTTER_TARGET_PLATFORM) - set(FLUTTER_TARGET_PLATFORM "windows-x64") -endif() +# # Set fallback configurations for older versions of the flutter tool. +# if (NOT DEFINED FLUTTER_TARGET_PLATFORM) +# set(FLUTTER_TARGET_PLATFORM "windows-x64") +# endif() # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -97,7 +97,8 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - ${FLUTTER_TARGET_PLATFORM} $ + # ${FLUTTER_TARGET_PLATFORM} $ + windows-x64 $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS From d9e4f87aa77a9a09c2c7ee437be034e4db8ea3c8 Mon Sep 17 00:00:00 2001 From: appdevelpo <56633229+appdevelpo@users.noreply.github.com> Date: Sat, 2 Dec 2023 17:47:16 +0800 Subject: [PATCH 09/23] Update prbuild.yml --- .github/workflows/prbuild.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prbuild.yml b/.github/workflows/prbuild.yml index cf066329..c79f02ba 100644 --- a/.github/workflows/prbuild.yml +++ b/.github/workflows/prbuild.yml @@ -72,7 +72,7 @@ jobs: run: flutter build windows --release - name: Rename Release Directory Name to Miru-App # 为了解压缩后更好看一点 run: | - mv build/windows/runner/Release build/windows/runner/Miru-App + mv build/windows/x64/runner/Release build/windows/x64/runner/Miru-App # 发布安装包 - name: Upload Artifact uses: actions/upload-artifact@v3 From f673f834daed2b5108686387b69e514a3d2705dc Mon Sep 17 00:00:00 2001 From: appdevelpo <56633229+appdevelpo@users.noreply.github.com> Date: Sat, 2 Dec 2023 18:59:48 +0800 Subject: [PATCH 10/23] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=B9=81=E9=AB=94?= =?UTF-8?q?=E4=B8=AD=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/i18n/en.json | 1 + assets/i18n/zhHant.json | 200 +++++++++++++++++++++++++++++ lib/views/pages/settings_page.dart | 1 + 3 files changed, 202 insertions(+) create mode 100644 assets/i18n/zhHant.json diff --git a/assets/i18n/en.json b/assets/i18n/en.json index 8cce097a..f8613b45 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -8,6 +8,7 @@ "ryu": "うちなーぐち", "uk": "Українська", "zh": "中文", + "zhHant": "繁體中文", "hi": "हिंदी" }, diff --git a/assets/i18n/zhHant.json b/assets/i18n/zhHant.json new file mode 100644 index 00000000..6f76ef79 --- /dev/null +++ b/assets/i18n/zhHant.json @@ -0,0 +1,200 @@ +{ + "common": { + "home": "首頁", + "search": "搜尋", + "extension": "擴充套件", + "extension-repo": "擴充套件庫", + "settings": "設定", + "no-extension": "未安装任何擴充套件", + "no-result": "未找到相關结果", + "no-more-data": "没有更多數據了", + "cancel": "取消", + "confirm": "確定", + "close": "關閉", + "copied": "已複製到剪貼簿", + "uninstall": "解除安裝", + "install": "安装", + "repo": "倉庫", + "unset": "未設定", + "extension-missing": "未找到擴充套件 {package} ", + "error": "錯誤", + "retry": "重試", + "next": "下一個", + "previous": "上一個", + "show-all": "顯示全部", + "delete": "删除", + "delete-all": "删除全部" + }, + + "home": { + "continue-watching": "繼續觀看", + "favorite": "收藏", + "no-record": "暫無收藏和觀看紀錄", + "watched": "看到 {ep}" + }, + + "search": { + "hint-text": "請善用搜索哦!~", + "all": "全部", + "filter": "篩選" + }, + + "extension": { + "import": { + "title": "導入擴充套件", + "url-label": "擴充套件地址", + "tips": "你可以通過連結導入擴充套件,或者點擊下方的目錄,將擴充套件文件放入其中。", + "extension-dir": "擴充套件目錄", + "import-by-url": "通過連結導入" + }, + "error-dialog": "錯誤信息", + "installed": "已安装", + "edit-code": "編輯程式" + }, + + "extension-repo": { + "error": "發生錯誤!", + "error-tips": "請檢查網絡連線或者擴充套件庫地址是否正確", + "empty": "擴充套件庫為空", + "upgrade": "更新" + }, + + "settings": { + "repo-url": "擴充套件庫地址", + "repo-url-subtitle": "獲取擴充套件的仓库地址", + "tmdb-key": "TMDB API Key", + "tmdb-key-subtitle": "在這裡輸入您的 TMDB API Key", + "bt-server": "BT Server", + "bt-server-subtitle": "線上播放 BT 種子的必要套件", + "bt-server-manager": "管理", + "upgrade": "更新軟體", + "upgrade-subtitle": "現在版本:{version}", + "upgrade-training": "檢查更新", + "auto-check-update": "自動檢查更新", + "auto-check-update-subtitle": "每次啟動時檢查更新", + "language": "語言", + "theme": "主題", + "theme-subtitle": "選擇螢幕主題", + "theme-system": "跟隨系统", + "theme-light": "浅色", + "theme-dark": "深色", + "theme-black": "黑色", + "nsfw": "NSFW", + "nsfw-subtitle": "顯示成人内容", + "external-player": "預設播放器", + "external-player-subtitle": "現在使用的是 {player}", + "external-player-builtin": "内建播放器", + "language-subtitle": "選擇軟體的語言", + "extension-log": "擴充套件日誌視窗", + "extension-log-subtitle": "擴充套件除錯視窗", + "about": "關於", + "official-site": "官網", + "official-site-training": "前往官網", + "source-code": "原始碼", + "source-code-training": "前往 Star", + "license": "許可證", + "license-subtitle": "許可證" + }, + + "external-player-launching": "正在啟動 {player}", + + "detail": { + "favorite": "收藏", + "favorited": "已收藏", + "continue-watching": "繼續 {episode}", + "total-episodes": "共 {total}", + "overview": "簡介", + "cast": "演員", + "additional-info": "額外資訊", + "get-lastest-data-error": "無法獲取最新數據", + "modify-tmdb-binding": "修改 TMDB 绑定", + "no-tmdb-data": "未匹配到 TMDB 數據,请自行綁定數據", + "tmdb-key-missing": "TMDB API Key 丢失,請前往設置填寫" + }, + + "video": { + "episodes": "集", + "watch-now": "立即觀看", + "no-episodes": "暫無", + "play-complete": "播放完成", + "resume-last-playback": "恢復上次播放位置", + "subtitle-none": "不使用字幕", + "subtitle": "字幕", + "subtitle-change": "切換字幕 {title}", + "subtitle-file": "選擇字幕文件", + "torrent-downloading": "正在下載種子" + }, + + "reader": { + "chapters": "章", + "read-now": "立即閱讀", + "no-chapters": "暫無章節" + }, + + "comic-settings": { + "read-mode": "漫畫閱讀模式", + "standard": "標準模式", + "right-to-left": "從右到左", + "web-tonn": "Webtoon" + }, + + "novel-settings": { + "font-size": "字體大小" + }, + + "upgrade": { + "check-update": "檢查更新", + "new-version": "偵測到新版本 {version}", + "download": "前往更新", + "no-update": "已经是最新版本", + "not-now": "暫不更新", + "error": "检查更新失敗,請檢查網路狀態" + }, + + "extension-install-error": "擴充套件安裝失敗", + + "extension-type": { + "video": "影片", + "novel": "小說", + "comic": "漫畫" + }, + + "extension-info": { + "author": "作者", + "description": "描述", + "version": "版本", + "language": "語言", + "original-site": "原始網站", + "other-infomation": "其他訊息", + "license": "許可", + "title": "擴充套件詳情" + }, + + "cookie-clean": { + "title": "清除 Cookie", + "subtitle": "清除之後可能要重新登錄", + "success": "清除成功", + "clean": "清除" + }, + + "tmdb": { + "backdrops": "劇照", + "status": "狀態", + "original-title": "原始標題", + "release-date": "發佈日期", + "genres": "類型", + "runtime": "時長", + "languages": "語言" + }, + + "bt-server": { + "not-installed": "BT-Server 尚未安装", + "running": "BT-Server 正在執行", + "stopped": "BT-Server 已停止", + "version": "版本 {version}", + "remote-version": "遠程版本 {version}", + "stop": "停止", + "upgrade": "升级", + "start": "啟動" + } +} diff --git a/lib/views/pages/settings_page.dart b/lib/views/pages/settings_page.dart index 0b3c50fe..9b81aca6 100644 --- a/lib/views/pages/settings_page.dart +++ b/lib/views/pages/settings_page.dart @@ -193,6 +193,7 @@ class _SettingsPageState extends State { 'languages.ru'.i18n: 'ru', 'languages.uk'.i18n: 'uk', 'languages.hi'.i18n: 'hi', + 'languages.zhHant'.i18n: 'zhHant', }, buildSubtitle: () => 'settings.language-subtitle'.i18n, applyValue: (value) { From b6610edfa4266b7aebf5d38a3885a07f814274e1 Mon Sep 17 00:00:00 2001 From: appdevelpo <56633229+appdevelpo@users.noreply.github.com> Date: Sat, 2 Dec 2023 22:14:18 +0800 Subject: [PATCH 11/23] default reading mode add default reading mode ,prbuild --- .github/workflows/prbuild.yml | 2 +- assets/i18n/en.json | 1 + lib/controllers/watch/comic_controller.dart | 23 ++++++++++++------ lib/data/services/database_service.dart | 5 ++-- lib/utils/miru_storage.dart | 6 +++-- lib/views/pages/settings_page.dart | 27 ++++++++++++++++++++- 6 files changed, 51 insertions(+), 13 deletions(-) diff --git a/.github/workflows/prbuild.yml b/.github/workflows/prbuild.yml index c79f02ba..b2c5ad7f 100644 --- a/.github/workflows/prbuild.yml +++ b/.github/workflows/prbuild.yml @@ -77,5 +77,5 @@ jobs: - name: Upload Artifact uses: actions/upload-artifact@v3 with: - path: "build/windows/runner/Miru-App" + path: "build/windows/x64/runner/Miru-App" name: Miru-pr-${{ github.event.pull_request.number }}-windows.zip diff --git a/assets/i18n/en.json b/assets/i18n/en.json index f8613b45..bb4a6177 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -102,6 +102,7 @@ "extension-log-subtitle": "Used for debugging extensions", "skip-interval": "Skip Interval", "skip-interval-subtitle": "Skipping interval for builtin video player", + "default-reader-mode": "Default reader mode", "about": "About", "official-site": "Official Website", "official-site-training": "Visit", diff --git a/lib/controllers/watch/comic_controller.dart b/lib/controllers/watch/comic_controller.dart index bff80c8f..4da058f9 100644 --- a/lib/controllers/watch/comic_controller.dart +++ b/lib/controllers/watch/comic_controller.dart @@ -6,6 +6,7 @@ import 'package:miru_app/data/services/database_service.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'dart:async'; import 'package:extended_image/extended_image.dart'; +import 'package:miru_app/utils/miru_storage.dart'; class ComicController extends ReaderController { ComicController({ @@ -17,8 +18,15 @@ class ComicController extends ReaderController { required super.runtime, required super.cover, }); - + Map readmode = { + 'standard': MangaReadMode.standard, + 'rightToLeft': MangaReadMode.rightToLeft, + 'webTonn': MangaReadMode.webTonn, + }; + final String setting = MiruStorage.getSetting(SettingKey.readingMode); final readType = MangaReadMode.standard.obs; + + // MangaReadMode // 当前页码 final currentPage = 0.obs; bool timerCancel = false; @@ -100,13 +108,15 @@ class ComicController extends ReaderController { } _initSetting() async { - readType.value = await DatabaseService.getMnagaReaderType(super.detailUrl); + readType.value = readmode[setting] ?? MangaReadMode.standard; + readType.value = await DatabaseService.getMnagaReaderType( + super.detailUrl, readType.value); } - double mapValue(double value) { - double mappedValue = ((value - 0) * (1 - (-1))) / (2.5 - 0) + (-1); - return mappedValue; - } + // double mapValue(double value) { + // double mappedValue = ((value - 0) * (1 - (-1))) / (2.5 - 0) + (-1); + // return mappedValue; + // } _jumpPage(int page) { if (readType.value == MangaReadMode.webTonn) { @@ -186,7 +196,6 @@ class ComicController extends ReaderController { ); } pageController.value.dispose(); - timerCancel = true; super.onClose(); } diff --git a/lib/data/services/database_service.dart b/lib/data/services/database_service.dart index f08b9ab2..c783babe 100644 --- a/lib/data/services/database_service.dart +++ b/lib/data/services/database_service.dart @@ -221,9 +221,10 @@ class DatabaseService { } // 获取漫画阅读模式 - static Future getMnagaReaderType(String url) { + static Future getMnagaReaderType( + String url, MangaReadMode defaultMode) { return db.mangaSettings.filter().urlEqualTo(url).findFirst().then( - (value) => value?.readMode ?? MangaReadMode.standard, + (value) => value?.readMode ?? defaultMode, ); } diff --git a/lib/utils/miru_storage.dart b/lib/utils/miru_storage.dart index 5a1a2ee1..8b393f8d 100644 --- a/lib/utils/miru_storage.dart +++ b/lib/utils/miru_storage.dart @@ -118,6 +118,7 @@ class MiruStorage { await _initSetting(SettingKey.keyJ, -10.0); await _initSetting(SettingKey.arrowLeft, -2.0); await _initSetting(SettingKey.arrowRight, 2.0); + await _initSetting(SettingKey.readingMode, "standard"); } static _initSetting(String key, dynamic value) async { @@ -149,6 +150,7 @@ class SettingKey { static String skipInterval = 'SkipInterval'; static String keyI = 'KeyI'; static String keyJ = 'KeyJ'; - static String arrowLeft = 'arrowleft'; - static String arrowRight = 'arrowright'; + static String arrowLeft = 'Arrowleft'; + static String arrowRight = 'Arrowright'; + static String readingMode = 'ReadingMode'; } diff --git a/lib/views/pages/settings_page.dart b/lib/views/pages/settings_page.dart index 9b81aca6..58423ea3 100644 --- a/lib/views/pages/settings_page.dart +++ b/lib/views/pages/settings_page.dart @@ -246,6 +246,31 @@ class _SettingsPageState extends State { }, ), const SizedBox(height: 8), + SettingsRadiosTile( + icon: const PlatformWidget( + androidWidget: Icon(Icons.chrome_reader_mode), + desktopWidget: + Icon(fluent.FluentIcons.reading_mode_solid, size: 24), + ), + title: 'settings.default-reader-mode'.i18n, + itemNameValue: () { + final map = { + 'comic-settings.standard'.i18n: 'standard', + 'comic-settings.right-to-left'.i18n: 'rightToLeft', + 'comic-settings.web-tonn'.i18n: 'webTonn', + }; + return map; + }(), + buildSubtitle: () => + '${MiruStorage.getSetting(SettingKey.readingMode)}'.i18n, + applyValue: (value) { + MiruStorage.setSetting(SettingKey.readingMode, value); + }, + buildGroupValue: () { + return MiruStorage.getSetting(SettingKey.readingMode); + }, + ), + const SizedBox(height: 8), SettingsRadiosTile( icon: const PlatformWidget( androidWidget: Icon(Icons.play_arrow), @@ -301,7 +326,7 @@ class _SettingsPageState extends State { fluent.Expander( header: Row( children: [ - Icon(fluent.FluentIcons.keyboard_classic, size: 24), + const Icon(fluent.FluentIcons.keyboard_classic, size: 24), const SizedBox(width: 16), Column( crossAxisAlignment: CrossAxisAlignment.start, From 69afabf6149459b048a3f25de2d300bdc410d73f Mon Sep 17 00:00:00 2001 From: appdevelpo <56633229+appdevelpo@users.noreply.github.com> Date: Wed, 6 Dec 2023 19:31:14 +0800 Subject: [PATCH 12/23] add selectable video quality in player add quality options if hls file provided add dependency : flutter_hls_parser for format parsing fix reverse button in android --- lib/controllers/watch/video_controller.dart | 69 +++++++++++-- .../watch/video/video_player_content.dart | 96 ++++++++++++++++++- lib/views/widgets/detail/detail_episodes.dart | 15 ++- pubspec.lock | 8 ++ pubspec.yaml | 2 +- 5 files changed, 178 insertions(+), 12 deletions(-) diff --git a/lib/controllers/watch/video_controller.dart b/lib/controllers/watch/video_controller.dart index 1c39fec5..c16ccf67 100644 --- a/lib/controllers/watch/video_controller.dart +++ b/lib/controllers/watch/video_controller.dart @@ -3,9 +3,10 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'dart:isolate'; import 'package:flutter/material.dart'; -import 'package:dio/dio.dart'; +import 'package:dio/dio.dart' as dio; import 'package:file_picker/file_picker.dart'; import 'package:flutter/services.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; @@ -29,6 +30,7 @@ import 'package:path/path.dart' as path; import 'package:fluent_ui/fluent_ui.dart' as fluent; import 'package:crypto/crypto.dart'; import 'package:miru_app/utils/miru_storage.dart'; +import 'package:flutter_hls_parser/flutter_hls_parser.dart'; class VideoPlayerController extends GetxController { final String title; @@ -56,10 +58,11 @@ class VideoPlayerController extends GetxController { final subtitles = [].obs; final keyboardShortcuts = {}; final selectedSubtitle = 0.obs; - + final currentQality = "null".obs; + final qualityUrls = {}; // 是否已经自动跳转到上次播放进度 bool _isAutoSeekPosition = false; - + Map? videoheaders = {}; final messageQueue = []; final Rx cuurentMessageWidget = Rx(null); @@ -67,10 +70,11 @@ class VideoPlayerController extends GetxController { final speed = 1.0.obs; final torrentMediaFileList = [].obs; - final currentTorrentFile = ''.obs; String _torrenHash = ""; + final ReceivePort qualityRereceivePort = ReceivePort(); + Isolate? qualityReceiver; // 复制当前 context @override @@ -162,12 +166,26 @@ class VideoPlayerController extends GetxController { index.value++; } }); - + //畫質的listener + qualityRereceivePort.listen((message) async { + debugPrint("${message.keys} get"); + final resolution = message['resolution']; + final urls = message['urls']; + qualityUrls.addAll(Map.fromIterables(resolution, urls)); + qualityRereceivePort.close(); + qualityReceiver!.kill(); + }); + //讀取現在的畫質 + player.stream.height.listen((event) async { + final width = player.state.width; + currentQality.value = "${width}x$event"; + }); // 自动恢复上次播放进度 player.stream.duration.listen((event) async { if (_isAutoSeekPosition || event.inSeconds == 0) { return; } + // 获取上次播放进度 final history = await DatabaseService.getHistoryByPackageAndUrl( runtime.extension.package, @@ -254,6 +272,7 @@ class VideoPlayerController extends GetxController { selectedSubtitle.value = -1; final playUrl = playList[index.value].url; final watchData = await runtime.watch(playUrl) as ExtensionBangumiWatch; + videoheaders = watchData.headers; if (watchData.type == ExtensionWatchBangumiType.torrent) { if (Get.find().btServerisRunning.value == false) { @@ -269,7 +288,7 @@ class VideoPlayerController extends GetxController { await MiruDirectory.getCacheDirectory, 'temp.torrent', ); - await Dio().download(watchData.url, torrentFile); + await dio.Dio().download(watchData.url, torrentFile); final file = File(torrentFile); _torrenHash = await BTServerApi.addTorrent(file.readAsBytesSync()); @@ -291,6 +310,26 @@ class VideoPlayerController extends GetxController { } playTorrentFile(torrentMediaFileList.first); } else { + //背景取得畫質 + qualityReceiver = await Isolate.spawn((SendPort sendport) async { + dio.Dio dioReq = dio.Dio(); + try { + dio.Response response = await dioReq.get(watchData.url, + options: dio.Options(headers: watchData.headers)); + debugPrint(response.data); + final playList = await HlsPlaylistParser.create().parseString( + Uri.parse(watchData.url), response.data) as HlsMasterPlaylist; + List urlList = + playList.mediaPlaylistUrls.map((e) => e.toString()).toList(); + final resolution = playList.variants + .map((it) => "${it.format.width}x${it.format.height}"); + debugPrint("get sources"); + sendport.send({'resolution': resolution, 'urls': urlList}); + } catch (error) { + debugPrint('Error: $error'); + } + }, qualityRereceivePort.sendPort); + await player.open(Media(watchData.url, httpHeaders: watchData.headers)); if (watchData.audioTrack != null) { await player.setAudioTrack(AudioTrack.uri(watchData.audioTrack!)); @@ -315,6 +354,7 @@ class VideoPlayerController extends GetxController { await Future.delayed(const Duration(seconds: 3)); play(); + return; } sendMessage( @@ -338,6 +378,23 @@ class VideoPlayerController extends GetxController { isFullScreen.value = !isFullScreen.value; } + switchQuality(String qualityUrl) async { + final currentSecond = player.state.position.inSeconds; + try { + await player.open(Media(qualityUrl, httpHeaders: videoheaders)); + //跳轉到切換之前的時間 + Timer.periodic(const Duration(seconds: 1), (timer) { + player.seek(Duration(seconds: currentSecond)); + if (player.state.position.inSeconds == currentSecond) { + timer.cancel(); + } + }); + } catch (e) { + await Future.delayed(const Duration(seconds: 3)); + player.open(Media(qualityUrl, httpHeaders: videoheaders)); + } + } + onExit() async { if (_torrenHash.isNotEmpty) { BTServerApi.removeTorrent(_torrenHash); diff --git a/lib/views/pages/watch/video/video_player_content.dart b/lib/views/pages/watch/video/video_player_content.dart index a1ad98ea..bb848484 100644 --- a/lib/views/pages/watch/video/video_player_content.dart +++ b/lib/views/pages/watch/video/video_player_content.dart @@ -1,3 +1,4 @@ +import 'package:fluent_ui/fluent_ui.dart' as fluent; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:media_kit_video/media_kit_video.dart'; @@ -21,7 +22,8 @@ class VideoPlayerConten extends StatefulWidget { class _VideoPlayerContenState extends State { late final _c = Get.find(tag: widget.tag); final speeds = [0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0]; - + String? selected; + final menuController = fluent.FlyoutController(); Widget _buildDesktop(BuildContext context) { final topButtonBar = Row( children: [ @@ -196,7 +198,52 @@ class _VideoPlayerContenState extends State { ), ]; }, - ) + ), + TextButton( + onPressed: () { + fluent.showDialog( + context: context, + builder: (contex) { + return fluent.ContentDialog( + title: Text("choose-quality".i18n), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + for (final q in _c.qualityUrls.entries) + Row(children: [ + fluent.RadioButton( + checked: selected == q.key || + q.key == _c.currentQality.value, + onChanged: (checked) { + // debugPrint("$boolean"); + setState(() { + if (checked) { + selected = q.key; + _c.switchQuality( + _c.qualityUrls[q.key]!); + Navigator.pop(context); + } + }); + }), + Text( + q.key, + style: const TextStyle( + fontSize: 20, color: Colors.white), + ), + ]) + ], + ), + actions: [ + fluent.Button( + onPressed: () => Navigator.pop(context), + child: Text("cancel".i18n)) + ], + ); + }); + }, + child: Obx(() => (Text(_c.currentQality.value, + style: const TextStyle(color: Colors.white))))) ], ), ), @@ -280,6 +327,51 @@ class _VideoPlayerContenState extends State { data: Theme.of(context), child: Row( children: [ + SizedBox( + // height: 8, + // width: 10, + child: TextButton( + onPressed: () { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text("choose-quality".i18n), + content: Column( + children: [ + for (final q in _c.qualityUrls.entries) + RadioListTile( + title: Text(q.key), + value: q.value, + groupValue: _c.qualityUrls[ + _c.currentQality.value], + onChanged: (value) { + Navigator.pop(context); + // widget.applyValue(value as T); + debugPrint( + "$value value changed"); + + _c.currentQality.value = _c + .qualityUrls.keys + .firstWhere( + (element) => + _c.qualityUrls[ + element] == + value, + orElse: () => _c + .qualityUrls + .keys + .first); + setState(() {}); + _c.switchQuality(value!); + }, + ), + ], + ), + )); + }, + child: Obx(() => (Text(_c.currentQality.value, + style: const TextStyle(color: Colors.white)))))), + const SizedBox(width: 2), Padding( padding: const EdgeInsets.only(right: 8.0), child: PopupMenuButton( diff --git a/lib/views/widgets/detail/detail_episodes.dart b/lib/views/widgets/detail/detail_episodes.dart index 4a4bf6ca..eb9246d6 100644 --- a/lib/views/widgets/detail/detail_episodes.dart +++ b/lib/views/widgets/detail/detail_episodes.dart @@ -90,19 +90,28 @@ class _DetailEpisodesState extends State { ), Expanded( child: ListView.builder( - reverse: isRevered, padding: const EdgeInsets.all(0), itemCount: episodes.isEmpty ? 0 : episodes[c.selectEpGroup.value].urls.length, itemBuilder: (context, index) { return ListTile( - title: Text(episodes[c.selectEpGroup.value].urls[index].name), + title: isRevered + ? Text(episodes[c.selectEpGroup.value] + .urls[episodes[c.selectEpGroup.value].urls.length - + 1 - + index] + .name) + : Text(episodes[c.selectEpGroup.value].urls[index].name), onTap: () { c.goWatch( context, episodes[c.selectEpGroup.value].urls, - index, + isRevered + ? episodes[c.selectEpGroup.value].urls.length - + 1 - + index + : index, c.selectEpGroup.value, ); }, diff --git a/pubspec.lock b/pubspec.lock index dced9361..9905a1da 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -438,6 +438,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.0" + flutter_hls_parser: + dependency: "direct main" + description: + name: flutter_hls_parser + sha256: f4b7df4f927623aea5c72006232693e07fdd11438f600c08670d6a23c1aa85c8 + url: "https://pub.dev" + source: hosted + version: "2.0.1" flutter_i18n: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 1f8344f2..785bd265 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -56,7 +56,7 @@ dependencies: android_intent_plus: ^4.0.2 crypto: ^3.0.3 extended_image: ^8.2.0 - + flutter_hls_parser: ^2.0.1 dev_dependencies: flutter_test: sdk: flutter From c6c2112524066bd973e6da167efe92dc73359dc8 Mon Sep 17 00:00:00 2001 From: appdevelpo <56633229+appdevelpo@users.noreply.github.com> Date: Thu, 7 Dec 2023 23:22:07 +0800 Subject: [PATCH 13/23] add backup and restore add backup and restore function for android and desktop Need To Restart The App --- assets/i18n/en.json | 16 +- lib/utils/application.dart | 161 ++++++++++++++++++ lib/utils/miru_storage.dart | 5 + lib/views/pages/settings_page.dart | 53 ++++++ macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 26 ++- pubspec.yaml | 3 + 7 files changed, 264 insertions(+), 2 deletions(-) diff --git a/assets/i18n/en.json b/assets/i18n/en.json index bb4a6177..61c84a17 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -109,7 +109,11 @@ "source-code": "Open Source", "source-code-training": "Give Star", "license": "License", - "license-subtitle": "License" + "license-subtitle": "License", + "export-file":"Export", + "import-file":"Import", + "backup":"Backup", + "backup-subtitle":"Backup or restore data from local" }, "external-player-launching": "Launching {player}", @@ -212,5 +216,15 @@ "stop": "Stop", "upgrade": "Upgrade", "start": "Start" + }, + "backup": { + "import-unzip-success" : "recover success :", + "import-unzip-success-sub":"data recover success please restart the app", + "import-unzip-fail" : "recover fail", + "export-success":"export success", + "export-fail":"export fail", + "export-success-sub":"data export success choose the folder", + "import-filePath-failed":"failed at getting file in this path please switch to the another folder", + "select-folder":"select the folder to store save file" } } diff --git a/lib/utils/application.dart b/lib/utils/application.dart index bc11c005..17b7866f 100644 --- a/lib/utils/application.dart +++ b/lib/utils/application.dart @@ -1,10 +1,12 @@ // ignore_for_file: use_build_context_synchronously import 'dart:io'; +import 'package:archive/archive.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; +import 'package:miru_app/utils/miru_storage.dart'; import 'package:get/get.dart'; import 'package:miru_app/utils/i18n.dart'; import 'package:miru_app/utils/router.dart'; @@ -13,6 +15,9 @@ import 'package:miru_app/views/widgets/messenger.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:path_provider/path_provider.dart'; late PackageInfo packageInfo; late AndroidDeviceInfo androidDeviceInfo; @@ -135,4 +140,160 @@ class ApplicationUtils { ); } } + + static Future exportSaveFile( + BuildContext context, + ) async { + final directory = await getApplicationDocumentsDirectory(); + final archive = Archive(); + DateTime now = DateTime.now(); + //android backup function + if (Platform.isAndroid) { + bool result = await archivefiles([ + "${directory.path}/miru/default.isar", + "${directory.path}/miru/settings.hive" + ], "${directory.path}/miru-backup-${now.year}-${now.month}-${now.day}_${now.hour}:${now.minute}.zip", + archive, ZipEncoder()); + if (result) { + return showPlatformSnackbar( + context: context, + title: 'backup.export-success'.i18n, + content: "backup.export-success".i18n, + ); + } + return showPlatformSnackbar( + context: context, + title: 'backup.export-failed'.i18n, + content: "backup.export-failed".i18n, + ); + } + //desktop backup function + else { + String? folderPath = await FilePicker.platform + .getDirectoryPath(dialogTitle: "backup.select-folder".i18n); + if (folderPath == null) { + return showPlatformSnackbar( + context: context, + title: 'backup.import-filePath-failed'.i18n, + content: "backup.import-failed".i18n, + ); + } + bool result = await archivefiles([ + "${directory.path}/miru/default.isar", + "${directory.path}/miru/settings.hive" + ], "$folderPath\\miru-backup-${now.year}-${now.month}-${now.day}_${now.hour}-${now.minute}.zip", + archive, ZipEncoder()); + if (result) { + return showPlatformSnackbar( + context: context, + title: 'backup.export-success'.i18n, + content: "backup.export-success".i18n, + ); + } + return showPlatformSnackbar( + context: context, + title: 'backup.export-failed'.i18n, + content: "backup.export-failed".i18n, + ); + } + } + + static Future importSaveFile( + BuildContext context, + ) async { + final directory = await getApplicationDocumentsDirectory(); + FilePickerResult? folderPath = await FilePicker.platform.pickFiles(); + if (folderPath == null) { + return showPlatformSnackbar( + context: context, + title: 'backup.import-filePath-failed'.i18n, + content: "backup.import-failed".i18n, + ); + } + debugPrint("${folderPath.files.single.path}"); + debugPrint("unZip: $directory"); + //clear database + MiruStorage.deleteAll(); + final unZip = await unarchivefiles( + folderPath.files.single.path!, "${directory.path}/miru", ZipDecoder()); + + //unzip succeeded 解壓成功 + if (unZip == null) { + return showPlatformSnackbar( + context: context, + title: 'backup.import-unzip-success'.i18n, + content: "backup.import-unzip-success-sub".i18n, + ); + } + //unzip failed 解壓失敗 + return showPlatformSnackbar( + context: context, + title: 'backup.import-unzip-failed'.i18n, + content: unZip, + ); + } + + static Future copyFile( + String sourcePath, String destinationPath) async { + File sourceFile = File(sourcePath); + File destinationFile = File(destinationPath); + try { + await sourceFile.copy(destinationFile.path); + return true; + } catch (e) { + return false; + } + } + + static Future unarchivefiles( + String path, String targetPath, dynamic encoder) async { + try { + final bytes = await File(path).readAsBytes(); + final archive = ZipDecoder().decodeBytes(bytes); + for (final file in archive) { + final filename = file.name; + if (file.isFile) { + final data = file.content as List; + debugPrint("$path/$filename"); + File("$targetPath/$filename") + ..createSync(recursive: true) + ..writeAsBytesSync(data); + } else { + Directory('$path/$filename').create(recursive: true); + } + } + return; + } catch (e) { + debugPrint("$e"); + return e; + } + } + + static Future archivefiles(List paths, String targetPath, + Archive archive, dynamic encoder) async { + for (final path in paths) { + final bytes = await File(path).readAsBytes(); + final fileName = File(path).path.split('/').last; + final archiveFile = ArchiveFile(fileName, bytes.length, bytes); + archive.addFile(archiveFile); + debugPrint("${bytes.length}"); + } + final output = encoder.encode(archive); + if (output == null) { + debugPrint("encode failed"); + return false; + } + if (Platform.isAndroid) { + File(targetPath).writeAsBytesSync(output); + await Share.shareXFiles( + [XFile(targetPath)], + ); + File(targetPath).delete(); + return true; + } + File(targetPath).writeAsBytesSync(output); + debugPrint("$targetPath"); + // + return true; + } } diff --git a/lib/utils/miru_storage.dart b/lib/utils/miru_storage.dart index 8b393f8d..53efb287 100644 --- a/lib/utils/miru_storage.dart +++ b/lib/utils/miru_storage.dart @@ -134,6 +134,11 @@ class MiruStorage { static getSetting(String key) { return settings.get(key); } + + static deleteAll() async { + final re = await database.close(deleteFromDisk: true); + debugPrint('Database closed: $re'); + } } class SettingKey { diff --git a/lib/views/pages/settings_page.dart b/lib/views/pages/settings_page.dart index 58423ea3..8d8a7fc3 100644 --- a/lib/views/pages/settings_page.dart +++ b/lib/views/pages/settings_page.dart @@ -163,6 +163,59 @@ class _SettingsPageState extends State { ), ), const SizedBox(height: 8), + SettingsTile( + icon: const PlatformWidget( + androidWidget: Icon(Icons.backup), + desktopWidget: Icon(fluent.FluentIcons.update_restore, size: 24), + ), + title: 'settings.backup'.i18n, + buildSubtitle: () => FlutterI18n.translate( + context, + 'settings.backup-subtitle', + translationParams: { + 'version': packageInfo.version, + }, + ), + trailing: PlatformWidget( + androidWidget: Row( + mainAxisSize: MainAxisSize.min, + children: [ + TextButton( + onPressed: () { + ApplicationUtils.exportSaveFile(context); + }, + child: Text('settings.export-file'.i18n), + ), + TextButton( + onPressed: () { + ApplicationUtils.importSaveFile(context); + }, + child: Text('settings.import-file'.i18n), + ) + ], + ), + desktopWidget: Row(children: [ + fluent.FilledButton( + onPressed: () { + ApplicationUtils.exportSaveFile( + context, + ); + }, + child: Text('settings.export-file'.i18n), + ), + const SizedBox(width: 16), + fluent.Button( + onPressed: () { + ApplicationUtils.importSaveFile( + context, + ); + }, + child: Text('settings.import-file'.i18n), + ) + ]), + ), + ), + const SizedBox(height: 8), SettingsSwitchTile( icon: const PlatformWidget( androidWidget: Icon(Icons.autorenew_sharp), diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index f71d65fb..1249d6d1 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -16,6 +16,7 @@ import package_info_plus import path_provider_foundation import screen_brightness_macos import screen_retriever +import share_plus import sqflite import url_launcher_macos import wakelock_plus @@ -33,6 +34,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin")) ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) + SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 9905a1da..92ab7f98 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -26,7 +26,7 @@ packages: source: hosted version: "4.0.3" archive: - dependency: transitive + dependency: "direct main" description: name: archive sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b" @@ -225,6 +225,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.8" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e + url: "https://pub.dev" + source: hosted + version: "0.3.3+8" crypto: dependency: "direct main" description: @@ -1101,6 +1109,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.8" + share_plus: + dependency: "direct main" + description: + name: share_plus + sha256: f74fc3f1cbd99f39760182e176802f693fa0ec9625c045561cfad54681ea93dd + url: "https://pub.dev" + source: hosted + version: "7.2.1" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: df08bc3a07d01f5ea47b45d03ffcba1fa9cd5370fb44b3f38c70e42cced0f956 + url: "https://pub.dev" + source: hosted + version: "3.3.1" shelf: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 785bd265..45510610 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -57,6 +57,9 @@ dependencies: crypto: ^3.0.3 extended_image: ^8.2.0 flutter_hls_parser: ^2.0.1 + archive: ^3.4.9 + # permission_handler: ^11.1.0 + share_plus: ^7.2.1 dev_dependencies: flutter_test: sdk: flutter From 34d97925f356c42be708ba8a44da438f197f78ce Mon Sep 17 00:00:00 2001 From: appdevelpo <56633229+appdevelpo@users.noreply.github.com> Date: Thu, 7 Dec 2023 23:23:15 +0800 Subject: [PATCH 14/23] update --- windows/flutter/generated_plugin_registrant.cc | 3 +++ windows/flutter/generated_plugins.cmake | 1 + 2 files changed, 4 insertions(+) diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index c697e2b2..4ed5ca7b 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -34,6 +35,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin")); ScreenRetrieverPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ScreenRetrieverPlugin")); + SharePlusWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); WindowManagerPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 7fbaee51..7d0a2d91 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -11,6 +11,7 @@ list(APPEND FLUTTER_PLUGIN_LIST media_kit_video screen_brightness_windows screen_retriever + share_plus url_launcher_windows window_manager ) From 5c4b8feb04f1a8ebf4086cbce1450c80e76e2019 Mon Sep 17 00:00:00 2001 From: MiaoMint <1981324730@qq.com> Date: Mon, 11 Dec 2023 22:15:42 +0800 Subject: [PATCH 15/23] dart fix --- lib/utils/application.dart | 2 +- lib/views/dialogs/bt_dialog.dart | 2 +- lib/views/dialogs/tmdb_binding.dart | 4 ++-- lib/views/pages/code_edit_page.dart | 4 ++-- lib/views/pages/detail_page.dart | 6 +++--- lib/views/pages/extension/extension_page.dart | 2 +- .../pages/extension/extension_repo_page.dart | 2 +- .../extension/extension_settings_page.dart | 4 ++-- lib/views/pages/favorites_page.dart | 2 +- lib/views/pages/home_page.dart | 2 +- lib/views/pages/log_page.dart | 4 ++-- lib/views/pages/main_page.dart | 6 +++--- .../pages/search/extension_searcher_page.dart | 7 +++---- lib/views/pages/search/search_page.dart | 2 +- lib/views/pages/settings_page.dart | 2 +- .../watch/reader/comic/comic_reader.dart | 4 ++-- .../reader/comic/comic_reader_content.dart | 4 ++-- .../reader/comic/comic_reader_settings.dart | 2 +- .../watch/reader/novel/novel_reader.dart | 4 ++-- .../reader/novel/novel_reader_content.dart | 2 +- .../reader/novel/novel_reader_settings.dart | 2 +- lib/views/pages/watch/video/video_player.dart | 9 ++++----- .../watch/video/video_player_content.dart | 4 ++-- lib/views/pages/watch/watch_page.dart | 4 ++-- lib/views/pages/webview_page.dart | 4 ++-- lib/views/widgets/button.dart | 20 +++++++++---------- lib/views/widgets/cache_network_image.dart | 8 ++++---- lib/views/widgets/card_tile.dart | 4 ++-- lib/views/widgets/cover.dart | 4 ++-- .../detail/detail_appbar_flexible_space.dart | 4 ++-- .../widgets/detail/detail_appbar_title.dart | 4 ++-- .../detail/detail_background_color.dart | 4 ++-- .../widgets/detail/detail_continue_play.dart | 4 ++-- lib/views/widgets/detail/detail_episodes.dart | 4 ++-- .../widgets/detail/detail_extension_tile.dart | 4 ++-- .../detail/detail_favorite_button.dart | 4 ++-- lib/views/widgets/detail/detail_overview.dart | 4 ++-- .../widgets/extension/extension_card.dart | 4 ++-- .../widgets/extension/extension_tile.dart | 2 +- lib/views/widgets/extension/info_card.dart | 4 ++-- lib/views/widgets/extension_item_card.dart | 4 ++-- lib/views/widgets/grid_item_tile.dart | 4 ++-- lib/views/widgets/home/home_favorites.dart | 4 ++-- lib/views/widgets/home/home_recent.dart | 4 ++-- lib/views/widgets/home/home_resent_card.dart | 4 ++-- lib/views/widgets/horizontal_list.dart | 8 +++----- lib/views/widgets/infinite_scroller.dart | 4 ++-- lib/views/widgets/list_title.dart | 2 +- lib/views/widgets/log/extension_log_tile.dart | 2 +- lib/views/widgets/progress.dart | 4 ++-- .../widgets/search/search_all_extension.dart | 4 ++-- lib/views/widgets/search/search_all_tile.dart | 4 ++-- lib/views/widgets/settings_input_tile.dart | 4 ++-- .../widgets/settings_numberbox_button.dart | 6 ++---- lib/views/widgets/settings_radios_tile.dart | 4 ++-- lib/views/widgets/settings_switch_tile.dart | 4 ++-- lib/views/widgets/settings_tile.dart | 4 ++-- lib/views/widgets/watch/comic_view.dart | 4 ++-- .../widgets/watch/control_panel_footer.dart | 2 +- .../widgets/watch/control_panel_header.dart | 4 ++-- lib/views/widgets/watch/playlist.dart | 8 ++++---- lib/views/widgets/watch/reader_view.dart | 4 ++-- 62 files changed, 127 insertions(+), 133 deletions(-) diff --git a/lib/utils/application.dart b/lib/utils/application.dart index 17b7866f..d2788a2e 100644 --- a/lib/utils/application.dart +++ b/lib/utils/application.dart @@ -292,7 +292,7 @@ class ApplicationUtils { return true; } File(targetPath).writeAsBytesSync(output); - debugPrint("$targetPath"); + debugPrint(targetPath); // return true; } diff --git a/lib/views/dialogs/bt_dialog.dart b/lib/views/dialogs/bt_dialog.dart index b1e382a8..12505061 100644 --- a/lib/views/dialogs/bt_dialog.dart +++ b/lib/views/dialogs/bt_dialog.dart @@ -10,7 +10,7 @@ import 'package:miru_app/views/widgets/platform_widget.dart'; import 'package:miru_app/views/widgets/progress.dart'; class BTDialog extends StatefulWidget { - const BTDialog({Key? key}) : super(key: key); + const BTDialog({super.key}); @override State createState() => _BTDialogState(); diff --git a/lib/views/dialogs/tmdb_binding.dart b/lib/views/dialogs/tmdb_binding.dart index 072c85c1..29863449 100644 --- a/lib/views/dialogs/tmdb_binding.dart +++ b/lib/views/dialogs/tmdb_binding.dart @@ -16,9 +16,9 @@ import 'package:tmdb_api/tmdb_api.dart'; class TMDBBinding extends StatefulWidget { const TMDBBinding({ - Key? key, + super.key, required this.title, - }) : super(key: key); + }); final String title; @override diff --git a/lib/views/pages/code_edit_page.dart b/lib/views/pages/code_edit_page.dart index e0848233..4e2b4f9b 100644 --- a/lib/views/pages/code_edit_page.dart +++ b/lib/views/pages/code_edit_page.dart @@ -11,8 +11,8 @@ import 'package:flutter_highlight/themes/monokai-sublime.dart'; class CodeEditPage extends StatefulWidget { const CodeEditPage({ required this.extension, - Key? key, - }) : super(key: key); + super.key, + }); final Extension extension; @override State createState() => _CodeEditPageState(); diff --git a/lib/views/pages/detail_page.dart b/lib/views/pages/detail_page.dart index 4db4bd99..38853ebc 100644 --- a/lib/views/pages/detail_page.dart +++ b/lib/views/pages/detail_page.dart @@ -24,11 +24,11 @@ import 'package:url_launcher/url_launcher.dart'; class DetailPage extends StatefulWidget { const DetailPage({ - Key? key, + super.key, required this.url, required this.package, this.tag, - }) : super(key: key); + }); final String url; final String package; final String? tag; @@ -528,7 +528,7 @@ class _DetailPageState extends State { width: 200, child: e, )) - .toList(), + , ]), ); }, diff --git a/lib/views/pages/extension/extension_page.dart b/lib/views/pages/extension/extension_page.dart index 2e9e5461..5fe6577c 100644 --- a/lib/views/pages/extension/extension_page.dart +++ b/lib/views/pages/extension/extension_page.dart @@ -17,7 +17,7 @@ import 'package:miru_app/views/widgets/platform_widget.dart'; import 'package:url_launcher/url_launcher.dart'; class ExtensionPage extends StatefulWidget { - const ExtensionPage({Key? key}) : super(key: key); + const ExtensionPage({super.key}); @override State createState() => _ExtensionPageState(); diff --git a/lib/views/pages/extension/extension_repo_page.dart b/lib/views/pages/extension/extension_repo_page.dart index 4db6f141..f5f15531 100644 --- a/lib/views/pages/extension/extension_repo_page.dart +++ b/lib/views/pages/extension/extension_repo_page.dart @@ -11,7 +11,7 @@ import 'package:miru_app/views/widgets/platform_widget.dart'; import 'package:miru_app/views/widgets/progress.dart'; class ExtensionRepoPage extends StatefulWidget { - const ExtensionRepoPage({Key? key}) : super(key: key); + const ExtensionRepoPage({super.key}); @override State createState() => _ExtensionRepoPageState(); diff --git a/lib/views/pages/extension/extension_settings_page.dart b/lib/views/pages/extension/extension_settings_page.dart index b7ba990a..155c27c6 100644 --- a/lib/views/pages/extension/extension_settings_page.dart +++ b/lib/views/pages/extension/extension_settings_page.dart @@ -25,9 +25,9 @@ import 'package:miru_app/views/widgets/settings_tile.dart'; class ExtensionSettingsPage extends StatefulWidget { const ExtensionSettingsPage({ - Key? key, + super.key, required this.package, - }) : super(key: key); + }); final String package; @override diff --git a/lib/views/pages/favorites_page.dart b/lib/views/pages/favorites_page.dart index c225b690..fc5cc363 100644 --- a/lib/views/pages/favorites_page.dart +++ b/lib/views/pages/favorites_page.dart @@ -9,7 +9,7 @@ import 'package:miru_app/views/widgets/platform_widget.dart'; import 'package:miru_app/views/widgets/progress.dart'; class FavoritesPage extends fluent.StatefulWidget { - const FavoritesPage({Key? key, required this.type}) : super(key: key); + const FavoritesPage({super.key, required this.type}); final ExtensionType type; @override diff --git a/lib/views/pages/home_page.dart b/lib/views/pages/home_page.dart index 7d14b535..3b8318b7 100644 --- a/lib/views/pages/home_page.dart +++ b/lib/views/pages/home_page.dart @@ -8,7 +8,7 @@ import 'package:miru_app/utils/i18n.dart'; import 'package:miru_app/views/widgets/platform_widget.dart'; class HomePage extends StatefulWidget { - const HomePage({Key? key}) : super(key: key); + const HomePage({super.key}); @override State createState() => _HomePageState(); diff --git a/lib/views/pages/log_page.dart b/lib/views/pages/log_page.dart index 349bc34c..9604c087 100644 --- a/lib/views/pages/log_page.dart +++ b/lib/views/pages/log_page.dart @@ -7,9 +7,9 @@ import 'package:miru_app/views/widgets/log/extension_log_tile.dart'; class ExtensionLogWindow extends StatefulWidget { const ExtensionLogWindow({ - Key? key, + super.key, required this.windowController, - }) : super(key: key); + }); final WindowController windowController; @override diff --git a/lib/views/pages/main_page.dart b/lib/views/pages/main_page.dart index 263110fb..c377ba55 100644 --- a/lib/views/pages/main_page.dart +++ b/lib/views/pages/main_page.dart @@ -16,11 +16,11 @@ import 'package:window_manager/window_manager.dart'; class DesktopMainPage extends StatefulWidget { const DesktopMainPage({ - Key? key, + super.key, required this.child, required this.shellContext, required this.state, - }) : super(key: key); + }); final Widget child; final BuildContext? shellContext; @@ -150,7 +150,7 @@ class _DesktopMainPageState extends State { } class AndroidMainPage extends fluent.StatefulWidget { - const AndroidMainPage({Key? key}) : super(key: key); + const AndroidMainPage({super.key}); @override fluent.State createState() => _AndroidMainPageState(); diff --git a/lib/views/pages/search/extension_searcher_page.dart b/lib/views/pages/search/extension_searcher_page.dart index c07f87db..f1020e88 100644 --- a/lib/views/pages/search/extension_searcher_page.dart +++ b/lib/views/pages/search/extension_searcher_page.dart @@ -20,10 +20,10 @@ import 'package:miru_app/views/widgets/search_appbar.dart'; class ExtensionSearcherPage extends fluent.StatefulWidget { const ExtensionSearcherPage({ - Key? key, + super.key, required this.package, this.keyWord, - }) : super(key: key); + }); final String package; final String? keyWord; @@ -356,12 +356,11 @@ class _ExtensionSearcherPageState extends fluent.State { class _ExtensionFilterWidget extends StatefulWidget { const _ExtensionFilterWidget({ - Key? key, required this.runtime, required this.selectedFilters, required this.onSelectFilter, required this.filters, - }) : super(key: key); + }); final ExtensionService runtime; final Map filters; final Map> selectedFilters; diff --git a/lib/views/pages/search/search_page.dart b/lib/views/pages/search/search_page.dart index 611203b4..3c8c316c 100644 --- a/lib/views/pages/search/search_page.dart +++ b/lib/views/pages/search/search_page.dart @@ -11,7 +11,7 @@ import 'package:miru_app/views/widgets/platform_widget.dart'; import 'package:miru_app/views/widgets/search_appbar.dart'; class SearchPage extends StatefulWidget { - const SearchPage({Key? key}) : super(key: key); + const SearchPage({super.key}); @override State createState() => _SearchPageState(); diff --git a/lib/views/pages/settings_page.dart b/lib/views/pages/settings_page.dart index 8d8a7fc3..4582238e 100644 --- a/lib/views/pages/settings_page.dart +++ b/lib/views/pages/settings_page.dart @@ -24,7 +24,7 @@ import 'package:tmdb_api/tmdb_api.dart'; import 'package:url_launcher/url_launcher.dart'; class SettingsPage extends StatefulWidget { - const SettingsPage({Key? key}) : super(key: key); + const SettingsPage({super.key}); @override State createState() => _SettingsPageState(); diff --git a/lib/views/pages/watch/reader/comic/comic_reader.dart b/lib/views/pages/watch/reader/comic/comic_reader.dart index ee5ee035..063768f2 100644 --- a/lib/views/pages/watch/reader/comic/comic_reader.dart +++ b/lib/views/pages/watch/reader/comic/comic_reader.dart @@ -12,7 +12,7 @@ import 'package:window_manager/window_manager.dart'; class ComicReader extends StatefulWidget { const ComicReader({ - Key? key, + super.key, required this.title, required this.playList, required this.detailUrl, @@ -20,7 +20,7 @@ class ComicReader extends StatefulWidget { required this.episodeGroupId, required this.runtime, this.cover, - }) : super(key: key); + }); final String title; final List playList; diff --git a/lib/views/pages/watch/reader/comic/comic_reader_content.dart b/lib/views/pages/watch/reader/comic/comic_reader_content.dart index bf2589ab..f91a02e8 100644 --- a/lib/views/pages/watch/reader/comic/comic_reader_content.dart +++ b/lib/views/pages/watch/reader/comic/comic_reader_content.dart @@ -15,7 +15,7 @@ import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:extended_image/extended_image.dart'; class ComicReaderContent extends StatefulWidget { - const ComicReaderContent(this.tag, {Key? key}) : super(key: key); + const ComicReaderContent(this.tag, {super.key}); final String tag; @override @@ -112,7 +112,7 @@ class _ComicReaderContentState extends State { onInteractionUpdate: (x) { double correctScaleValue = transformationController.value.getMaxScaleOnAxis(); - if (correctScaleValue > minScaleValue) ; + if (correctScaleValue > minScaleValue) {} if (x.scale == correctScaleValue) { setState(() { isZoomed = false; diff --git a/lib/views/pages/watch/reader/comic/comic_reader_settings.dart b/lib/views/pages/watch/reader/comic/comic_reader_settings.dart index a0eb18fb..7e494f49 100644 --- a/lib/views/pages/watch/reader/comic/comic_reader_settings.dart +++ b/lib/views/pages/watch/reader/comic/comic_reader_settings.dart @@ -7,7 +7,7 @@ import 'package:miru_app/utils/i18n.dart'; import 'package:miru_app/views/widgets/platform_widget.dart'; class ComicReaderSettings extends StatefulWidget { - const ComicReaderSettings(this.tag, {Key? key}) : super(key: key); + const ComicReaderSettings(this.tag, {super.key}); final String tag; @override diff --git a/lib/views/pages/watch/reader/novel/novel_reader.dart b/lib/views/pages/watch/reader/novel/novel_reader.dart index de3c9725..29e903b7 100644 --- a/lib/views/pages/watch/reader/novel/novel_reader.dart +++ b/lib/views/pages/watch/reader/novel/novel_reader.dart @@ -9,7 +9,7 @@ import 'package:miru_app/data/services/extension_service.dart'; class NovelReader extends StatefulWidget { const NovelReader({ - Key? key, + super.key, required this.playList, required this.runtime, required this.episodeGroupId, @@ -17,7 +17,7 @@ class NovelReader extends StatefulWidget { required this.title, required this.detailUrl, this.cover, - }) : super(key: key); + }); final String title; final List playList; diff --git a/lib/views/pages/watch/reader/novel/novel_reader_content.dart b/lib/views/pages/watch/reader/novel/novel_reader_content.dart index e137d9e8..e8460c99 100644 --- a/lib/views/pages/watch/reader/novel/novel_reader_content.dart +++ b/lib/views/pages/watch/reader/novel/novel_reader_content.dart @@ -9,7 +9,7 @@ import 'package:fluent_ui/fluent_ui.dart' as fluent; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; class NovelReaderContent extends StatefulWidget { - const NovelReaderContent(this.tag, {Key? key}) : super(key: key); + const NovelReaderContent(this.tag, {super.key}); final String tag; @override diff --git a/lib/views/pages/watch/reader/novel/novel_reader_settings.dart b/lib/views/pages/watch/reader/novel/novel_reader_settings.dart index aa7bc42d..7dd0c4c9 100644 --- a/lib/views/pages/watch/reader/novel/novel_reader_settings.dart +++ b/lib/views/pages/watch/reader/novel/novel_reader_settings.dart @@ -6,7 +6,7 @@ import 'package:miru_app/utils/i18n.dart'; import 'package:miru_app/views/widgets/platform_widget.dart'; class NovelReaderSettings extends StatefulWidget { - const NovelReaderSettings(this.tag, {Key? key}) : super(key: key); + const NovelReaderSettings(this.tag, {super.key}); final String tag; @override diff --git a/lib/views/pages/watch/video/video_player.dart b/lib/views/pages/watch/video/video_player.dart index 9503af1b..726ced22 100644 --- a/lib/views/pages/watch/video/video_player.dart +++ b/lib/views/pages/watch/video/video_player.dart @@ -10,14 +10,14 @@ import 'package:miru_app/views/widgets/platform_widget.dart'; class VideoPlayer extends StatefulWidget { const VideoPlayer({ - Key? key, + super.key, required this.playList, required this.runtime, required this.episodeGroupId, required this.playerIndex, required this.title, required this.detailUrl, - }) : super(key: key); + }); final String title; final List playList; @@ -58,10 +58,9 @@ class _VideoPlayerState extends State { _buildContent() { return Obx(() { final maxWidth = MediaQuery.of(context).size.width; - return WillPopScope( - onWillPop: () async { + return PopScope( + onPopInvoked: (_) async { await _c.onExit(); - return true; }, child: Row( children: [ diff --git a/lib/views/pages/watch/video/video_player_content.dart b/lib/views/pages/watch/video/video_player_content.dart index bb848484..3bf13267 100644 --- a/lib/views/pages/watch/video/video_player_content.dart +++ b/lib/views/pages/watch/video/video_player_content.dart @@ -10,9 +10,9 @@ import 'package:window_manager/window_manager.dart'; class VideoPlayerConten extends StatefulWidget { const VideoPlayerConten({ - Key? key, + super.key, required this.tag, - }) : super(key: key); + }); final String tag; @override diff --git a/lib/views/pages/watch/watch_page.dart b/lib/views/pages/watch/watch_page.dart index 5911f53d..a239bab5 100644 --- a/lib/views/pages/watch/watch_page.dart +++ b/lib/views/pages/watch/watch_page.dart @@ -7,7 +7,7 @@ import 'package:miru_app/utils/extension.dart'; class WatchPage extends StatelessWidget { const WatchPage({ - Key? key, + super.key, required this.playList, required this.package, required this.title, @@ -15,7 +15,7 @@ class WatchPage extends StatelessWidget { required this.episodeGroupId, required this.detailUrl, this.cover, - }) : super(key: key); + }); final List playList; final int playerIndex; final String title; diff --git a/lib/views/pages/webview_page.dart b/lib/views/pages/webview_page.dart index 65dfedfd..da385831 100644 --- a/lib/views/pages/webview_page.dart +++ b/lib/views/pages/webview_page.dart @@ -4,10 +4,10 @@ import 'package:miru_app/data/services/extension_service.dart'; class WebViewPage extends StatefulWidget { const WebViewPage({ - Key? key, + super.key, required this.extensionRuntime, required this.url, - }) : super(key: key); + }); final ExtensionService extensionRuntime; final String url; diff --git a/lib/views/widgets/button.dart b/lib/views/widgets/button.dart index 06d496c7..57e688f6 100644 --- a/lib/views/widgets/button.dart +++ b/lib/views/widgets/button.dart @@ -4,10 +4,10 @@ import 'package:miru_app/views/widgets/platform_widget.dart'; class PlatformButton extends StatelessWidget { const PlatformButton({ - Key? key, + super.key, required this.child, this.onPressed, - }) : super(key: key); + }); final Widget child; final VoidCallback? onPressed; @@ -30,10 +30,10 @@ class PlatformButton extends StatelessWidget { class PlatformFilledButton extends StatelessWidget { const PlatformFilledButton({ - Key? key, + super.key, required this.child, this.onPressed, - }) : super(key: key); + }); final Widget child; final VoidCallback? onPressed; @@ -56,10 +56,10 @@ class PlatformFilledButton extends StatelessWidget { class PlatformTextButton extends StatelessWidget { const PlatformTextButton({ - Key? key, + super.key, required this.child, this.onPressed, - }) : super(key: key); + }); final Widget child; final VoidCallback? onPressed; @@ -82,10 +82,10 @@ class PlatformTextButton extends StatelessWidget { class PlatformIconButton extends StatelessWidget { const PlatformIconButton({ - Key? key, + super.key, required this.icon, this.onPressed, - }) : super(key: key); + }); final Widget icon; final VoidCallback? onPressed; @@ -108,11 +108,11 @@ class PlatformIconButton extends StatelessWidget { class PlatformToggleButton extends fluent.StatelessWidget { const PlatformToggleButton({ - Key? key, + super.key, required this.checked, required this.onChanged, required this.text, - }) : super(key: key); + }); final bool checked; final void Function(bool)? onChanged; diff --git a/lib/views/widgets/cache_network_image.dart b/lib/views/widgets/cache_network_image.dart index b87bf662..21525cac 100644 --- a/lib/views/widgets/cache_network_image.dart +++ b/lib/views/widgets/cache_network_image.dart @@ -5,13 +5,13 @@ import 'package:flutter/material.dart'; class CacheNetWorkImagePic extends StatelessWidget { const CacheNetWorkImagePic( this.url, { - Key? key, + super.key, this.fit = BoxFit.cover, this.width, this.height, this.fallback, this.headers, - }) : super(key: key); + }); final String url; final BoxFit fit; final double? width; @@ -42,13 +42,13 @@ class CacheNetWorkImagePic extends StatelessWidget { class CacheNetWorkImageComic extends StatelessWidget { const CacheNetWorkImageComic( this.url, { - Key? key, + super.key, this.fit = BoxFit.cover, this.width, this.height, this.fallback, this.headers, - }) : super(key: key); + }); final String url; final BoxFit fit; final double? width; diff --git a/lib/views/widgets/card_tile.dart b/lib/views/widgets/card_tile.dart index 366166e0..d569e1ab 100644 --- a/lib/views/widgets/card_tile.dart +++ b/lib/views/widgets/card_tile.dart @@ -2,12 +2,12 @@ import 'package:fluent_ui/fluent_ui.dart'; class CardTile extends StatelessWidget { const CardTile({ - Key? key, + super.key, required this.title, required this.child, this.trailing, this.leading, - }) : super(key: key); + }); final String title; final Widget? leading; final Widget? trailing; diff --git a/lib/views/widgets/cover.dart b/lib/views/widgets/cover.dart index a563c10a..3c405abb 100644 --- a/lib/views/widgets/cover.dart +++ b/lib/views/widgets/cover.dart @@ -4,11 +4,11 @@ import 'package:miru_app/views/widgets/cache_network_image.dart'; class Cover extends StatelessWidget { const Cover({ - Key? key, + super.key, required this.alt, this.url, this.noText = false, - }) : super(key: key); + }); final String? url; final String alt; final bool noText; diff --git a/lib/views/widgets/detail/detail_appbar_flexible_space.dart b/lib/views/widgets/detail/detail_appbar_flexible_space.dart index 6042d6e1..84ba6daa 100644 --- a/lib/views/widgets/detail/detail_appbar_flexible_space.dart +++ b/lib/views/widgets/detail/detail_appbar_flexible_space.dart @@ -9,9 +9,9 @@ import 'package:miru_app/views/widgets/cover.dart'; class DetailAppbarflexibleSpace extends StatefulWidget { const DetailAppbarflexibleSpace({ - Key? key, + super.key, this.tag, - }) : super(key: key); + }); final String? tag; diff --git a/lib/views/widgets/detail/detail_appbar_title.dart b/lib/views/widgets/detail/detail_appbar_title.dart index b2597413..a5374da7 100644 --- a/lib/views/widgets/detail/detail_appbar_title.dart +++ b/lib/views/widgets/detail/detail_appbar_title.dart @@ -3,9 +3,9 @@ import 'package:flutter/material.dart'; class DetailAppbarTitle extends StatefulWidget { const DetailAppbarTitle( this.text, { - Key? key, + super.key, required this.controller, - }) : super(key: key); + }); final String text; final ScrollController controller; diff --git a/lib/views/widgets/detail/detail_background_color.dart b/lib/views/widgets/detail/detail_background_color.dart index aee05f4c..fc0e32e2 100644 --- a/lib/views/widgets/detail/detail_background_color.dart +++ b/lib/views/widgets/detail/detail_background_color.dart @@ -2,9 +2,9 @@ import 'package:fluent_ui/fluent_ui.dart'; class DetailBackgroundColor extends StatefulWidget { const DetailBackgroundColor({ - Key? key, + super.key, required this.controller, - }) : super(key: key); + }); final ScrollController controller; @override diff --git a/lib/views/widgets/detail/detail_continue_play.dart b/lib/views/widgets/detail/detail_continue_play.dart index 59fbf976..3ad66f19 100644 --- a/lib/views/widgets/detail/detail_continue_play.dart +++ b/lib/views/widgets/detail/detail_continue_play.dart @@ -9,9 +9,9 @@ import 'package:miru_app/views/widgets/platform_widget.dart'; class DetailContinuePlay extends StatefulWidget { const DetailContinuePlay({ - Key? key, + super.key, this.tag, - }) : super(key: key); + }); final String? tag; @override diff --git a/lib/views/widgets/detail/detail_episodes.dart b/lib/views/widgets/detail/detail_episodes.dart index eb9246d6..586ee69e 100644 --- a/lib/views/widgets/detail/detail_episodes.dart +++ b/lib/views/widgets/detail/detail_episodes.dart @@ -12,9 +12,9 @@ import 'package:miru_app/views/widgets/platform_widget.dart'; class DetailEpisodes extends StatefulWidget { const DetailEpisodes({ - Key? key, + super.key, this.tag, - }) : super(key: key); + }); final String? tag; @override diff --git a/lib/views/widgets/detail/detail_extension_tile.dart b/lib/views/widgets/detail/detail_extension_tile.dart index 6e56b62f..40c9e4a1 100644 --- a/lib/views/widgets/detail/detail_extension_tile.dart +++ b/lib/views/widgets/detail/detail_extension_tile.dart @@ -7,9 +7,9 @@ import 'package:miru_app/views/widgets/cache_network_image.dart'; class DetailExtensionTile extends StatelessWidget { const DetailExtensionTile({ - Key? key, + super.key, this.tag, - }) : super(key: key); + }); final String? tag; diff --git a/lib/views/widgets/detail/detail_favorite_button.dart b/lib/views/widgets/detail/detail_favorite_button.dart index d4df2f86..37596b20 100644 --- a/lib/views/widgets/detail/detail_favorite_button.dart +++ b/lib/views/widgets/detail/detail_favorite_button.dart @@ -7,9 +7,9 @@ import 'package:miru_app/views/widgets/platform_widget.dart'; class DetailFavoriteButton extends StatefulWidget { const DetailFavoriteButton({ - Key? key, + super.key, this.tag, - }) : super(key: key); + }); final String? tag; @override diff --git a/lib/views/widgets/detail/detail_overview.dart b/lib/views/widgets/detail/detail_overview.dart index e4e9537e..ceed041b 100644 --- a/lib/views/widgets/detail/detail_overview.dart +++ b/lib/views/widgets/detail/detail_overview.dart @@ -7,9 +7,9 @@ import 'package:miru_app/views/widgets/cache_network_image.dart'; class DetailOverView extends StatelessWidget { const DetailOverView({ - Key? key, + super.key, this.tag, - }) : super(key: key); + }); final String? tag; diff --git a/lib/views/widgets/extension/extension_card.dart b/lib/views/widgets/extension/extension_card.dart index 549064f0..f11a5b92 100644 --- a/lib/views/widgets/extension/extension_card.dart +++ b/lib/views/widgets/extension/extension_card.dart @@ -10,14 +10,14 @@ import 'package:miru_app/views/widgets/progress.dart'; class ExtensionCard extends StatefulWidget { const ExtensionCard({ - Key? key, + super.key, required this.name, required this.version, required this.icon, required this.package, required this.nsfw, required this.type, - }) : super(key: key); + }); final String? icon; final String name; final String version; diff --git a/lib/views/widgets/extension/extension_tile.dart b/lib/views/widgets/extension/extension_tile.dart index 2e502612..ec2e06f3 100644 --- a/lib/views/widgets/extension/extension_tile.dart +++ b/lib/views/widgets/extension/extension_tile.dart @@ -13,7 +13,7 @@ import 'package:url_launcher/url_launcher.dart'; import 'package:path/path.dart' as path; class ExtensionTile extends StatefulWidget { - const ExtensionTile(this.extension, {Key? key}) : super(key: key); + const ExtensionTile(this.extension, {super.key}); final Extension extension; @override diff --git a/lib/views/widgets/extension/info_card.dart b/lib/views/widgets/extension/info_card.dart index f6e65672..33a0246a 100644 --- a/lib/views/widgets/extension/info_card.dart +++ b/lib/views/widgets/extension/info_card.dart @@ -3,11 +3,11 @@ import 'package:miru_app/views/widgets/platform_widget.dart'; class InfoCard extends StatelessWidget { const InfoCard({ - Key? key, + super.key, required this.icon, required this.title, required this.content, - }) : super(key: key); + }); final IconData icon; final String title; final String content; diff --git a/lib/views/widgets/extension_item_card.dart b/lib/views/widgets/extension_item_card.dart index 36d25cd0..919bf6ee 100644 --- a/lib/views/widgets/extension_item_card.dart +++ b/lib/views/widgets/extension_item_card.dart @@ -7,13 +7,13 @@ import 'package:miru_app/views/widgets/platform_widget.dart'; class ExtensionItemCard extends StatefulWidget { const ExtensionItemCard({ - Key? key, + super.key, required this.title, required this.url, required this.package, this.cover, this.update, - }) : super(key: key); + }); final String title; final String? cover; final String? update; diff --git a/lib/views/widgets/grid_item_tile.dart b/lib/views/widgets/grid_item_tile.dart index c5e55e21..13202d5b 100644 --- a/lib/views/widgets/grid_item_tile.dart +++ b/lib/views/widgets/grid_item_tile.dart @@ -4,12 +4,12 @@ import 'package:miru_app/views/widgets/platform_widget.dart'; class GridItemTile extends StatefulWidget { const GridItemTile({ - Key? key, + super.key, required this.title, this.cover, this.subtitle, this.onTap, - }) : super(key: key); + }); final String title; final String? cover; final String? subtitle; diff --git a/lib/views/widgets/home/home_favorites.dart b/lib/views/widgets/home/home_favorites.dart index 101723ea..615b27e7 100644 --- a/lib/views/widgets/home/home_favorites.dart +++ b/lib/views/widgets/home/home_favorites.dart @@ -11,10 +11,10 @@ import 'package:miru_app/views/widgets/horizontal_list.dart'; class HomeFavorites extends StatefulWidget { const HomeFavorites({ - Key? key, + super.key, required this.type, required this.data, - }) : super(key: key); + }); final ExtensionType type; final List data; diff --git a/lib/views/widgets/home/home_recent.dart b/lib/views/widgets/home/home_recent.dart index 94426b10..06c65b2f 100644 --- a/lib/views/widgets/home/home_recent.dart +++ b/lib/views/widgets/home/home_recent.dart @@ -7,9 +7,9 @@ import 'package:miru_app/views/widgets/platform_widget.dart'; class HomeRecent extends StatefulWidget { const HomeRecent({ - Key? key, + super.key, required this.data, - }) : super(key: key); + }); final List data; @override diff --git a/lib/views/widgets/home/home_resent_card.dart b/lib/views/widgets/home/home_resent_card.dart index 47715e33..aec13622 100644 --- a/lib/views/widgets/home/home_resent_card.dart +++ b/lib/views/widgets/home/home_resent_card.dart @@ -20,9 +20,9 @@ import 'package:palette_generator/palette_generator.dart'; class HomeRecentCard extends StatefulWidget { const HomeRecentCard({ - Key? key, + super.key, required this.history, - }) : super(key: key); + }); final History history; @override diff --git a/lib/views/widgets/horizontal_list.dart b/lib/views/widgets/horizontal_list.dart index d8d2f339..13e1e96f 100644 --- a/lib/views/widgets/horizontal_list.dart +++ b/lib/views/widgets/horizontal_list.dart @@ -5,7 +5,7 @@ import 'package:miru_app/views/widgets/platform_widget.dart'; class HorizontalList extends StatefulWidget { const HorizontalList({ - Key? key, + super.key, required this.title, required this.onClickMore, this.itemCount, @@ -14,8 +14,7 @@ class HorizontalList extends StatefulWidget { }) : assert( (itemCount != null && itemBuilder != null) || contentBuilder != null, "itemCount and itemBuilder or contentBuilder must not be null", - ), - super(key: key); + ); final String title; final void Function() onClickMore; final int? itemCount; @@ -136,8 +135,7 @@ class _HorizontalListState extends State { } class HorizontalTitle extends StatefulWidget { - const HorizontalTitle(this.text, {Key? key, required this.onClick}) - : super(key: key); + const HorizontalTitle(this.text, {super.key, required this.onClick}); final String text; final Function() onClick; diff --git a/lib/views/widgets/infinite_scroller.dart b/lib/views/widgets/infinite_scroller.dart index a3ee81ca..13fc0991 100644 --- a/lib/views/widgets/infinite_scroller.dart +++ b/lib/views/widgets/infinite_scroller.dart @@ -7,14 +7,14 @@ import 'package:easy_refresh/easy_refresh.dart'; class InfiniteScroller extends StatefulWidget { const InfiniteScroller({ - Key? key, + super.key, required this.child, required this.onRefresh, required this.onLoad, this.refreshOnStart = true, this.enableInfiniteScroll = true, this.easyRefreshController, - }) : super(key: key); + }); final Widget child; final Future Function() onRefresh; diff --git a/lib/views/widgets/list_title.dart b/lib/views/widgets/list_title.dart index dc282845..ad09e29b 100644 --- a/lib/views/widgets/list_title.dart +++ b/lib/views/widgets/list_title.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:miru_app/views/widgets/platform_widget.dart'; class ListTitle extends StatelessWidget { - const ListTitle({Key? key, required this.title}) : super(key: key); + const ListTitle({super.key, required this.title}); final String title; diff --git a/lib/views/widgets/log/extension_log_tile.dart b/lib/views/widgets/log/extension_log_tile.dart index 8ef563e6..8a473e24 100644 --- a/lib/views/widgets/log/extension_log_tile.dart +++ b/lib/views/widgets/log/extension_log_tile.dart @@ -3,7 +3,7 @@ import 'package:miru_app/models/extension.dart'; import 'package:miru_app/views/widgets/cache_network_image.dart'; class ExtensionLogTile extends StatelessWidget { - const ExtensionLogTile({Key? key, required this.log}) : super(key: key); + const ExtensionLogTile({super.key, required this.log}); final ExtensionLog log; @override diff --git a/lib/views/widgets/progress.dart b/lib/views/widgets/progress.dart index dc418ae5..821e9041 100644 --- a/lib/views/widgets/progress.dart +++ b/lib/views/widgets/progress.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:miru_app/views/widgets/platform_widget.dart'; class ProgressRing extends StatelessWidget { - const ProgressRing({Key? key, this.value}) : super(key: key); + const ProgressRing({super.key, this.value}); final double? value; @override @@ -20,7 +20,7 @@ class ProgressRing extends StatelessWidget { } class ProgressBar extends StatelessWidget { - const ProgressBar({Key? key, this.value}) : super(key: key); + const ProgressBar({super.key, this.value}); final double? value; @override diff --git a/lib/views/widgets/search/search_all_extension.dart b/lib/views/widgets/search/search_all_extension.dart index da7cde4b..f0b4e281 100644 --- a/lib/views/widgets/search/search_all_extension.dart +++ b/lib/views/widgets/search/search_all_extension.dart @@ -11,11 +11,11 @@ import 'package:miru_app/views/widgets/button.dart'; class SearchAllExtSearch extends StatefulWidget { const SearchAllExtSearch({ - Key? key, + super.key, required this.kw, required this.runtimeList, required this.onClickMore, - }) : super(key: key); + }); final String kw; final List runtimeList; final Function(int) onClickMore; diff --git a/lib/views/widgets/search/search_all_tile.dart b/lib/views/widgets/search/search_all_tile.dart index a81796dc..89daaf67 100644 --- a/lib/views/widgets/search/search_all_tile.dart +++ b/lib/views/widgets/search/search_all_tile.dart @@ -9,11 +9,11 @@ import 'package:miru_app/views/widgets/progress.dart'; class SearchAllTile extends StatefulWidget { const SearchAllTile({ - Key? key, + super.key, required this.searchResult, required this.onClickMore, required this.kw, - }) : super(key: key); + }); final String kw; final SearchResult searchResult; diff --git a/lib/views/widgets/settings_input_tile.dart b/lib/views/widgets/settings_input_tile.dart index c08da165..4afe511e 100644 --- a/lib/views/widgets/settings_input_tile.dart +++ b/lib/views/widgets/settings_input_tile.dart @@ -6,14 +6,14 @@ import 'package:miru_app/views/widgets/settings_tile.dart'; class SettingsIntpuTile extends fluent.StatefulWidget { const SettingsIntpuTile({ - Key? key, + super.key, this.icon, required this.title, required this.onChanged, required this.text, required this.buildSubtitle, this.trailing = const Icon(Icons.chevron_right), - }) : super(key: key); + }); final Widget? icon; final String title; final String Function() buildSubtitle; diff --git a/lib/views/widgets/settings_numberbox_button.dart b/lib/views/widgets/settings_numberbox_button.dart index 1dadd812..57792f1d 100644 --- a/lib/views/widgets/settings_numberbox_button.dart +++ b/lib/views/widgets/settings_numberbox_button.dart @@ -1,19 +1,17 @@ import 'package:fluent_ui/fluent_ui.dart' as fluent; import 'package:flutter/material.dart'; -import 'package:miru_app/utils/i18n.dart'; // import 'package:miru_app/views/widgets/platform_widget.dart'; -import 'package:miru_app/views/widgets/settings_tile.dart'; class SettingNumboxButton extends fluent.StatefulWidget { const SettingNumboxButton({ - Key? key, + super.key, this.icon, required this.title, required this.onChanged, required this.button1text, required this.button2text, required this.numberBoxvalue, - }) : super(key: key); + }); final Widget? icon; final String title; final String button1text; diff --git a/lib/views/widgets/settings_radios_tile.dart b/lib/views/widgets/settings_radios_tile.dart index 6cc127c4..dbe27d5c 100644 --- a/lib/views/widgets/settings_radios_tile.dart +++ b/lib/views/widgets/settings_radios_tile.dart @@ -5,7 +5,7 @@ import 'package:miru_app/views/widgets/platform_widget.dart'; class SettingsRadiosTile extends StatefulWidget { const SettingsRadiosTile({ - Key? key, + super.key, this.icon, required this.title, this.buildSubtitle, @@ -13,7 +13,7 @@ class SettingsRadiosTile extends StatefulWidget { required this.applyValue, required this.buildGroupValue, this.trailing = const Icon(Icons.chevron_right), - }) : super(key: key); + }); final Widget? icon; final String title; final String Function()? buildSubtitle; diff --git a/lib/views/widgets/settings_switch_tile.dart b/lib/views/widgets/settings_switch_tile.dart index 6fd84d2f..8301c19e 100644 --- a/lib/views/widgets/settings_switch_tile.dart +++ b/lib/views/widgets/settings_switch_tile.dart @@ -5,13 +5,13 @@ import 'package:miru_app/views/widgets/platform_widget.dart'; class SettingsSwitchTile extends StatefulWidget { const SettingsSwitchTile({ - Key? key, + super.key, this.icon, required this.title, required this.buildValue, required this.onChanged, this.buildSubtitle, - }) : super(key: key); + }); final Widget? icon; final String title; final String Function()? buildSubtitle; diff --git a/lib/views/widgets/settings_tile.dart b/lib/views/widgets/settings_tile.dart index 3f20f1f9..178df90c 100644 --- a/lib/views/widgets/settings_tile.dart +++ b/lib/views/widgets/settings_tile.dart @@ -4,13 +4,13 @@ import 'package:miru_app/views/widgets/platform_widget.dart'; class SettingsTile extends StatefulWidget { const SettingsTile({ - Key? key, + super.key, this.icon, required this.title, this.trailing, this.buildSubtitle, this.onTap, - }) : super(key: key); + }); final Widget? icon; final String title; final String Function()? buildSubtitle; diff --git a/lib/views/widgets/watch/comic_view.dart b/lib/views/widgets/watch/comic_view.dart index 3bc9efe3..5afce70b 100644 --- a/lib/views/widgets/watch/comic_view.dart +++ b/lib/views/widgets/watch/comic_view.dart @@ -7,10 +7,10 @@ import 'package:miru_app/controllers/watch/reader_controller.dart'; class ReaderView extends StatelessWidget { const ReaderView( this.tag, { - Key? key, + super.key, required this.content, required this.buildSettings, - }) : super(key: key); + }); final String tag; final Widget content; final Widget Function(BuildContext context) buildSettings; diff --git a/lib/views/widgets/watch/control_panel_footer.dart b/lib/views/widgets/watch/control_panel_footer.dart index 2ae0bb27..c80a83a4 100644 --- a/lib/views/widgets/watch/control_panel_footer.dart +++ b/lib/views/widgets/watch/control_panel_footer.dart @@ -8,7 +8,7 @@ import 'package:miru_app/utils/i18n.dart'; import 'package:miru_app/views/widgets/button.dart'; class ControlPanelFooter extends StatelessWidget { - const ControlPanelFooter(this.tag, {Key? key}) : super(key: key); + const ControlPanelFooter(this.tag, {super.key}); final String tag; @override diff --git a/lib/views/widgets/watch/control_panel_header.dart b/lib/views/widgets/watch/control_panel_header.dart index 3c9c5823..9c3a54fd 100644 --- a/lib/views/widgets/watch/control_panel_header.dart +++ b/lib/views/widgets/watch/control_panel_header.dart @@ -12,9 +12,9 @@ import 'package:window_manager/window_manager.dart'; class ControlPanelHeader extends StatefulWidget { const ControlPanelHeader( this.tag, { - Key? key, + super.key, required this.buildSettings, - }) : super(key: key); + }); final String tag; final Widget Function(BuildContext context) buildSettings; diff --git a/lib/views/widgets/watch/playlist.dart b/lib/views/widgets/watch/playlist.dart index a79316d2..7900921d 100644 --- a/lib/views/widgets/watch/playlist.dart +++ b/lib/views/widgets/watch/playlist.dart @@ -5,12 +5,12 @@ import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; class PlayList extends fluent.StatelessWidget { const PlayList({ - Key? key, + super.key, required this.title, required this.list, required this.selectIndex, required this.onChange, - }) : super(key: key); + }); final String title; final List list; final int selectIndex; @@ -69,11 +69,11 @@ class PlayList extends fluent.StatelessWidget { class PlaylistAndroidTile extends StatelessWidget { const PlaylistAndroidTile({ - Key? key, + super.key, required this.title, required this.onTap, required this.selected, - }) : super(key: key); + }); final String title; final Function() onTap; final bool selected; diff --git a/lib/views/widgets/watch/reader_view.dart b/lib/views/widgets/watch/reader_view.dart index 9e8a6182..b75b36b0 100644 --- a/lib/views/widgets/watch/reader_view.dart +++ b/lib/views/widgets/watch/reader_view.dart @@ -7,10 +7,10 @@ import 'package:miru_app/controllers/watch/reader_controller.dart'; class ReaderView extends StatelessWidget { const ReaderView( this.tag, { - Key? key, + super.key, required this.content, required this.buildSettings, - }) : super(key: key); + }); final String tag; final Widget content; final Widget Function(BuildContext context) buildSettings; From 61457a2f7893f3a11a7a23f923e0146451572714 Mon Sep 17 00:00:00 2001 From: appdevelpo <56633229+appdevelpo@users.noreply.github.com> Date: Mon, 11 Dec 2023 22:56:27 +0800 Subject: [PATCH 16/23] Update comic_reader.dart --- .../watch/reader/comic/comic_reader.dart | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/views/pages/watch/reader/comic/comic_reader.dart b/lib/views/pages/watch/reader/comic/comic_reader.dart index 063768f2..8314f7e8 100644 --- a/lib/views/pages/watch/reader/comic/comic_reader.dart +++ b/lib/views/pages/watch/reader/comic/comic_reader.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:fluent_ui/fluent_ui.dart'; import 'package:get/get.dart'; import 'package:miru_app/models/extension.dart'; @@ -7,7 +9,6 @@ import 'package:miru_app/controllers/watch/comic_controller.dart'; // import 'package:miru_app/views/pages/watch/reader/comic/comic_zoom.dart'; import 'package:miru_app/views/widgets/watch/comic_view.dart'; import 'package:miru_app/data/services/extension_service.dart'; -import 'package:miru_app/views/widgets/platform_widget.dart'; import 'package:window_manager/window_manager.dart'; class ComicReader extends StatefulWidget { @@ -60,22 +61,21 @@ class _ComicReaderState extends State { @override Widget build(BuildContext context) { - return PlatformBuildWidget( - androidBuilder: (context) { - return ReaderView( - widget.title, - content: Center( - child: ComicReaderContent(widget.title), - ), - buildSettings: (context) => ComicReaderSettings(widget.title), - ); - }, - desktopBuilder: (context) => ReaderView( - widget.title, - content: DragToMoveArea( - child: ComicReaderContent(widget.title), - ), - buildSettings: (context) => ComicReaderSettings(widget.title), - )); + if (Platform.isAndroid) { + return ReaderView( + widget.title, + content: Center( + child: ComicReaderContent(widget.title), + ), + buildSettings: (context) => ComicReaderSettings(widget.title), + ); + } + return ReaderView( + widget.title, + content: DragToMoveArea( + child: ComicReaderContent(widget.title), + ), + buildSettings: (context) => ComicReaderSettings(widget.title), + ); } } From 4bdd9bff47ed0681b6534e82cd09b798d5f2a704 Mon Sep 17 00:00:00 2001 From: MiaoMint <1981324730@qq.com> Date: Mon, 11 Dec 2023 23:16:00 +0800 Subject: [PATCH 17/23] remove backup --- assets/i18n/en.json | 16 +- lib/utils/application.dart | 161 ------------------ lib/utils/miru_storage.dart | 5 - lib/views/pages/settings_page.dart | 53 ------ macos/Flutter/GeneratedPluginRegistrant.swift | 2 - pubspec.lock | 26 +-- pubspec.yaml | 4 +- .../flutter/generated_plugin_registrant.cc | 3 - windows/flutter/generated_plugins.cmake | 1 - 9 files changed, 3 insertions(+), 268 deletions(-) diff --git a/assets/i18n/en.json b/assets/i18n/en.json index 61c84a17..bb4a6177 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -109,11 +109,7 @@ "source-code": "Open Source", "source-code-training": "Give Star", "license": "License", - "license-subtitle": "License", - "export-file":"Export", - "import-file":"Import", - "backup":"Backup", - "backup-subtitle":"Backup or restore data from local" + "license-subtitle": "License" }, "external-player-launching": "Launching {player}", @@ -216,15 +212,5 @@ "stop": "Stop", "upgrade": "Upgrade", "start": "Start" - }, - "backup": { - "import-unzip-success" : "recover success :", - "import-unzip-success-sub":"data recover success please restart the app", - "import-unzip-fail" : "recover fail", - "export-success":"export success", - "export-fail":"export fail", - "export-success-sub":"data export success choose the folder", - "import-filePath-failed":"failed at getting file in this path please switch to the another folder", - "select-folder":"select the folder to store save file" } } diff --git a/lib/utils/application.dart b/lib/utils/application.dart index d2788a2e..bc11c005 100644 --- a/lib/utils/application.dart +++ b/lib/utils/application.dart @@ -1,12 +1,10 @@ // ignore_for_file: use_build_context_synchronously import 'dart:io'; -import 'package:archive/archive.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; -import 'package:miru_app/utils/miru_storage.dart'; import 'package:get/get.dart'; import 'package:miru_app/utils/i18n.dart'; import 'package:miru_app/utils/router.dart'; @@ -15,9 +13,6 @@ import 'package:miru_app/views/widgets/messenger.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart'; -import 'package:file_picker/file_picker.dart'; -import 'package:share_plus/share_plus.dart'; -import 'package:path_provider/path_provider.dart'; late PackageInfo packageInfo; late AndroidDeviceInfo androidDeviceInfo; @@ -140,160 +135,4 @@ class ApplicationUtils { ); } } - - static Future exportSaveFile( - BuildContext context, - ) async { - final directory = await getApplicationDocumentsDirectory(); - final archive = Archive(); - DateTime now = DateTime.now(); - //android backup function - if (Platform.isAndroid) { - bool result = await archivefiles([ - "${directory.path}/miru/default.isar", - "${directory.path}/miru/settings.hive" - ], "${directory.path}/miru-backup-${now.year}-${now.month}-${now.day}_${now.hour}:${now.minute}.zip", - archive, ZipEncoder()); - if (result) { - return showPlatformSnackbar( - context: context, - title: 'backup.export-success'.i18n, - content: "backup.export-success".i18n, - ); - } - return showPlatformSnackbar( - context: context, - title: 'backup.export-failed'.i18n, - content: "backup.export-failed".i18n, - ); - } - //desktop backup function - else { - String? folderPath = await FilePicker.platform - .getDirectoryPath(dialogTitle: "backup.select-folder".i18n); - if (folderPath == null) { - return showPlatformSnackbar( - context: context, - title: 'backup.import-filePath-failed'.i18n, - content: "backup.import-failed".i18n, - ); - } - bool result = await archivefiles([ - "${directory.path}/miru/default.isar", - "${directory.path}/miru/settings.hive" - ], "$folderPath\\miru-backup-${now.year}-${now.month}-${now.day}_${now.hour}-${now.minute}.zip", - archive, ZipEncoder()); - if (result) { - return showPlatformSnackbar( - context: context, - title: 'backup.export-success'.i18n, - content: "backup.export-success".i18n, - ); - } - return showPlatformSnackbar( - context: context, - title: 'backup.export-failed'.i18n, - content: "backup.export-failed".i18n, - ); - } - } - - static Future importSaveFile( - BuildContext context, - ) async { - final directory = await getApplicationDocumentsDirectory(); - FilePickerResult? folderPath = await FilePicker.platform.pickFiles(); - if (folderPath == null) { - return showPlatformSnackbar( - context: context, - title: 'backup.import-filePath-failed'.i18n, - content: "backup.import-failed".i18n, - ); - } - debugPrint("${folderPath.files.single.path}"); - debugPrint("unZip: $directory"); - //clear database - MiruStorage.deleteAll(); - final unZip = await unarchivefiles( - folderPath.files.single.path!, "${directory.path}/miru", ZipDecoder()); - - //unzip succeeded 解壓成功 - if (unZip == null) { - return showPlatformSnackbar( - context: context, - title: 'backup.import-unzip-success'.i18n, - content: "backup.import-unzip-success-sub".i18n, - ); - } - //unzip failed 解壓失敗 - return showPlatformSnackbar( - context: context, - title: 'backup.import-unzip-failed'.i18n, - content: unZip, - ); - } - - static Future copyFile( - String sourcePath, String destinationPath) async { - File sourceFile = File(sourcePath); - File destinationFile = File(destinationPath); - try { - await sourceFile.copy(destinationFile.path); - return true; - } catch (e) { - return false; - } - } - - static Future unarchivefiles( - String path, String targetPath, dynamic encoder) async { - try { - final bytes = await File(path).readAsBytes(); - final archive = ZipDecoder().decodeBytes(bytes); - for (final file in archive) { - final filename = file.name; - if (file.isFile) { - final data = file.content as List; - debugPrint("$path/$filename"); - File("$targetPath/$filename") - ..createSync(recursive: true) - ..writeAsBytesSync(data); - } else { - Directory('$path/$filename').create(recursive: true); - } - } - return; - } catch (e) { - debugPrint("$e"); - return e; - } - } - - static Future archivefiles(List paths, String targetPath, - Archive archive, dynamic encoder) async { - for (final path in paths) { - final bytes = await File(path).readAsBytes(); - final fileName = File(path).path.split('/').last; - final archiveFile = ArchiveFile(fileName, bytes.length, bytes); - archive.addFile(archiveFile); - debugPrint("${bytes.length}"); - } - final output = encoder.encode(archive); - if (output == null) { - debugPrint("encode failed"); - return false; - } - if (Platform.isAndroid) { - File(targetPath).writeAsBytesSync(output); - await Share.shareXFiles( - [XFile(targetPath)], - ); - File(targetPath).delete(); - return true; - } - File(targetPath).writeAsBytesSync(output); - debugPrint(targetPath); - // - return true; - } } diff --git a/lib/utils/miru_storage.dart b/lib/utils/miru_storage.dart index 53efb287..8b393f8d 100644 --- a/lib/utils/miru_storage.dart +++ b/lib/utils/miru_storage.dart @@ -134,11 +134,6 @@ class MiruStorage { static getSetting(String key) { return settings.get(key); } - - static deleteAll() async { - final re = await database.close(deleteFromDisk: true); - debugPrint('Database closed: $re'); - } } class SettingKey { diff --git a/lib/views/pages/settings_page.dart b/lib/views/pages/settings_page.dart index 4582238e..2c3fc71a 100644 --- a/lib/views/pages/settings_page.dart +++ b/lib/views/pages/settings_page.dart @@ -163,59 +163,6 @@ class _SettingsPageState extends State { ), ), const SizedBox(height: 8), - SettingsTile( - icon: const PlatformWidget( - androidWidget: Icon(Icons.backup), - desktopWidget: Icon(fluent.FluentIcons.update_restore, size: 24), - ), - title: 'settings.backup'.i18n, - buildSubtitle: () => FlutterI18n.translate( - context, - 'settings.backup-subtitle', - translationParams: { - 'version': packageInfo.version, - }, - ), - trailing: PlatformWidget( - androidWidget: Row( - mainAxisSize: MainAxisSize.min, - children: [ - TextButton( - onPressed: () { - ApplicationUtils.exportSaveFile(context); - }, - child: Text('settings.export-file'.i18n), - ), - TextButton( - onPressed: () { - ApplicationUtils.importSaveFile(context); - }, - child: Text('settings.import-file'.i18n), - ) - ], - ), - desktopWidget: Row(children: [ - fluent.FilledButton( - onPressed: () { - ApplicationUtils.exportSaveFile( - context, - ); - }, - child: Text('settings.export-file'.i18n), - ), - const SizedBox(width: 16), - fluent.Button( - onPressed: () { - ApplicationUtils.importSaveFile( - context, - ); - }, - child: Text('settings.import-file'.i18n), - ) - ]), - ), - ), - const SizedBox(height: 8), SettingsSwitchTile( icon: const PlatformWidget( androidWidget: Icon(Icons.autorenew_sharp), diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 1249d6d1..f71d65fb 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -16,7 +16,6 @@ import package_info_plus import path_provider_foundation import screen_brightness_macos import screen_retriever -import share_plus import sqflite import url_launcher_macos import wakelock_plus @@ -34,7 +33,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin")) ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) - SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 92ab7f98..9905a1da 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -26,7 +26,7 @@ packages: source: hosted version: "4.0.3" archive: - dependency: "direct main" + dependency: transitive description: name: archive sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b" @@ -225,14 +225,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.8" - cross_file: - dependency: transitive - description: - name: cross_file - sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e - url: "https://pub.dev" - source: hosted - version: "0.3.3+8" crypto: dependency: "direct main" description: @@ -1109,22 +1101,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.8" - share_plus: - dependency: "direct main" - description: - name: share_plus - sha256: f74fc3f1cbd99f39760182e176802f693fa0ec9625c045561cfad54681ea93dd - url: "https://pub.dev" - source: hosted - version: "7.2.1" - share_plus_platform_interface: - dependency: transitive - description: - name: share_plus_platform_interface - sha256: df08bc3a07d01f5ea47b45d03ffcba1fa9cd5370fb44b3f38c70e42cced0f956 - url: "https://pub.dev" - source: hosted - version: "3.3.1" shelf: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 45510610..4325b793 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -57,9 +57,7 @@ dependencies: crypto: ^3.0.3 extended_image: ^8.2.0 flutter_hls_parser: ^2.0.1 - archive: ^3.4.9 - # permission_handler: ^11.1.0 - share_plus: ^7.2.1 + dev_dependencies: flutter_test: sdk: flutter diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 4ed5ca7b..c697e2b2 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -14,7 +14,6 @@ #include #include #include -#include #include #include @@ -35,8 +34,6 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin")); ScreenRetrieverPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ScreenRetrieverPlugin")); - SharePlusWindowsPluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); WindowManagerPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 7d0a2d91..7fbaee51 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -11,7 +11,6 @@ list(APPEND FLUTTER_PLUGIN_LIST media_kit_video screen_brightness_windows screen_retriever - share_plus url_launcher_windows window_manager ) From 2ef68f90d3e4931ed059f2b997dc3835693325de Mon Sep 17 00:00:00 2001 From: appdevelpo <56633229+appdevelpo@users.noreply.github.com> Date: Mon, 11 Dec 2023 23:28:54 +0800 Subject: [PATCH 18/23] delete zooming in desktop ver --- .../reader/comic/comic_reader_content.dart | 169 ++++++++++-------- 1 file changed, 94 insertions(+), 75 deletions(-) diff --git a/lib/views/pages/watch/reader/comic/comic_reader_content.dart b/lib/views/pages/watch/reader/comic/comic_reader_content.dart index f91a02e8..445a1b33 100644 --- a/lib/views/pages/watch/reader/comic/comic_reader_content.dart +++ b/lib/views/pages/watch/reader/comic/comic_reader_content.dart @@ -98,88 +98,107 @@ class _ComicReaderContentState extends State { final cuurentPage = _c.currentPage.value; if (readerType == MangaReadMode.webTonn) { //zooming is inspired by: https://github.com/flutter/flutter/issues/86531 + if (Platform.isAndroid) { + return Stack(children: [ + InteractiveViewer( + minScale: minScaleValue, + // maxScale: 2.0, - return Stack(children: [ - InteractiveViewer( - minScale: minScaleValue, - // maxScale: 2.0, - transformationController: transformationController, - onInteractionEnd: (ScaleEndDetails endDetails) { - setState(() { - isZoomed = false; - }); - }, - onInteractionUpdate: (x) { - double correctScaleValue = - transformationController.value.getMaxScaleOnAxis(); - if (correctScaleValue > minScaleValue) {} - if (x.scale == correctScaleValue) { + transformationController: transformationController, + onInteractionEnd: (ScaleEndDetails endDetails) { setState(() { isZoomed = false; }); - } - setState(() { - isZoomed = true; - }); - debugPrint("${x.scale}"); - }, - child: ScrollablePositionedList.builder( - padding: EdgeInsets.symmetric( - horizontal: viewPadding, - ), - initialScrollIndex: cuurentPage, - itemScrollController: _c.itemScrollController, - itemPositionsListener: _c.itemPositionsListener, - scrollOffsetController: _c.scrollOffsetController, - scrollOffsetListener: _c.scrolloffsetListener, - physics: isZoomed - ? const NeverScrollableScrollPhysics() - : const ScrollPhysics(), - itemBuilder: (context, index) { - final url = images[index]; - return Obx( - () => CacheNetWorkImageComic( - url, - fit: BoxFit.cover, - headers: _c.watchData.value?.headers, - ), - ); }, - itemCount: images.length, + onInteractionUpdate: (x) { + double correctScaleValue = + transformationController.value.getMaxScaleOnAxis(); + if (correctScaleValue > minScaleValue) {} + if (x.scale == correctScaleValue) { + setState(() { + isZoomed = false; + }); + } + setState(() { + isZoomed = true; + }); + debugPrint("${x.scale}"); + }, + child: ScrollablePositionedList.builder( + padding: EdgeInsets.symmetric( + horizontal: viewPadding, + ), + initialScrollIndex: cuurentPage, + itemScrollController: _c.itemScrollController, + itemPositionsListener: _c.itemPositionsListener, + scrollOffsetController: _c.scrollOffsetController, + scrollOffsetListener: _c.scrolloffsetListener, + physics: isZoomed + ? const NeverScrollableScrollPhysics() + : const ScrollPhysics(), + itemBuilder: (context, index) { + final url = images[index]; + return Obx( + () => CacheNetWorkImageComic( + url, + fit: BoxFit.cover, + headers: _c.watchData.value?.headers, + ), + ); + }, + itemCount: images.length, + ), ), + Positioned.fill( + top: 120, + bottom: 120, + child: GestureDetector( + onTapDown: (TapDownDetails details) { + final xPos = details.globalPosition.dx; + if (xPos > Get.width * 4 / 5) { + return _c.nextPage(); + } + if (xPos < Get.width / 5) { + return _c.previousPage(); + } + }, + )), + Container( + alignment: Alignment.bottomCenter, + margin: const EdgeInsets.only(bottom: 40), + child: Container( + decoration: BoxDecoration( + color: Colors.blue, + borderRadius: BorderRadius.circular(40), + ), + alignment: Alignment.center, + width: 200, + height: 30, + child: Text( + "${cuurentPage + 1}/${images.length}", + style: const TextStyle( + color: Colors.white, fontSize: 15), + ))) + ]); + } + return ScrollablePositionedList.builder( + padding: EdgeInsets.symmetric( + horizontal: viewPadding, ), - Positioned.fill( - top: 120, - bottom: 120, - child: GestureDetector( - onTapDown: (TapDownDetails details) { - final xPos = details.globalPosition.dx; - if (xPos > Get.width * 4 / 5) { - return _c.nextPage(); - } - if (xPos < Get.width / 5) { - return _c.previousPage(); - } - }, - )), - Container( - alignment: Alignment.bottomCenter, - margin: const EdgeInsets.only(bottom: 40), - child: Container( - decoration: BoxDecoration( - color: Colors.blue, - borderRadius: BorderRadius.circular(40), - ), - alignment: Alignment.center, - width: 200, - height: 30, - child: Text( - "${cuurentPage + 1}/${images.length}", - style: const TextStyle( - color: Colors.white, fontSize: 15), - ))) - ]); - + initialScrollIndex: cuurentPage, + itemScrollController: _c.itemScrollController, + itemPositionsListener: _c.itemPositionsListener, + scrollOffsetController: _c.scrollOffsetController, + itemBuilder: (context, index) { + final url = images[index]; + return CacheNetWorkImagePic( + url, + fit: BoxFit.fitWidth, + headers: _c.watchData.value?.headers, + ); + }, + itemCount: images.length, + ); //works but lots of bugs // return Stack(children: [ // Obx(() => Transform.scale( From 3ed521a12e0ebf63fc957cf9b54ccc9db79f110e Mon Sep 17 00:00:00 2001 From: MiaoMint <44718819+MiaoMint@users.noreply.github.com> Date: Tue, 12 Dec 2023 19:38:04 +0800 Subject: [PATCH 19/23] Revert "add function for extension" This reverts commit 024599cd6e4a300d7cde685401c482a5ba594717. --- lib/controllers/watch/comic_controller.dart | 39 ------------- lib/data/services/extension_service.dart | 61 +-------------------- lib/models/extension.dart | 18 +----- lib/models/extension.g.dart | 18 ------ 4 files changed, 2 insertions(+), 134 deletions(-) diff --git a/lib/controllers/watch/comic_controller.dart b/lib/controllers/watch/comic_controller.dart index 4da058f9..363a9f9b 100644 --- a/lib/controllers/watch/comic_controller.dart +++ b/lib/controllers/watch/comic_controller.dart @@ -49,7 +49,6 @@ class ComicController extends ReaderController { final pos = itemPositionsListener.itemPositions.value.first; currentPage.value = pos.index; }); - _pageUpdate(); ever(readType, (callback) { _jumpPage(currentPage.value); // 保存设置 @@ -82,31 +81,6 @@ class ComicController extends ReaderController { super.onInit(); } - _pageUpdate() async { - int curPage = currentPage.value; - Timer(const Duration(seconds: 2), () { - //set update timer - Timer.periodic(const Duration(milliseconds: 600), (timer) { - // debugPrint('$curPage'); - if (curPage >= super.watchData.value!.urls.length - 2 || timerCancel) { - timer.cancel(); - } - curPage++; - - if (super.watchData.value!.urls[curPage] == "") { - _getPage(curPage); - } - }); - }); - } - - _getPage(int page) async { - await Future.delayed(const Duration(seconds: 1)); - - final updatePage = await runtime.updatePages(page) as ExtensionUpdatePages; - super.watchData.value!.urls[page] = updatePage.url; - } - _initSetting() async { readType.value = readmode[setting] ?? MangaReadMode.standard; readType.value = await DatabaseService.getMnagaReaderType( @@ -125,18 +99,6 @@ class ComicController extends ReaderController { index: page, ); } - int curPage = currentPage.value; - Timer.periodic(const Duration(milliseconds: 600), (timer) { - // debugPrint('$curPage'); - if (curPage == 0 || timerCancel) { - timer.cancel(); - } - - if (super.watchData.value!.urls[curPage] == "") { - _getPage(curPage); - } - curPage--; - }); return; } if (pageController.value.hasClients) { @@ -196,7 +158,6 @@ class ComicController extends ReaderController { ); } pageController.value.dispose(); - timerCancel = true; super.onClose(); } } diff --git a/lib/data/services/extension_service.dart b/lib/data/services/extension_service.dart index d6815b54..c1fd87af 100644 --- a/lib/data/services/extension_service.dart +++ b/lib/data/services/extension_service.dart @@ -69,26 +69,7 @@ class ExtensionService { )) .data; }); - // request with headers - runtime.onMessage('RequestWithHeader', (dynamic args) async { - ExtensionUtils.addLog( - extension, - ExtensionLogLevel.info, - "RequestWithHeader: ${args[0]} , ${args[1]}", - ); - _cuurentRequestUrl = args[0]; - Response response = await _dio.request( - args[0], - data: args[1]['data'], - queryParameters: args[1]['queryParameters'] ?? {}, - options: Options( - headers: args[1]['headers'] ?? {}, - method: args[1]['method'] ?? 'get', - ), - ); - Map headers = response.headers.map; - return {'data': response.data, 'headers': headers}; - }); + // 设置 runtime.onMessage('registerSetting', (dynamic args) async { args[0]['package'] = extension.package; @@ -314,22 +295,6 @@ class ExtensionService { return res; } } - async RequestWithHeader(url, options) { - options = options || {}; - options.headers = options.headers || {}; - const miruUrl = options.headers["Miru-Url"] || "${extension.webSite}"; - options.method = options.method || "get"; - const res = await sendMessage( - "RequestWithHeader", - JSON.stringify([miruUrl + url, options]) - ); - console.log(res) - try { - return JSON.parse(res); - } catch (e) { - return res; - } - } querySelector(content, selector) { return new Element(content, selector); } @@ -362,9 +327,6 @@ class ExtensionService { } createFilter(filter){ throw new Error("not implement createFilter"); - } - updatePages(){ - } detail(url) { throw new Error("not implement detail"); @@ -541,11 +503,6 @@ class ExtensionService { result.headers ??= { "Referer": _cuurentRequestUrl, }; - if (result.pages == null) { - return result; - } - result.urls.addAll( - List.filled(result.pages! - result.urls.length, "")); return result; default: return ExtensionFikushonWatch.fromJson(data); @@ -553,22 +510,6 @@ class ExtensionService { }); } - Future updatePages(int page) async { - return _runExtension(() async { - final jsResult = await runtime.handlePromise( - await runtime - .evaluateAsync('stringify(()=>extenstion.updatePages("$page"))'), - ); - final data = jsonDecode(jsResult.stringResult); - - final result = ExtensionUpdatePages.fromJson(data); - result.headers ??= { - "Referer": _cuurentRequestUrl, - }; - return result; - }); - } - Future checkUpdate(url) async { return _runExtension(() async { final jsResult = await runtime.handlePromise( diff --git a/lib/models/extension.dart b/lib/models/extension.dart index 05e8e606..1a959364 100644 --- a/lib/models/extension.dart +++ b/lib/models/extension.dart @@ -186,12 +186,11 @@ class ExtensionMangaWatch { ExtensionMangaWatch({ required this.urls, this.headers, - this.pages, }); final List urls; late Map? headers; - final int? pages; + factory ExtensionMangaWatch.fromJson(Map json) => _$ExtensionMangaWatchFromJson(json); @@ -215,21 +214,6 @@ class ExtensionFikushonWatch { Map toJson() => _$ExtensionFikushonWatchToJson(this); } -@JsonSerializable() -class ExtensionUpdatePages { - late Map? headers; - final String url; - ExtensionUpdatePages({ - required this.url, - this.headers, - }); - - factory ExtensionUpdatePages.fromJson(Map json) => - _$ExtensionUpdatePagesFromJson(json); - - Map toJson() => _$ExtensionUpdatePagesToJson(this); -} - @JsonSerializable() class ExtensionLog { ExtensionLog({ diff --git a/lib/models/extension.g.dart b/lib/models/extension.g.dart index dd819fdc..98ba3920 100644 --- a/lib/models/extension.g.dart +++ b/lib/models/extension.g.dart @@ -184,7 +184,6 @@ ExtensionMangaWatch _$ExtensionMangaWatchFromJson(Map json) => headers: (json['headers'] as Map?)?.map( (k, e) => MapEntry(k, e as String), ), - pages: json['pages'] as int?, ); Map _$ExtensionMangaWatchToJson( @@ -192,7 +191,6 @@ Map _$ExtensionMangaWatchToJson( { 'urls': instance.urls, 'headers': instance.headers, - 'pages': instance.pages, }; ExtensionFikushonWatch _$ExtensionFikushonWatchFromJson( @@ -212,22 +210,6 @@ Map _$ExtensionFikushonWatchToJson( 'subtitle': instance.subtitle, }; -ExtensionUpdatePages _$ExtensionUpdatePagesFromJson( - Map json) => - ExtensionUpdatePages( - url: json['url'] as String, - headers: (json['headers'] as Map?)?.map( - (k, e) => MapEntry(k, e as String), - ), - ); - -Map _$ExtensionUpdatePagesToJson( - ExtensionUpdatePages instance) => - { - 'headers': instance.headers, - 'url': instance.url, - }; - ExtensionLog _$ExtensionLogFromJson(Map json) => ExtensionLog( extension: Extension.fromJson(json['extension'] as Map), content: json['content'] as String, From 5b1d9219f30a499a2063d2986589999ac86ef7dc Mon Sep 17 00:00:00 2001 From: appdevelpo <56633229+appdevelpo@users.noreply.github.com> Date: Tue, 12 Dec 2023 20:15:09 +0800 Subject: [PATCH 20/23] delete unnecessary code --- .../reader/comic/comic_reader_content.dart | 2 +- lib/views/widgets/cache_network_image.dart | 37 ------------------- 2 files changed, 1 insertion(+), 38 deletions(-) diff --git a/lib/views/pages/watch/reader/comic/comic_reader_content.dart b/lib/views/pages/watch/reader/comic/comic_reader_content.dart index 445a1b33..779c7389 100644 --- a/lib/views/pages/watch/reader/comic/comic_reader_content.dart +++ b/lib/views/pages/watch/reader/comic/comic_reader_content.dart @@ -139,7 +139,7 @@ class _ComicReaderContentState extends State { itemBuilder: (context, index) { final url = images[index]; return Obx( - () => CacheNetWorkImageComic( + () => CacheNetWorkImagePic( url, fit: BoxFit.cover, headers: _c.watchData.value?.headers, diff --git a/lib/views/widgets/cache_network_image.dart b/lib/views/widgets/cache_network_image.dart index 21525cac..ee0e543e 100644 --- a/lib/views/widgets/cache_network_image.dart +++ b/lib/views/widgets/cache_network_image.dart @@ -38,40 +38,3 @@ class CacheNetWorkImagePic extends StatelessWidget { ); } } - -class CacheNetWorkImageComic extends StatelessWidget { - const CacheNetWorkImageComic( - this.url, { - super.key, - this.fit = BoxFit.cover, - this.width, - this.height, - this.fallback, - this.headers, - }); - final String url; - final BoxFit fit; - final double? width; - final double? height; - final Widget? fallback; - final Map? headers; - - _errorBuild() { - if (fallback != null) { - return fallback!; - } - return const Center(child: Icon(fluent.FluentIcons.error)); - } - - @override - Widget build(BuildContext context) { - return CachedNetworkImage( - imageUrl: url, - httpHeaders: headers, - fit: fit, - width: width, - height: height, - errorWidget: (context, url, error) => _errorBuild(), - ); - } -} From 8669004668b2517973bbcdd50fa34ffa88914ce9 Mon Sep 17 00:00:00 2001 From: MiaoMint <44718819+MiaoMint@users.noreply.github.com> Date: Tue, 12 Dec 2023 21:41:16 +0800 Subject: [PATCH 21/23] Optimise video quality selection ui --- lib/controllers/watch/comic_controller.dart | 1 - lib/controllers/watch/video_controller.dart | 8 +- .../watch/video/video_player_content.dart | 172 ++++++++---------- 3 files changed, 77 insertions(+), 104 deletions(-) diff --git a/lib/controllers/watch/comic_controller.dart b/lib/controllers/watch/comic_controller.dart index 363a9f9b..7ed5abec 100644 --- a/lib/controllers/watch/comic_controller.dart +++ b/lib/controllers/watch/comic_controller.dart @@ -4,7 +4,6 @@ import 'package:miru_app/models/index.dart'; import 'package:miru_app/controllers/watch/reader_controller.dart'; import 'package:miru_app/data/services/database_service.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; -import 'dart:async'; import 'package:extended_image/extended_image.dart'; import 'package:miru_app/utils/miru_storage.dart'; diff --git a/lib/controllers/watch/video_controller.dart b/lib/controllers/watch/video_controller.dart index c16ccf67..7db55ce8 100644 --- a/lib/controllers/watch/video_controller.dart +++ b/lib/controllers/watch/video_controller.dart @@ -58,7 +58,7 @@ class VideoPlayerController extends GetxController { final subtitles = [].obs; final keyboardShortcuts = {}; final selectedSubtitle = 0.obs; - final currentQality = "null".obs; + final currentQality = "".obs; final qualityUrls = {}; // 是否已经自动跳转到上次播放进度 bool _isAutoSeekPosition = false; @@ -177,8 +177,10 @@ class VideoPlayerController extends GetxController { }); //讀取現在的畫質 player.stream.height.listen((event) async { - final width = player.state.width; - currentQality.value = "${width}x$event"; + if (player.state.width != null) { + final width = player.state.width; + currentQality.value = "${width}x$event"; + } }); // 自动恢复上次播放进度 player.stream.duration.listen((event) async { diff --git a/lib/views/pages/watch/video/video_player_content.dart b/lib/views/pages/watch/video/video_player_content.dart index 3bf13267..21e0e0ce 100644 --- a/lib/views/pages/watch/video/video_player_content.dart +++ b/lib/views/pages/watch/video/video_player_content.dart @@ -94,7 +94,7 @@ class _VideoPlayerContenState extends State { const MaterialDesktopPositionIndicator(), const Spacer(), Theme( - data: Theme.of(context), + data: ThemeData.dark(useMaterial3: true), child: Row( children: [ Padding( @@ -132,7 +132,8 @@ class _VideoPlayerContenState extends State { return [ for (int i = 0; i < _c.torrentMediaFileList.length; i++) PopupMenuItem( - padding: const EdgeInsets.all(0), + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 0), child: Obx( () => CheckboxListTile( value: _c.currentTorrentFile.value == @@ -159,7 +160,8 @@ class _VideoPlayerContenState extends State { return [ // 是否显示字幕 PopupMenuItem( - padding: const EdgeInsets.all(0), + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 0), child: Obx( () => CheckboxListTile( value: _c.selectedSubtitle.value == -1, @@ -172,7 +174,8 @@ class _VideoPlayerContenState extends State { ), // 选择文件 PopupMenuItem( - padding: const EdgeInsets.all(0), + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 0), child: Obx( () => CheckboxListTile( value: _c.selectedSubtitle.value == -2, @@ -185,7 +188,8 @@ class _VideoPlayerContenState extends State { ), for (int i = 0; i < _c.subtitles.length; i++) PopupMenuItem( - padding: const EdgeInsets.all(0), + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 0), child: Obx( () => CheckboxListTile( value: _c.selectedSubtitle.value == i, @@ -199,51 +203,33 @@ class _VideoPlayerContenState extends State { ]; }, ), - TextButton( - onPressed: () { - fluent.showDialog( - context: context, - builder: (contex) { - return fluent.ContentDialog( - title: Text("choose-quality".i18n), - content: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - for (final q in _c.qualityUrls.entries) - Row(children: [ - fluent.RadioButton( - checked: selected == q.key || - q.key == _c.currentQality.value, - onChanged: (checked) { - // debugPrint("$boolean"); - setState(() { - if (checked) { - selected = q.key; - _c.switchQuality( - _c.qualityUrls[q.key]!); - Navigator.pop(context); - } - }); - }), - Text( - q.key, - style: const TextStyle( - fontSize: 20, color: Colors.white), - ), - ]) - ], - ), - actions: [ - fluent.Button( - onPressed: () => Navigator.pop(context), - child: Text("cancel".i18n)) - ], - ); - }); - }, - child: Obx(() => (Text(_c.currentQality.value, - style: const TextStyle(color: Colors.white))))) + Obx(() { + if (_c.currentQality.value.isEmpty) { + return const SizedBox.shrink(); + } + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: PopupMenuButton( + child: Obx( + () => Text( + _c.currentQality.value, + style: const TextStyle(color: Colors.white), + ), + ), + itemBuilder: (context) { + return [ + for (final qualit in _c.qualityUrls.entries) + PopupMenuItem( + child: Text(qualit.key), + onTap: () { + _c.switchQuality(_c.qualityUrls[qualit.key]!); + }, + ), + ]; + }, + ), + ); + }) ], ), ), @@ -327,51 +313,6 @@ class _VideoPlayerContenState extends State { data: Theme.of(context), child: Row( children: [ - SizedBox( - // height: 8, - // width: 10, - child: TextButton( - onPressed: () { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text("choose-quality".i18n), - content: Column( - children: [ - for (final q in _c.qualityUrls.entries) - RadioListTile( - title: Text(q.key), - value: q.value, - groupValue: _c.qualityUrls[ - _c.currentQality.value], - onChanged: (value) { - Navigator.pop(context); - // widget.applyValue(value as T); - debugPrint( - "$value value changed"); - - _c.currentQality.value = _c - .qualityUrls.keys - .firstWhere( - (element) => - _c.qualityUrls[ - element] == - value, - orElse: () => _c - .qualityUrls - .keys - .first); - setState(() {}); - _c.switchQuality(value!); - }, - ), - ], - ), - )); - }, - child: Obx(() => (Text(_c.currentQality.value, - style: const TextStyle(color: Colors.white)))))), - const SizedBox(width: 2), Padding( padding: const EdgeInsets.only(right: 8.0), child: PopupMenuButton( @@ -407,7 +348,8 @@ class _VideoPlayerContenState extends State { return [ for (int i = 0; i < _c.torrentMediaFileList.length; i++) PopupMenuItem( - padding: const EdgeInsets.all(0), + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 0), child: Obx( () => CheckboxListTile( value: _c.currentTorrentFile.value == @@ -434,7 +376,8 @@ class _VideoPlayerContenState extends State { return [ // 是否显示字幕 PopupMenuItem( - padding: const EdgeInsets.all(0), + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 0), child: Obx( () => CheckboxListTile( value: _c.selectedSubtitle.value == -1, @@ -447,7 +390,8 @@ class _VideoPlayerContenState extends State { ), // 选择文件 PopupMenuItem( - padding: const EdgeInsets.all(0), + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 0), child: Obx( () => CheckboxListTile( value: _c.selectedSubtitle.value == -2, @@ -460,7 +404,8 @@ class _VideoPlayerContenState extends State { ), for (int i = 0; i < _c.subtitles.length; i++) PopupMenuItem( - padding: const EdgeInsets.all(0), + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 0), child: Obx( () => CheckboxListTile( value: _c.selectedSubtitle.value == i, @@ -473,7 +418,34 @@ class _VideoPlayerContenState extends State { ), ]; }, - ) + ), + Obx(() { + if (_c.currentQality.value.isEmpty) { + return const SizedBox.shrink(); + } + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: PopupMenuButton( + child: Obx( + () => Text( + _c.currentQality.value, + style: const TextStyle(color: Colors.white), + ), + ), + itemBuilder: (context) { + return [ + for (final qualit in _c.qualityUrls.entries) + PopupMenuItem( + child: Text(qualit.key), + onTap: () { + _c.switchQuality(_c.qualityUrls[qualit.key]!); + }, + ), + ]; + }, + ), + ); + }), ], ), ), From 0b86fa86a509e904fb821633c5f2b76213dd33e1 Mon Sep 17 00:00:00 2001 From: appdevelpo <56633229+appdevelpo@users.noreply.github.com> Date: Tue, 12 Dec 2023 22:30:44 +0800 Subject: [PATCH 22/23] fix index bug --- lib/views/pages/watch/reader/comic/comic_reader_content.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/views/pages/watch/reader/comic/comic_reader_content.dart b/lib/views/pages/watch/reader/comic/comic_reader_content.dart index 779c7389..60fd8db6 100644 --- a/lib/views/pages/watch/reader/comic/comic_reader_content.dart +++ b/lib/views/pages/watch/reader/comic/comic_reader_content.dart @@ -324,6 +324,7 @@ class _ComicReaderContentState extends State { //common mode and left to right mode return Stack(children: [ ExtendedImageGesturePageView.builder( + itemCount: images.length, reverse: readerType == MangaReadMode.rightToLeft, onPageChanged: (index) { _c.currentPage.value = index; From d000ea33bf59e5556c8a46f32aae9ba1d7ade650 Mon Sep 17 00:00:00 2001 From: MiaoMint <44718819+MiaoMint@users.noreply.github.com> Date: Tue, 12 Dec 2023 22:31:22 +0800 Subject: [PATCH 23/23] Optimise skipping interval setting ui --- lib/utils/miru_storage.dart | 2 -- lib/views/pages/settings_page.dart | 25 ++++++++----------- .../widgets/settings_numberbox_button.dart | 1 + 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/lib/utils/miru_storage.dart b/lib/utils/miru_storage.dart index 8b393f8d..1d6aa795 100644 --- a/lib/utils/miru_storage.dart +++ b/lib/utils/miru_storage.dart @@ -113,7 +113,6 @@ class MiruStorage { await _initSetting(SettingKey.enableNSFW, false); await _initSetting(SettingKey.videoPlayer, 'built-in'); await _initSetting(SettingKey.listMode, "grid"); - await _initSetting(SettingKey.skipInterval, "10000"); await _initSetting(SettingKey.keyI, 10.0); await _initSetting(SettingKey.keyJ, -10.0); await _initSetting(SettingKey.arrowLeft, -2.0); @@ -147,7 +146,6 @@ class SettingKey { static String videoPlayer = 'VideoPlayer'; static String databaseVersion = 'DatabaseVersion'; static String listMode = 'ListMode'; - static String skipInterval = 'SkipInterval'; static String keyI = 'KeyI'; static String keyJ = 'KeyJ'; static String arrowLeft = 'Arrowleft'; diff --git a/lib/views/pages/settings_page.dart b/lib/views/pages/settings_page.dart index 2c3fc71a..ff5441c3 100644 --- a/lib/views/pages/settings_page.dart +++ b/lib/views/pages/settings_page.dart @@ -324,23 +324,18 @@ class _SettingsPageState extends State { const SizedBox(height: 8), if (!Platform.isAndroid) fluent.Expander( - header: Row( + leading: const Icon(fluent.FluentIcons.keyboard_classic, size: 24), + header: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Icon(fluent.FluentIcons.keyboard_classic, size: 24), - const SizedBox(width: 16), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("settings.skip-interval".i18n), - const SizedBox(height: 2), - Text( - "settings.skip-interval-subtitle".i18n, - style: const TextStyle(fontSize: 12), - ) - ], + const SizedBox(height: 10), + Text("settings.skip-interval".i18n), + const SizedBox(height: 2), + Text( + "settings.skip-interval-subtitle".i18n, + style: const TextStyle(fontSize: 12), ), - const Spacer(), - const SizedBox(), + const SizedBox(height: 15) ], ), content: Column( diff --git a/lib/views/widgets/settings_numberbox_button.dart b/lib/views/widgets/settings_numberbox_button.dart index 57792f1d..633284dd 100644 --- a/lib/views/widgets/settings_numberbox_button.dart +++ b/lib/views/widgets/settings_numberbox_button.dart @@ -44,6 +44,7 @@ class _SettingsIntpuTileState extends fluent.State { mode: fluent.SpinButtonPlacementMode.inline, ), )), + const SizedBox(width: 8), if (!buttonSwitch) fluent.Button( child: Text(widget.button1text),