From 3cdd40a710b81efb66cefb60780155a3db85081d Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Fri, 28 Mar 2025 17:32:08 +0800 Subject: [PATCH] feat: create note related #554 Signed-off-by: bggRGjQaUbCoE --- lib/pages/video/detail/controller.dart | 8 +- .../video/detail/note/note_list_page.dart | 97 ++++++--- lib/pages/webview/webview_page.dart | 205 ++++++++++-------- 3 files changed, 191 insertions(+), 119 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index ef50b9b2..05ef8908 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -1632,19 +1632,25 @@ class VideoDetailController extends GetxController oid: oid.value, enableSlide: false, heroTag: heroTag, + isStein: graphVersion != null, ), ) : NoteListPage( oid: oid.value, enableSlide: false, heroTag: heroTag, + isStein: graphVersion != null, ), isFullScreen: () => plPlayerController.isFullScreen.value, ); } else { childKey.currentState?.showBottomSheet( backgroundColor: Colors.transparent, - (context) => NoteListPage(oid: oid.value, heroTag: heroTag), + (context) => NoteListPage( + oid: oid.value, + heroTag: heroTag, + isStein: graphVersion != null, + ), ); } } diff --git a/lib/pages/video/detail/note/note_list_page.dart b/lib/pages/video/detail/note/note_list_page.dart index 06f396c9..7f40fefd 100644 --- a/lib/pages/video/detail/note/note_list_page.dart +++ b/lib/pages/video/detail/note/note_list_page.dart @@ -6,6 +6,7 @@ import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/pages/common/common_slide_page.dart'; import 'package:PiliPlus/pages/video/detail/note/note_list_page_ctr.dart'; +import 'package:PiliPlus/pages/webview/webview_page.dart'; import 'package:PiliPlus/utils/app_scheme.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:flutter/material.dart'; @@ -18,11 +19,13 @@ class NoteListPage extends CommonSlidePage { required this.heroTag, this.oid, this.upperMid, + required this.isStein, }); final dynamic heroTag; final dynamic oid; final dynamic upperMid; + final bool isStein; @override State createState() => _NoteListPageState(); @@ -34,6 +37,8 @@ class _NoteListPageState extends CommonSlidePageState { tag: widget.heroTag, ); + final _key = GlobalKey(); + @override void dispose() { Get.delete(tag: widget.heroTag); @@ -42,35 +47,77 @@ class _NoteListPageState extends CommonSlidePageState { @override Widget get buildPage => Scaffold( - appBar: AppBar( - automaticallyImplyLeading: false, - titleSpacing: 16, - toolbarHeight: 45, - title: Obx( - () => Text( - '笔记${_controller.count.value == -1 ? '' : '(${_controller.count.value})'}'), + key: _key, + resizeToAvoidBottomInset: false, + body: Scaffold( + backgroundColor: Colors.transparent, + resizeToAvoidBottomInset: false, + appBar: AppBar( + automaticallyImplyLeading: false, + titleSpacing: 16, + toolbarHeight: 45, + title: Obx( + () => Text( + '笔记${_controller.count.value == -1 ? '' : '(${_controller.count.value})'}'), + ), + bottom: PreferredSize( + preferredSize: Size.fromHeight(1), + child: Divider( + height: 1, + color: Theme.of(context).colorScheme.outline.withOpacity(0.1), + ), + ), + actions: [ + iconButton( + context: context, + tooltip: '关闭', + icon: Icons.clear, + onPressed: Get.back, + size: 32, + ), + const SizedBox(width: 16), + ], ), - bottom: PreferredSize( - preferredSize: Size.fromHeight(1), - child: Divider( - height: 1, - color: Theme.of(context).colorScheme.outline.withOpacity(0.1), + body: enableSlide + ? slideList(Obx(() => _buildBody(_controller.loadingState.value))) + : Obx(() => _buildBody(_controller.loadingState.value)), + bottomNavigationBar: Container( + padding: EdgeInsets.only( + left: 12, + right: 12, + top: 6, + bottom: MediaQuery.paddingOf(context).bottom + 6, + ), + width: double.infinity, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.onInverseSurface, + border: Border( + top: BorderSide( + width: 0.5, + color: Theme.of(context).colorScheme.outline.withOpacity(0.1), + ), + ), + ), + child: FilledButton.tonal( + style: FilledButton.styleFrom( + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + padding: EdgeInsets.zero, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + ), + onPressed: () { + _key.currentState?.showBottomSheet( + (context) => WebviewPageNew( + url: + 'https://www.bilibili.com/h5/note-app?oid=${widget.oid}&pagefrom=ugcvideo&is_stein_gate=${widget.isStein ? 1 : 0}', + ), + ); + }, + child: const Text('开始记笔记'), ), ), - actions: [ - iconButton( - context: context, - tooltip: '关闭', - icon: Icons.clear, - onPressed: Get.back, - size: 32, - ), - const SizedBox(width: 16), - ], ), - body: enableSlide - ? slideList(Obx(() => _buildBody(_controller.loadingState.value))) - : Obx(() => _buildBody(_controller.loadingState.value)), ); Widget _buildBody(LoadingState loadingState) { diff --git a/lib/pages/webview/webview_page.dart b/lib/pages/webview/webview_page.dart index 8a1a9320..50575c13 100644 --- a/lib/pages/webview/webview_page.dart +++ b/lib/pages/webview/webview_page.dart @@ -33,14 +33,16 @@ extension _WebviewMenuItemExt on _WebviewMenuItem { } class WebviewPageNew extends StatefulWidget { - const WebviewPageNew({super.key}); + const WebviewPageNew({super.key, this.url}); + + final String? url; @override State createState() => _WebviewPageNewState(); } class _WebviewPageNewState extends State { - final String _url = Get.parameters['url'] ?? ''; + late final String _url = widget.url ?? Get.parameters['url'] ?? ''; final uaType = Get.parameters['uaType'] ?? 'mob'; final _titleStream = StreamController(); final _progressStream = StreamController(); @@ -69,97 +71,99 @@ class _WebviewPageNewState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: StreamBuilder( - initialData: null, - stream: _titleStream.stream, - builder: (context, snapshot) => Text( - maxLines: 1, - snapshot.hasData ? snapshot.data! : _url, - overflow: TextOverflow.ellipsis, - ), - ), - bottom: PreferredSize( - preferredSize: Size.zero, - child: StreamBuilder( - initialData: 0.0, - stream: _progressStream.stream, - builder: (context, snapshot) => snapshot.data as double < 1 - ? LinearProgressIndicator( - value: snapshot.data as double, - ) - : const SizedBox.shrink(), - ), - ), - actions: [ - PopupMenuButton( - onSelected: (item) async { - switch (item) { - case _WebviewMenuItem.refresh: - _webViewController?.reload(); - break; - case _WebviewMenuItem.copy: - WebUri? uri = await _webViewController?.getUrl(); - if (uri != null) { - Utils.copyText(uri.toString()); - } - break; - case _WebviewMenuItem.openInBrowser: - WebUri? uri = await _webViewController?.getUrl(); - if (uri != null) { - Utils.launchURL(uri.toString()); - } - break; - case _WebviewMenuItem.clearCache: - try { - await InAppWebViewController.clearAllCache(); - await _webViewController?.clearHistory(); - SmartDialog.showToast('已清理'); - } catch (e) { - SmartDialog.showToast(e.toString()); - } - break; - case _WebviewMenuItem.goBack: - if (await _webViewController?.canGoBack() == true) { - _webViewController?.goBack(); - } else { - Get.back(); - } - break; - case _WebviewMenuItem.resetCookie: - final cookies = Accounts.main.cookieJar.toList(); - for (var item in cookies) { - await CookieManager().setCookie( - url: WebUri(item.domain ?? ''), - name: item.name, - value: item.value, - path: item.path ?? '', - domain: item.domain, - isSecure: item.secure, - isHttpOnly: item.httpOnly, - ); - } - SmartDialog.showToast('设置成功,刷新或重新打开网页'); - break; - } - }, - itemBuilder: (context) => >[ - ..._WebviewMenuItem.values - .sublist(0, _WebviewMenuItem.values.length - 1) - .map((item) => - PopupMenuItem(value: item, child: Text(item.title))), - const PopupMenuDivider(), - PopupMenuItem( - value: _WebviewMenuItem.goBack, - child: Text( - _WebviewMenuItem.goBack.title, - style: - TextStyle(color: Theme.of(context).colorScheme.error), - )), - ], - ) - ], - ), + appBar: widget.url != null + ? null + : AppBar( + title: StreamBuilder( + initialData: null, + stream: _titleStream.stream, + builder: (context, snapshot) => Text( + maxLines: 1, + snapshot.hasData ? snapshot.data! : _url, + overflow: TextOverflow.ellipsis, + ), + ), + bottom: PreferredSize( + preferredSize: Size.zero, + child: StreamBuilder( + initialData: 0.0, + stream: _progressStream.stream, + builder: (context, snapshot) => snapshot.data as double < 1 + ? LinearProgressIndicator( + value: snapshot.data as double, + ) + : const SizedBox.shrink(), + ), + ), + actions: [ + PopupMenuButton( + onSelected: (item) async { + switch (item) { + case _WebviewMenuItem.refresh: + _webViewController?.reload(); + break; + case _WebviewMenuItem.copy: + WebUri? uri = await _webViewController?.getUrl(); + if (uri != null) { + Utils.copyText(uri.toString()); + } + break; + case _WebviewMenuItem.openInBrowser: + WebUri? uri = await _webViewController?.getUrl(); + if (uri != null) { + Utils.launchURL(uri.toString()); + } + break; + case _WebviewMenuItem.clearCache: + try { + await InAppWebViewController.clearAllCache(); + await _webViewController?.clearHistory(); + SmartDialog.showToast('已清理'); + } catch (e) { + SmartDialog.showToast(e.toString()); + } + break; + case _WebviewMenuItem.goBack: + if (await _webViewController?.canGoBack() == true) { + _webViewController?.goBack(); + } else { + Get.back(); + } + break; + case _WebviewMenuItem.resetCookie: + final cookies = Accounts.main.cookieJar.toList(); + for (var item in cookies) { + await CookieManager().setCookie( + url: WebUri(item.domain ?? ''), + name: item.name, + value: item.value, + path: item.path ?? '', + domain: item.domain, + isSecure: item.secure, + isHttpOnly: item.httpOnly, + ); + } + SmartDialog.showToast('设置成功,刷新或重新打开网页'); + break; + } + }, + itemBuilder: (context) => >[ + ..._WebviewMenuItem.values + .sublist(0, _WebviewMenuItem.values.length - 1) + .map((item) => PopupMenuItem( + value: item, child: Text(item.title))), + const PopupMenuDivider(), + PopupMenuItem( + value: _WebviewMenuItem.goBack, + child: Text( + _WebviewMenuItem.goBack.title, + style: TextStyle( + color: Theme.of(context).colorScheme.error), + )), + ], + ) + ], + ), body: SafeArea( child: InAppWebView( initialSettings: InAppWebViewSettings( @@ -176,6 +180,12 @@ class _WebviewPageNewState extends State { URLRequest(url: WebUri.uri(Uri.tryParse(_url) ?? Uri())), onWebViewCreated: (InAppWebViewController controller) { _webViewController = controller; + _webViewController?.addJavaScriptHandler( + handlerName: 'finishButtonClicked', + callback: (args) { + Get.back(); + }, + ); }, onProgressChanged: (controller, progress) { _progressStream.add(progress / 100); @@ -185,6 +195,15 @@ class _WebviewPageNewState extends State { }, onCloseWindow: (controller) => Get.back(), onLoadStop: (controller, url) { + if (url + .toString() + .startsWith('https://www.bilibili.com/h5/note-app')) { + _webViewController?.evaluateJavascript(source: """ + document.querySelector('.finish-btn').addEventListener('click', function() { + window.flutter_inappwebview.callHandler('finishButtonClicked'); + }); +"""); + } if (url.toString().startsWith('https://live.bilibili.com')) { _webViewController?.evaluateJavascript( source: '''