diff --git a/README.md b/README.md index 2e8bf871d..37a2e36fa 100644 --- a/README.md +++ b/README.md @@ -22,22 +22,18 @@
-## 开发环境 - -```bash -[✓] Flutter (Channel stable, 3.24.0, on Microsoft Windows [版本 10.0.19045.4046], locale zh-CN) -[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0) -[✓] Xcode - develop for iOS and macOS (Xcode 15.1) -[✓] Chrome - develop for the web -[✓] Android Studio (version 2022.3) -[✓] VS Code (version 1.85.1) -[✓] Connected device (3 available) -[✓] Network resources - -```
+## 适配平台 + +- [x] Android +- [x] iOS +- [x] Pad +- [x] Windows + + +
## refactor @@ -136,11 +132,6 @@ ## 功能 -目前着重移动端(Android、iOS)和Pad端,暂时没有适配桌面端、手表端等 - -
- - - [x] 推荐视频列表(app端) - [x] 最热视频列表 - [x] 热门直播 diff --git a/assets/images/logo/logo_large.png b/assets/images/logo/logo_large.png new file mode 100644 index 000000000..2370686ba Binary files /dev/null and b/assets/images/logo/logo_large.png differ diff --git a/lib/pages/emote/view.dart b/lib/pages/emote/view.dart index c406afa34..7ceb19100 100644 --- a/lib/pages/emote/view.dart +++ b/lib/pages/emote/view.dart @@ -42,9 +42,11 @@ class _EmotePanelState extends State ThemeData theme, LoadingState?> loadingState, ) { - late final color = Get.currentRoute.startsWith('/whisperDetail') - ? theme.colorScheme.surface - : theme.colorScheme.onInverseSurface; + late final color = ElevationOverlay.colorWithOverlay( + theme.colorScheme.surface, + theme.hoverColor, + Get.currentRoute.startsWith('/whisperDetail') ? 8 : 2, + ); return switch (loadingState) { Loading() => loadingWidget, Success(:var response) => diff --git a/lib/pages/episode_panel/view.dart b/lib/pages/episode_panel/view.dart index 46a14db34..3873f4966 100644 --- a/lib/pages/episode_panel/view.dart +++ b/lib/pages/episode_panel/view.dart @@ -74,7 +74,7 @@ class EpisodePanel extends CommonSlidePage { final int initialTabIndex; final bool? isSupportReverse; final bool? isReversed; - final ValueChanged onChangeEpisode; + final Future Function(ugc.BaseEpisodeItem) onChangeEpisode; final VoidCallback? onReverse; final VoidCallback? onClose; @@ -432,19 +432,23 @@ class _EpisodePanelState extends State } SmartDialog.showToast('切换到:$title'); widget.onClose?.call(); - if (!showTitle) { - _currentItemIndex = index; - } - widget.onChangeEpisode(episode); - if (widget.type == EpisodeType.season) { - try { - Get.find( - tag: widget.ugcIntroController!.heroTag, - ).seasonCid = episode.cid; - } catch (_) { - if (kDebugMode) rethrow; + + widget.onChangeEpisode(episode).then((res) { + if (res) { + if (!showTitle) { + _currentItemIndex = index; + } + if (widget.type == EpisodeType.season) { + try { + Get.find( + tag: widget.ugcIntroController!.heroTag, + ).seasonCid = episode.cid; + } catch (_) { + if (kDebugMode) rethrow; + } + } } - } + }); }, onLongPress: () { if (cover?.isNotEmpty == true) { diff --git a/lib/pages/fav_create/view.dart b/lib/pages/fav_create/view.dart index 8a703f5a0..22b74d8c9 100644 --- a/lib/pages/fav_create/view.dart +++ b/lib/pages/fav_create/view.dart @@ -259,57 +259,58 @@ class _CreateFavPageState extends State { ], ListTile( tileColor: theme.colorScheme.onInverseSurface, - leading: Text.rich( - style: const TextStyle( - height: 1, - fontSize: 14, - ), - TextSpan( - children: [ - TextSpan( - text: '*', - style: TextStyle( - fontSize: 14, - height: 1, - color: theme.colorScheme.error, + title: Row( + children: [ + SizedBox( + width: 55, + child: Text.rich( + TextSpan( + children: [ + TextSpan( + text: '*', + style: TextStyle( + fontSize: 14, + color: theme.colorScheme.error, + ), + ), + const TextSpan( + text: '名称', + style: TextStyle(fontSize: 14), + ), + ], ), ), - const TextSpan( - text: '名称', + ), + Expanded( + child: TextField( + autofocus: true, + readOnly: _attr != null && FavUtils.isDefaultFav(_attr!), + controller: _titleController, style: TextStyle( - height: 1, fontSize: 14, + color: _attr != null && FavUtils.isDefaultFav(_attr!) + ? theme.colorScheme.outline + : null, + ), + inputFormatters: [ + LengthLimitingTextInputFormatter(20), + ], + decoration: InputDecoration( + isDense: true, + hintText: '名称', + hintStyle: TextStyle( + fontSize: 14, + color: theme.colorScheme.outline, + ), + border: const OutlineInputBorder( + borderSide: BorderSide.none, + gapPadding: 0, + ), + contentPadding: EdgeInsets.zero, ), ), - ], - ), - ), - title: TextField( - autofocus: true, - readOnly: _attr != null && FavUtils.isDefaultFav(_attr!), - controller: _titleController, - style: TextStyle( - fontSize: 14, - color: _attr != null && FavUtils.isDefaultFav(_attr!) - ? theme.colorScheme.outline - : null, - ), - inputFormatters: [ - LengthLimitingTextInputFormatter(20), + ), ], - decoration: InputDecoration( - isDense: true, - hintText: '名称', - hintStyle: TextStyle( - fontSize: 14, - color: theme.colorScheme.outline, - ), - border: const OutlineInputBorder( - borderSide: BorderSide.none, - gapPadding: 0, - ), - contentPadding: EdgeInsets.zero, - ), ), ), const SizedBox(height: 16), @@ -319,24 +320,16 @@ class _CreateFavPageState extends State { title: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text.rich( - TextSpan( - children: [ - TextSpan( - text: '简介', - style: TextStyle( - fontSize: 14, - color: theme.colorScheme.onSurfaceVariant, - ), - ), - const TextSpan( - text: '*', - style: TextStyle(color: Colors.transparent), - ), - ], + SizedBox( + width: 55, + child: Text( + '简介', + style: TextStyle( + fontSize: 14, + color: theme.colorScheme.onSurfaceVariant, + ), ), ), - const SizedBox(width: 16), Expanded( child: TextField( minLines: 6, diff --git a/lib/pages/history/view.dart b/lib/pages/history/view.dart index f2ad12127..4f2fdf10a 100644 --- a/lib/pages/history/view.dart +++ b/lib/pages/history/view.dart @@ -254,20 +254,25 @@ class _HistoryPageState extends State padding: const EdgeInsets.only(left: 16, right: 6), child: Row( children: [ - Icon( - Icons.info_outline, - size: 18, - color: theme.onSecondaryContainer, - ), - const SizedBox(width: 4), Expanded( - child: Text( - '历史记录功能已关闭', + child: Text.rich( strutStyle: const StrutStyle(height: 1, leading: 0), style: TextStyle( height: 1, color: theme.onSecondaryContainer, ), + TextSpan( + children: [ + WidgetSpan( + child: Icon( + Icons.info_outline, + size: 18, + color: theme.onSecondaryContainer, + ), + ), + const TextSpan(text: ' 历史记录功能已关闭'), + ], + ), ), ), GestureDetector( diff --git a/lib/pages/live_emote/view.dart b/lib/pages/live_emote/view.dart index fcc4cae73..535207c6a 100644 --- a/lib/pages/live_emote/view.dart +++ b/lib/pages/live_emote/view.dart @@ -45,7 +45,11 @@ class _LiveEmotePanelState extends State Widget _buildBody(LoadingState?> loadingState) { late final theme = Theme.of(context); - late final color = theme.colorScheme.onInverseSurface; + late final color = ElevationOverlay.colorWithOverlay( + theme.colorScheme.surface, + theme.hoverColor, + 2, + ); return switch (loadingState) { Loading() => loadingWidget, Success(:var response) => diff --git a/lib/pages/live_room/widgets/chat_panel.dart b/lib/pages/live_room/widgets/chat_panel.dart index 9b490a93f..1e0d486b0 100644 --- a/lib/pages/live_room/widgets/chat_panel.dart +++ b/lib/pages/live_room/widgets/chat_panel.dart @@ -140,7 +140,7 @@ class LiveRoomChatPanel extends StatelessWidget { ), padding: const EdgeInsets.fromLTRB(10, 4, 4, 4), child: Text.rich( - style: const TextStyle(color: Colors.white), + style: const TextStyle(color: Colors.white, height: 1), strutStyle: const StrutStyle(height: 1, leading: 0), TextSpan( children: [ diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index 158002011..6e4bec5f8 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -51,8 +51,12 @@ class _MainAppState extends State @override void didChangeDependencies() { super.didChangeDependencies(); + final brightness = Theme.brightnessOf(context); NetworkImgLayer.reduce = - NetworkImgLayer.reduceLuxColor != null && context.isDarkMode; + NetworkImgLayer.reduceLuxColor != null && brightness.isDark; + if (Utils.isDesktop) { + windowManager.setBrightness(brightness); + } PageUtils.routeObserver.subscribe( this, ModalRoute.of(context) as PageRoute, @@ -160,6 +164,8 @@ class _MainAppState extends State Future _handleTray() async { if (Platform.isWindows) { await trayManager.setIcon('assets/images/logo/app_icon.ico'); + } else { + await trayManager.setIcon('assets/images/logo/logo_large.png'); } if (!Platform.isLinux) { await trayManager.setToolTip(Constants.appName); diff --git a/lib/pages/video/ai_conclusion/view.dart b/lib/pages/video/ai_conclusion/view.dart index cead90a61..30266886f 100644 --- a/lib/pages/video/ai_conclusion/view.dart +++ b/lib/pages/video/ai_conclusion/view.dart @@ -1,6 +1,7 @@ import 'package:PiliPlus/models_new/video/video_ai_conclusion/model_result.dart'; import 'package:PiliPlus/pages/common/slide/common_slide_page.dart'; import 'package:PiliPlus/pages/video/controller.dart'; +import 'package:PiliPlus/pages/video/introduction/ugc/widgets/selectable_text.dart'; import 'package:PiliPlus/utils/duration_utils.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; @@ -69,7 +70,7 @@ class _AiDetailState extends State SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 14), - child: SelectableText( + child: selectableText( widget.item.summary!, style: const TextStyle( fontSize: 15, @@ -98,57 +99,59 @@ class _AiDetailState extends State itemCount: widget.item.outline!.length, itemBuilder: (context, index) { final item = widget.item.outline![index]; - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (index != 0) const SizedBox(height: 10), - SelectableText( - item.title!, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - height: 1.5, + return SelectionArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (index != 0) const SizedBox(height: 10), + Text( + item.title!, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + height: 1.5, + ), ), - ), - const SizedBox(height: 6), - ...?item.partOutline?.map( - (item) => Wrap( - children: [ - SelectableText.rich( - TextSpan( - style: TextStyle( - fontSize: 14, - color: theme.colorScheme.onSurface, - height: 1.5, - ), - children: [ - TextSpan( - text: DurationUtils.formatDuration( - item.timestamp, - ), - style: TextStyle( - color: theme.colorScheme.primary, - ), - recognizer: TapGestureRecognizer() - ..onTap = () { - try { - Get.find( - tag: Get.arguments['heroTag'], - ).plPlayerController.seekTo( - Duration(seconds: item.timestamp!), - ); - } catch (_) {} - }, + const SizedBox(height: 6), + ...?item.partOutline?.map( + (item) => Wrap( + children: [ + Text.rich( + TextSpan( + style: TextStyle( + fontSize: 14, + color: theme.colorScheme.onSurface, + height: 1.5, ), - const TextSpan(text: ' '), - TextSpan(text: item.content!), - ], + children: [ + TextSpan( + text: DurationUtils.formatDuration( + item.timestamp, + ), + style: TextStyle( + color: theme.colorScheme.primary, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + try { + Get.find( + tag: Get.arguments['heroTag'], + ).plPlayerController.seekTo( + Duration(seconds: item.timestamp!), + ); + } catch (_) {} + }, + ), + const TextSpan(text: ' '), + TextSpan(text: item.content!), + ], + ), ), - ), - ], + ], + ), ), - ), - ], + ], + ), ); }, ), diff --git a/lib/pages/video/introduction/pgc/controller.dart b/lib/pages/video/introduction/pgc/controller.dart index 77d5dd40a..8cf3fa8c9 100644 --- a/lib/pages/video/introduction/pgc/controller.dart +++ b/lib/pages/video/introduction/pgc/controller.dart @@ -277,7 +277,7 @@ class PgcIntroController extends CommonIntroController { } // 修改分P或番剧分集 - Future onChangeEpisode(BaseEpisodeItem episode) async { + Future onChangeEpisode(BaseEpisodeItem episode) async { try { final int epId = episode.epId ?? episode.id!; final String bvid = episode.bvid ?? this.bvid; @@ -285,7 +285,7 @@ class PgcIntroController extends CommonIntroController { final int? cid = episode.cid ?? await SearchHttp.ab2c(aid: aid, bvid: bvid); if (cid == null) { - return; + return false; } final String? cover = episode.cover; @@ -323,8 +323,10 @@ class PgcIntroController extends CommonIntroController { this.cid.value = cid; queryOnlineTotal(); queryVideoIntro(episode as EpisodeItem); + return true; } catch (e) { if (kDebugMode) debugPrint('pgc onChangeEpisode: $e'); + return false; } } diff --git a/lib/pages/video/introduction/pgc/widgets/intro_detail.dart b/lib/pages/video/introduction/pgc/widgets/intro_detail.dart index 2c53c560b..497c17b56 100644 --- a/lib/pages/video/introduction/pgc/widgets/intro_detail.dart +++ b/lib/pages/video/introduction/pgc/widgets/intro_detail.dart @@ -9,6 +9,7 @@ import 'package:PiliPlus/models_new/video/video_tag/data.dart'; import 'package:PiliPlus/pages/common/slide/common_slide_page.dart'; import 'package:PiliPlus/pages/pgc_review/view.dart'; import 'package:PiliPlus/pages/search/widgets/search_text.dart'; +import 'package:PiliPlus/pages/video/introduction/ugc/widgets/selectable_text.dart'; import 'package:PiliPlus/utils/extension.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart' hide TabBarView; @@ -108,7 +109,7 @@ class _IntroDetailState extends State bottom: MediaQuery.viewPaddingOf(context).bottom + 100, ), children: [ - SelectableText( + selectableText( widget.item.title!, style: const TextStyle(fontSize: 16), ), @@ -152,7 +153,7 @@ class _IntroDetailState extends State style: theme.textTheme.titleMedium, ), const SizedBox(height: 4), - SelectableText( + selectableText( widget.item.evaluate!, style: textStyle, ), diff --git a/lib/pages/video/introduction/ugc/controller.dart b/lib/pages/video/introduction/ugc/controller.dart index 46d2be3a3..4ee8812b2 100644 --- a/lib/pages/video/introduction/ugc/controller.dart +++ b/lib/pages/video/introduction/ugc/controller.dart @@ -463,7 +463,7 @@ class UgcIntroController extends CommonIntroController with ReloadMixin { } // 修改分P或番剧分集 - Future onChangeEpisode( + Future onChangeEpisode( BaseEpisodeItem episode, { bool isStein = false, }) async { @@ -473,7 +473,7 @@ class UgcIntroController extends CommonIntroController with ReloadMixin { final int? cid = episode.cid ?? await SearchHttp.ab2c(aid: aid, bvid: bvid); if (cid == null) { - return; + return false; } final String? cover = episode.cover; @@ -488,7 +488,7 @@ class UgcIntroController extends CommonIntroController with ReloadMixin { cid: cid, cover: cover, ); - return; + return false; } } @@ -546,8 +546,10 @@ class UgcIntroController extends CommonIntroController with ReloadMixin { this.cid.value = cid; queryOnlineTotal(); + return true; } catch (e) { if (kDebugMode) debugPrint('ugc onChangeEpisode: $e'); + return false; } } diff --git a/lib/pages/video/introduction/ugc/view.dart b/lib/pages/video/introduction/ugc/view.dart index 75a94d0e6..e080731cb 100644 --- a/lib/pages/video/introduction/ugc/view.dart +++ b/lib/pages/video/introduction/ugc/view.dart @@ -15,6 +15,7 @@ import 'package:PiliPlus/pages/video/introduction/ugc/controller.dart'; import 'package:PiliPlus/pages/video/introduction/ugc/widgets/action_item.dart'; import 'package:PiliPlus/pages/video/introduction/ugc/widgets/page.dart'; import 'package:PiliPlus/pages/video/introduction/ugc/widgets/season.dart'; +import 'package:PiliPlus/pages/video/introduction/ugc/widgets/selectable_text.dart'; import 'package:PiliPlus/pages/video/introduction/ugc/widgets/triple_state.dart'; import 'package:PiliPlus/utils/app_scheme.dart'; import 'package:PiliPlus/utils/date_utils.dart'; @@ -327,13 +328,9 @@ class _UgcIntroPanelState extends TripleState { ), if (videoDetail.descV2?.isNotEmpty == true) ...[ const SizedBox(height: 8), - SelectableText.rich( + selectableRichText( style: const TextStyle(height: 1.4), - TextSpan( - children: [ - buildContent(theme, videoDetail), - ], - ), + buildContent(theme, videoDetail), ), ], Obx(() { @@ -601,7 +598,7 @@ class _UgcIntroPanelState extends TripleState { caseSensitive: false, ); - InlineSpan buildContent(ThemeData theme, VideoDetailData content) { + TextSpan buildContent(ThemeData theme, VideoDetailData content) { if (content.descV2.isNullOrEmpty) { return const TextSpan(); } diff --git a/lib/pages/video/introduction/ugc/widgets/selectable_text.dart b/lib/pages/video/introduction/ugc/widgets/selectable_text.dart new file mode 100644 index 000000000..be92da753 --- /dev/null +++ b/lib/pages/video/introduction/ugc/widgets/selectable_text.dart @@ -0,0 +1,38 @@ +import 'package:PiliPlus/utils/utils.dart'; +import 'package:flutter/material.dart'; + +Widget selectableText( + String text, { + TextStyle? style, +}) { + if (Utils.isDesktop) { + return SelectionArea( + child: Text( + style: style, + text, + ), + ); + } + return SelectableText( + style: style, + text, + ); +} + +Widget selectableRichText( + TextSpan textSpan, { + TextStyle? style, +}) { + if (Utils.isDesktop) { + return SelectionArea( + child: Text.rich( + style: style, + textSpan, + ), + ); + } + return SelectableText.rich( + style: style, + textSpan, + ); +} diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index b66882069..3df80ad2c 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -44,6 +44,11 @@ class _WhisperDetailPageState Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); final padding = MediaQuery.viewPaddingOf(context); + late final containerColor = ElevationOverlay.colorWithOverlay( + theme.colorScheme.surface, + theme.hoverColor, + 1, + ); return Scaffold( resizeToAvoidBottomInset: false, appBar: AppBar( @@ -145,8 +150,11 @@ class _WhisperDetailPageState ), ), if (_whisperDetailController.mid != null) ...[ - _buildInputView(theme), - buildPanelContainer(theme, theme.colorScheme.onInverseSurface), + _buildInputView(theme, containerColor), + buildPanelContainer( + theme, + containerColor, + ), ] else SizedBox(height: padding.bottom), ], @@ -228,11 +236,11 @@ class _WhisperDetailPageState ); } - Widget _buildInputView(ThemeData theme) { + Widget _buildInputView(ThemeData theme, Color containerColor) { return Container( padding: const EdgeInsets.symmetric(vertical: 8), decoration: BoxDecoration( - color: theme.colorScheme.onInverseSurface, + color: containerColor, borderRadius: const BorderRadius.vertical(top: Radius.circular(16)), ), child: Row(