diff --git a/lib/common/constants.dart b/lib/common/constants.dart index 3b510d0e8..b018cfb03 100644 --- a/lib/common/constants.dart +++ b/lib/common/constants.dart @@ -20,9 +20,9 @@ class Constants { static const String appKey = 'dfca71928277209b'; // 59b43e04ad6965f34319062b478f83dd TV端 static const String appSec = 'b5475a8825547a4fc26c7d518eaaa02e'; - static const String thirdSign = '04224646d1fea004e79606d3b038c84a'; - static const String thirdApi = - 'https://www.mcbbs.net/template/mcbbs/image/special_photo_bg.png'; + // static const String thirdSign = '04224646d1fea004e79606d3b038c84a'; + // static const String thirdApi = + // 'https://www.mcbbs.net/template/mcbbs/image/special_photo_bg.png'; static const String traceId = '11111111111111111111111111111111:1111111111111111:0:0'; diff --git a/lib/common/widgets/appbar/appbar.dart b/lib/common/widgets/appbar/appbar.dart index fd0355936..90c5e3d05 100644 --- a/lib/common/widgets/appbar/appbar.dart +++ b/lib/common/widgets/appbar/appbar.dart @@ -20,6 +20,8 @@ class MultiSelectAppBarWidget extends StatelessWidget @override Widget build(BuildContext context) { if (visible ?? ctr.enableMultiSelect.value) { + final style = TextButton.styleFrom(visualDensity: VisualDensity.compact); + final colorScheme = ColorScheme.of(context); return AppBar( bottom: child.bottom, leading: IconButton( @@ -30,17 +32,13 @@ class MultiSelectAppBarWidget extends StatelessWidget title: Obx(() => Text('已选: ${ctr.checkedCount}')), actions: [ TextButton( - style: TextButton.styleFrom( - visualDensity: VisualDensity.compact, - ), + style: style, onPressed: () => ctr.handleSelect(checked: true), child: const Text('全选'), ), ...?actions, TextButton( - style: TextButton.styleFrom( - visualDensity: VisualDensity.compact, - ), + style: style, onPressed: () { if (ctr.checkedCount == 0) { return; @@ -49,7 +47,7 @@ class MultiSelectAppBarWidget extends StatelessWidget }, child: Text( '移除', - style: TextStyle(color: Get.theme.colorScheme.error), + style: TextStyle(color: colorScheme.error), ), ), const SizedBox(width: 6), diff --git a/lib/common/widgets/dialog/report.dart b/lib/common/widgets/dialog/report.dart index 6ff44848a..1da1dd78c 100644 --- a/lib/common/widgets/dialog/report.dart +++ b/lib/common/widgets/dialog/report.dart @@ -1,4 +1,5 @@ import 'package:PiliPlus/common/widgets/radio_widget.dart'; +import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/utils/extension/string_ext.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; @@ -8,26 +9,22 @@ import 'package:get/get.dart'; Future autoWrapReportDialog( BuildContext context, Map> options, - Future Function(int reasonType, String? reasonDesc, bool banUid) + Future Function(int reasonType, String? reasonDesc, bool banUid) onSuccess, ) { int? reasonType; String? reasonDesc; bool banUid = false; - late final key = GlobalKey(); + late final key = GlobalKey>(); return showDialog( context: context, builder: (context) { return AlertDialog( constraints: const BoxConstraints(minWidth: 280, maxWidth: 420), title: const Text('举报'), - titlePadding: const EdgeInsets.only(left: 22, top: 16, right: 22), - contentPadding: const EdgeInsets.symmetric(vertical: 5), - actionsPadding: const EdgeInsets.only( - left: 16, - right: 16, - bottom: 10, - ), + titlePadding: const .only(left: 22, top: 16, right: 22), + contentPadding: const .symmetric(vertical: 5), + actionsPadding: const .only(left: 16, right: 16, bottom: 10), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, @@ -41,11 +38,7 @@ Future autoWrapReportDialog( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Padding( - padding: EdgeInsets.only( - left: 22, - right: 22, - bottom: 5, - ), + padding: .only(left: 22, right: 22, bottom: 5), child: Text('请选择举报的理由:'), ), RadioGroup( @@ -66,27 +59,21 @@ Future autoWrapReportDialog( ), if (reasonType == 0) Padding( - padding: const EdgeInsets.only( - left: 22, - top: 5, - right: 22, - ), - child: Form( + padding: const .only(left: 22, top: 5, right: 22), + child: TextFormField( key: key, - child: TextFormField( - autofocus: true, - minLines: 2, - maxLines: 4, - initialValue: reasonDesc, - decoration: const InputDecoration( - labelText: '为帮助审核人员更快处理,请补充问题类型和出现位置等详细信息', - border: OutlineInputBorder(), - contentPadding: EdgeInsets.all(10), - ), - onChanged: (value) => reasonDesc = value, - validator: (value) => - value.isNullOrEmpty ? '理由不能为空' : null, + autofocus: true, + minLines: 2, + maxLines: 4, + initialValue: reasonDesc, + decoration: const InputDecoration( + labelText: '为帮助审核人员更快处理,请补充问题类型和出现位置等详细信息', + border: OutlineInputBorder(), + contentPadding: .all(10), ), + onChanged: (value) => reasonDesc = value, + validator: (value) => + value.isNullOrEmpty ? '理由不能为空' : null, ), ), ], @@ -109,7 +96,7 @@ Future autoWrapReportDialog( onPressed: Get.back, child: Text( '取消', - style: TextStyle(color: Theme.of(context).colorScheme.outline), + style: TextStyle(color: ColorScheme.of(context).outline), ), ), TextButton( @@ -120,13 +107,13 @@ Future autoWrapReportDialog( } SmartDialog.showLoading(); try { - final data = await onSuccess(reasonType!, reasonDesc, banUid); + final res = await onSuccess(reasonType!, reasonDesc, banUid); SmartDialog.dismiss(); - if (data['code'] == 0) { + if (res.isSuccess) { Get.back(); SmartDialog.showToast('举报成功'); } else { - SmartDialog.showToast(data['message'].toString()); + res.toast(); } } catch (e, s) { SmartDialog.dismiss(); @@ -169,7 +156,7 @@ class _CheckBoxTextState extends State { @override Widget build(BuildContext context) { - final colorScheme = Theme.of(context).colorScheme; + final colorScheme = ColorScheme.of(context); return InkWell( onTap: () { setState(() { @@ -202,7 +189,7 @@ class _CheckBoxTextState extends State { } } -class ReportOptions { +abstract final class ReportOptions { // from https://s1.hdslb.com/bfs/seed/jinkela/comment-h5/static/js/605.chunks.js static Map> get commentReport => const { '违反法律法规': {9: '违法违规', 2: '色情', 10: '低俗', 12: '赌博诈骗', 23: '违法信息外链'}, diff --git a/lib/common/widgets/flutter/text_field/editable_text.dart b/lib/common/widgets/flutter/text_field/editable_text.dart index dbc2f8340..0b5f77245 100644 --- a/lib/common/widgets/flutter/text_field/editable_text.dart +++ b/lib/common/widgets/flutter/text_field/editable_text.dart @@ -3401,6 +3401,8 @@ class EditableTextState extends State // editing. if (!_isMultiline) { _finalizeEditing(action, shouldUnfocus: true); + } else if (HardwareKeyboard.instance.isControlPressed) { + _finalizeEditing(action, shouldUnfocus: true); } case TextInputAction.done: case TextInputAction.go: diff --git a/lib/grpc/grpc_req.dart b/lib/grpc/grpc_req.dart index 3d0e79ba9..bb5fb6d7b 100644 --- a/lib/grpc/grpc_req.dart +++ b/lib/grpc/grpc_req.dart @@ -118,20 +118,21 @@ class GrpcReq { static Uint8List compressProtobuf(Uint8List proto) { proto = const GZipEncoder().encodeBytes(proto); - var byteLength = ByteData(4)..setInt32(0, proto.length, Endian.big); return Uint8List(5 + proto.length) ..[0] = 1 - ..setRange(1, 5, byteLength.buffer.asUint8List()) + ..buffer.asByteData(1, 4).setInt32(0, proto.length, Endian.big) ..setAll(5, proto); } static Uint8List decompressProtobuf(Uint8List data) { - var length = ByteData.sublistView(data, 1, 5).getInt32(0, Endian.big); + final length = ByteData.sublistView(data, 1, 5).getInt32(0, Endian.big); if (data[0] == 1) { - return const GZipDecoder().decodeBytes(data.sublist(5, length + 5)); + return const GZipDecoder().decodeBytes( + Uint8List.sublistView(data, 5, length + 5), + ); } else { - return data.sublist(5, length + 5); + return Uint8List.sublistView(data, 5, length + 5); } } diff --git a/lib/grpc/reply.dart b/lib/grpc/reply.dart index d58d70647..f265a5559 100644 --- a/lib/grpc/reply.dart +++ b/lib/grpc/reply.dart @@ -30,8 +30,8 @@ class ReplyGrpc { reply.content.urls.values.any((url) { return url.hasExtra() && (url.extra.goodsCmControl == Int64.ONE || - url.extra.goodsItemId != Int64.ZERO || - url.extra.goodsPrefetchedCache.isNotEmpty); + url.extra.hasGoodsItemId() || + url.extra.hasGoodsPrefetchedCache()); })) || reply.content.message.contains(Constants.goodsUrlPrefix); } @@ -99,7 +99,7 @@ class ReplyGrpc { rpid: Int64(rpid), scene: DetailListScene.REPLY, mode: mode, - pagination: FeedPagination(offset: offset ?? ''), + pagination: offset == null ? null : FeedPagination(offset: offset), ), DetailListReply.fromBuffer, ); diff --git a/lib/http/api.dart b/lib/http/api.dart index 942414946..14311292a 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -688,7 +688,8 @@ class Api { // static const String videoTags = '/x/tag/archive/tags'; static const String videoTags = '/x/web-interface/view/detail/tag'; - static const String reportMember = '/ajax/report/add'; + static const String reportMember = + '${HttpString.spaceBaseUrl}/ajax/report/add'; static const String removeMsg = '/session_svr/v1/session_svr/remove_session'; diff --git a/lib/http/danmaku.dart b/lib/http/danmaku.dart index 8f6a12388..bcfaf058e 100644 --- a/lib/http/danmaku.dart +++ b/lib/http/danmaku.dart @@ -89,7 +89,7 @@ abstract final class DanmakuHttp { } } - static Future> danmakuReport({ + static Future> danmakuReport({ required int reason, required int cid, required int id, @@ -115,7 +115,12 @@ abstract final class DanmakuHttp { data: data, options: Options(contentType: Headers.formUrlEncodedContentType), ); - return res.data as Map; + + if (res.data['code'] == 0) { + return const Success(null); + } else { + return Error(res.data['message']); + } /// res.data['data']['block'] /// { diff --git a/lib/http/live.dart b/lib/http/live.dart index 22363a700..3a84c9073 100644 --- a/lib/http/live.dart +++ b/lib/http/live.dart @@ -633,7 +633,7 @@ abstract final class LiveHttp { } } - static Future> liveDmReport({ + static Future> liveDmReport({ required int roomId, required Object mid, required String msg, @@ -666,6 +666,10 @@ abstract final class LiveHttp { data: data, options: Options(contentType: Headers.formUrlEncodedContentType), ); - return res.data as Map; + if (res.data['code'] == 0) { + return const Success(null); + } else { + return Error(res.data['message']); + } } } diff --git a/lib/http/loading_state.dart b/lib/http/loading_state.dart index 2f20bc749..0b8c86b30 100644 --- a/lib/http/loading_state.dart +++ b/lib/http/loading_state.dart @@ -3,7 +3,7 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; sealed class LoadingState { const LoadingState(); - factory LoadingState.loading() = Loading; + factory LoadingState.loading() => const Loading._internal(); bool get isSuccess => this is Success; @@ -23,10 +23,6 @@ sealed class LoadingState { class Loading extends LoadingState { const Loading._internal(); - static const Loading _instance = Loading._internal(); - - factory Loading() => _instance; - @override String toString() { return 'ApiException: loading'; @@ -42,7 +38,7 @@ class Success extends LoadingState { if (identical(this, other)) { return true; } - if (other is Success) { + if (other is Success) { return response == other.response; } return false; @@ -63,13 +59,13 @@ class Error extends LoadingState { return true; } if (other is Error) { - return errMsg == other.errMsg; + return errMsg == other.errMsg && code == other.code; } return false; } @override - int get hashCode => errMsg.hashCode; + int get hashCode => Object.hash(errMsg, code); @override String toString() { diff --git a/lib/http/member.dart b/lib/http/member.dart index fd1466e6c..0c778689e 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -37,7 +37,7 @@ abstract final class MemberHttp { int? reasonV2, }) async { var res = await Request().post( - HttpString.spaceBaseUrl + Api.reportMember, + Api.reportMember, data: { 'mid': mid, 'reason': reason, diff --git a/lib/http/reply.dart b/lib/http/reply.dart index e238cbe7f..343ebb60e 100644 --- a/lib/http/reply.dart +++ b/lib/http/reply.dart @@ -10,7 +10,7 @@ import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/accounts/account.dart'; import 'package:dio/dio.dart'; -class ReplyHttp { +abstract final class ReplyHttp { static final Options options = Options( headers: {...Constants.baseHeaders, 'cookie': ''}, extra: {'account': const NoAccount()}, @@ -175,4 +175,35 @@ class ReplyHttp { return Error(res.data['message']); } } + + static Future> report({ + required Object rpid, + required Object oid, + required int reasonType, + bool banUid = true, + String? reasonDesc, + }) async { + final res = await Request().post( + '/x/v2/reply/report', + data: { + 'add_blacklist': banUid, + 'csrf': Accounts.main.csrf, + 'gaia_source': 'main_h5', + 'oid': oid, + 'platform': 'android', + 'reason': reasonType, + 'rpid': rpid, + 'scene': 'main', + 'type': 1, + if (reasonType == 0) 'content': reasonDesc!, + }, + options: Options(contentType: Headers.formUrlEncodedContentType), + ); + + if (res.data['code'] == 0) { + return const Success(null); + } else { + return Error(res.data['message']); + } + } } diff --git a/lib/http/user.dart b/lib/http/user.dart index 453617df9..3d23e06fc 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -221,7 +221,7 @@ class UserHttp { // } // 清空稍后再看 // clean_type: null->all, 1->invalid, 2->viewed - static Future toViewClear([int? cleanType]) async { + static Future> toViewClear([int? cleanType]) async { var res = await Request().post( Api.toViewClear, data: { @@ -231,9 +231,9 @@ class UserHttp { options: Options(contentType: Headers.formUrlEncodedContentType), ); if (res.data['code'] == 0) { - return {'status': true, 'msg': '操作完成'}; + return const Success(null); } else { - return {'status': false, 'msg': res.data['message']}; + return Error(res.data['message']); } } @@ -383,9 +383,9 @@ class UserHttp { } } - static Future dynamicReport({ - required dynamic mid, - required dynamic dynId, + static Future> dynamicReport({ + required Object mid, + required Object dynId, required int reasonType, String? reasonDesc, }) async { @@ -402,7 +402,11 @@ class UserHttp { }, options: Options(contentType: Headers.formUrlEncodedContentType), ); - return res.data as Map; + if (res.data['code'] == 0) { + return const Success(null); + } else { + return Error(res.data['message']); + } } static Future> spaceSetting() async { diff --git a/lib/main.dart b/lib/main.dart index 2e3d53821..a6bfae0ca 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -56,6 +56,7 @@ void main() async { if (kDebugMode) debugPrint('GStorage init error: $e'); exit(0); } + if (PlatformUtils.isDesktop) { final customDownPath = Pref.downloadPath; if (customDownPath != null && customDownPath.isNotEmpty) { @@ -135,15 +136,13 @@ void main() async { ), ); if (Platform.isAndroid) { - late List modes; - FlutterDisplayMode.supported.then((value) { - modes = value; + FlutterDisplayMode.supported.then((mode) { final String? storageDisplay = GStorage.setting.get( SettingBoxKey.displayMode, ); DisplayMode? displayMode; if (storageDisplay != null) { - displayMode = modes.firstWhereOrNull( + displayMode = mode.firstWhereOrNull( (e) => e.toString() == storageDisplay, ); } @@ -153,7 +152,7 @@ void main() async { } else if (PlatformUtils.isDesktop) { await windowManager.ensureInitialized(); - WindowOptions windowOptions = WindowOptions( + final windowOptions = WindowOptions( minimumSize: const Size(400, 720), skipTaskbar: false, titleBarStyle: Pref.showWindowTitleBar @@ -172,6 +171,10 @@ void main() async { }); } + if (Pref.dynamicColor) { + await MyApp.initPlatformState(); + } + if (Pref.enableLog) { // 异常捕获 logo记录 final customParameters = { @@ -215,6 +218,8 @@ void main() async { class MyApp extends StatelessWidget { const MyApp({super.key}); + static ColorScheme? _light, _dark; + static ThemeData? darkThemeData; static void _onBack() { @@ -252,36 +257,35 @@ class MyApp extends StatelessWidget { Get.back(); } - static Widget _build({ - ColorScheme? lightColorScheme, - ColorScheme? darkColorScheme, - }) { + @override + Widget build(BuildContext context) { + final dynamicColor = Pref.dynamicColor && _light != null && _dark != null; late final brandColor = colorThemeTypes[Pref.customColor].color; late final variant = FlexSchemeVariant.values[Pref.schemeVariant]; return GetMaterialApp( title: Constants.appName, theme: ThemeUtils.getThemeData( - colorScheme: - lightColorScheme ?? - SeedColorScheme.fromSeeds( - variant: variant, - primaryKey: brandColor, - brightness: Brightness.light, - useExpressiveOnContainerColors: false, - ), - isDynamic: lightColorScheme != null, + colorScheme: dynamicColor + ? _light! + : SeedColorScheme.fromSeeds( + variant: variant, + primaryKey: brandColor, + brightness: Brightness.light, + useExpressiveOnContainerColors: false, + ), + isDynamic: dynamicColor, ), darkTheme: ThemeUtils.getThemeData( isDark: true, - colorScheme: - darkColorScheme ?? - SeedColorScheme.fromSeeds( - variant: variant, - primaryKey: brandColor, - brightness: Brightness.dark, - useExpressiveOnContainerColors: false, - ), - isDynamic: darkColorScheme != null, + colorScheme: dynamicColor + ? _dark! + : SeedColorScheme.fromSeeds( + variant: variant, + primaryKey: brandColor, + brightness: Brightness.dark, + useExpressiveOnContainerColors: false, + ), + isDynamic: dynamicColor, ), themeMode: Pref.themeMode, localizationsDelegates: const [ @@ -343,23 +347,52 @@ class MyApp extends StatelessWidget { ); } - @override - Widget build(BuildContext context) { - if (!Platform.isIOS && Pref.dynamicColor) { - return DynamicColorBuilder( - builder: ((ColorScheme? lightDynamic, ColorScheme? darkDynamic) { - if (lightDynamic != null && darkDynamic != null) { - return _build( - lightColorScheme: lightDynamic.harmonized(), - darkColorScheme: darkDynamic.harmonized(), - ); - } else { - return _build(); - } - }), - ); + /// from [DynamicColorBuilderState.initPlatformState] + static Future initPlatformState() async { + if (_light != null || _dark != null) return; + // Platform messages may fail, so we use a try/catch PlatformException. + try { + final corePalette = await DynamicColorPlugin.getCorePalette(); + + if (corePalette != null) { + if (kDebugMode) { + debugPrint('dynamic_color: Core palette detected.'); + } + _light = corePalette.toColorScheme(); + _dark = corePalette.toColorScheme(brightness: Brightness.dark); + return; + } + } on PlatformException { + if (kDebugMode) { + debugPrint('dynamic_color: Failed to obtain core palette.'); + } + } + + try { + final Color? accentColor = await DynamicColorPlugin.getAccentColor(); + + if (accentColor != null) { + if (kDebugMode) { + debugPrint('dynamic_color: Accent color detected.'); + } + _light = ColorScheme.fromSeed( + seedColor: accentColor, + brightness: Brightness.light, + ); + _dark = ColorScheme.fromSeed( + seedColor: accentColor, + brightness: Brightness.dark, + ); + return; + } + } on PlatformException { + if (kDebugMode) { + debugPrint('dynamic_color: Failed to obtain accent color.'); + } + } + if (kDebugMode) { + debugPrint('dynamic_color: Dynamic color not detected on this device.'); } - return _build(); } } diff --git a/lib/models/common/home_tab_type.dart b/lib/models/common/home_tab_type.dart index fdf50bd7d..e168616e7 100644 --- a/lib/models/common/home_tab_type.dart +++ b/lib/models/common/home_tab_type.dart @@ -30,8 +30,7 @@ enum HomeTabType implements EnumWithLabel { HomeTabType.live => Get.find, HomeTabType.rcmd => Get.find, HomeTabType.hot => Get.find, - HomeTabType.rank => - (Get.find) as ScrollOrRefreshMixin Function(), + HomeTabType.rank => Get.find, HomeTabType.bangumi || HomeTabType.cinema => () => Get.find(tag: name), }; diff --git a/lib/pages/article/controller.dart b/lib/pages/article/controller.dart index 2c5e718d0..1ee0d209b 100644 --- a/lib/pages/article/controller.dart +++ b/lib/pages/article/controller.dart @@ -222,7 +222,7 @@ class ArticleController extends CommonDynController { @override Future onReload() { if (!isLoaded.value) { - return Future.value(); + return Future.syncValue(null); } return super.onReload(); } diff --git a/lib/pages/article/widgets/article_ops.dart b/lib/pages/article/widgets/article_ops.dart index a5a46fb53..ee2b8b305 100644 --- a/lib/pages/article/widgets/article_ops.dart +++ b/lib/pages/article/widgets/article_ops.dart @@ -2,28 +2,27 @@ import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/models_new/article/article_view/ops.dart'; import 'package:PiliPlus/pages/dynamics/widgets/vote.dart'; import 'package:PiliPlus/utils/app_scheme.dart'; -import 'package:PiliPlus/utils/extension/iterable_ext.dart'; import 'package:PiliPlus/utils/image_utils.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; class ArticleOpus extends StatelessWidget { - const ArticleOpus({super.key, required this.ops}); + const ArticleOpus({super.key, required List? ops}) : _ops = ops; - final List? ops; + final List? _ops; @override Widget build(BuildContext context) { - if (ops.isNullOrEmpty) { + if ((_ops == null || _ops.isEmpty)) { return const SliverToBoxAdapter(); } return SliverList.separated( - itemCount: ops!.length, + itemCount: _ops.length, itemBuilder: (context, index) { try { - final item = ops![index]; + final item = _ops[index]; if (item.insert is String) { return SelectableText(item.insert); } diff --git a/lib/pages/article/widgets/opus_content.dart b/lib/pages/article/widgets/opus_content.dart index 9a285866c..906f3d9d2 100644 --- a/lib/pages/article/widgets/opus_content.dart +++ b/lib/pages/article/widgets/opus_content.dart @@ -1,4 +1,4 @@ -import 'dart:math'; +import 'dart:math' as math; import 'package:PiliPlus/common/widgets/image/cached_network_svg_image.dart'; import 'package:PiliPlus/common/widgets/image/custom_grid_view.dart'; @@ -40,7 +40,7 @@ class OpusContent extends StatelessWidget { required Node item, required ColorScheme colorScheme, bool isQuote = false, - required double surfaceLuminance, + required ValueGetter surfaceLuminance, }) { switch (item.type) { case 'TEXT_NODE_TYPE_RICH' when (item.rich != null): @@ -123,12 +123,21 @@ class OpusContent extends StatelessWidget { static TextSpan _getSpan( Word? word, { Color? defaultColor, - required double surfaceLuminance, + required ValueGetter surfaceLuminance, }) { Color? color; if (word?.color case final c?) { final tmpColor = Color(c); - if ((surfaceLuminance - tmpColor.computeLuminance()).abs() > 0.1) { + double max = tmpColor.computeLuminance(); + double min = surfaceLuminance(); + if (max < min) { + final tmp = max; + max = min; + min = tmp; + } + + // WCAG AA : (max + 0.05) / (min + 0.05) > 3.0 + if (max > 3.0 * min + 0.1) { color = tmpColor; } } @@ -151,7 +160,9 @@ class OpusContent extends StatelessWidget { final colorScheme = Theme.of(context).colorScheme; late final isDarkMode = colorScheme.isDark; - late final surfaceLuminance = colorScheme.surface.computeLuminance(); + double? surfaceLuminance; + double getSurfaceLuminance() => + surfaceLuminance ??= colorScheme.surface.computeLuminance(); late final highlight = Highlight()..registerLanguages(builtinAllLanguages); @@ -171,7 +182,7 @@ class OpusContent extends StatelessWidget { (item) => _node2Widget( item: item, colorScheme: colorScheme, - surfaceLuminance: surfaceLuminance, + surfaceLuminance: getSurfaceLuminance, ), ) .toList(), @@ -204,7 +215,7 @@ class OpusContent extends StatelessWidget { final pic = element.pic!.pics!.first; final width = pic.width == null ? null - : min(maxWidth.toDouble(), pic.width!); + : math.min(maxWidth.toDouble(), pic.width!); final height = width == null || pic.height == null ? null : width * pic.height! / pic.width!; @@ -263,7 +274,7 @@ class OpusContent extends StatelessWidget { if (item.word != null) { return _getSpan( item.word, - surfaceLuminance: surfaceLuminance, + surfaceLuminance: getSurfaceLuminance, ); } if (item.rich case final rich?) { @@ -626,7 +637,7 @@ class OpusContent extends StatelessWidget { (e) => _node2Widget( item: e, colorScheme: colorScheme, - surfaceLuminance: surfaceLuminance, + surfaceLuminance: getSurfaceLuminance, ), ) .toList(), @@ -642,7 +653,7 @@ class OpusContent extends StatelessWidget { .map( (item) => _getSpan( item.word, - surfaceLuminance: surfaceLuminance, + surfaceLuminance: getSurfaceLuminance, ), ) .toList(), @@ -744,7 +755,7 @@ Widget moduleBlockedItem( } if (moduleBlocked.blockedType == 1) { - maxWidth = maxWidth <= 255 ? maxWidth : min(400, maxWidth * 0.8); + maxWidth = maxWidth <= 255 ? maxWidth : math.min(400, maxWidth * 0.8); return UnconstrainedBox( alignment: Alignment.centerLeft, child: Container( @@ -755,7 +766,7 @@ Widget moduleBlockedItem( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - if (moduleBlocked.icon != null) icon(max(40, maxWidth / 7)), + if (moduleBlocked.icon != null) icon(math.max(40, maxWidth / 7)), if (moduleBlocked.hintMessage?.isNotEmpty == true) ...[ const SizedBox(height: 5), Text( diff --git a/lib/pages/common/common_list_controller.dart b/lib/pages/common/common_list_controller.dart index 3f0d8ace4..ca4e6f6b3 100644 --- a/lib/pages/common/common_list_controller.dart +++ b/lib/pages/common/common_list_controller.dart @@ -1,6 +1,5 @@ import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/pages/common/common_controller.dart'; -import 'package:PiliPlus/utils/extension/iterable_ext.dart'; import 'package:get/get.dart'; abstract class CommonListController extends CommonController { @@ -27,8 +26,8 @@ abstract class CommonListController extends CommonController { LoadingState response = await customGetData(); if (response is Success) { if (!customHandleResponse(isRefresh, response)) { - List? dataList = getDataList(response.response); - if (dataList.isNullOrEmpty) { + final dataList = getDataList(response.response); + if (dataList == null || dataList.isEmpty) { isEnd = true; if (isRefresh) { loadingState.value = Success(dataList); @@ -38,7 +37,7 @@ abstract class CommonListController extends CommonController { isLoading = false; return; } - handleListResponse(dataList!); + handleListResponse(dataList); if (isRefresh) { checkIsEnd(dataList.length); loadingState.value = Success(dataList); diff --git a/lib/pages/common/multi_select/base.dart b/lib/pages/common/multi_select/base.dart index 0b72dff8d..f3d812ece 100644 --- a/lib/pages/common/multi_select/base.dart +++ b/lib/pages/common/multi_select/base.dart @@ -3,10 +3,10 @@ import 'package:PiliPlus/pages/common/common_list_controller.dart'; import 'package:get/get.dart'; mixin MultiSelectData { - bool? checked; + bool checked = false; } -mixin MultiSelectBase { +abstract interface class MultiSelectBase { RxBool get enableMultiSelect; int get checkedCount; @@ -28,7 +28,7 @@ mixin BaseMultiSelectMixin RxObjectMixin get state; List get list; - Iterable get allChecked => list.where((v) => v.checked == true); + Iterable get allChecked => list.where((v) => v.checked); @override void handleSelect({bool checked = false, bool disableSelect = true}) { @@ -44,8 +44,8 @@ mixin BaseMultiSelectMixin @override void onSelect(T item) { - item.checked = !(item.checked ?? false); - if (item.checked!) { + item.checked = !item.checked; + if (item.checked) { rxCount.value++; } else { rxCount.value--; @@ -70,13 +70,13 @@ mixin CommonMultiSelectMixin int get checkedCount => rxCount.value; Iterable get allChecked => - loadingState.value.data!.where((v) => v.checked == true); + loadingState.value.data!.where((v) => v.checked); @override void onSelect(T item) { List list = loadingState.value.data!; - item.checked = !(item.checked ?? false); - if (item.checked!) { + item.checked = !item.checked; + if (item.checked) { rxCount.value++; } else { rxCount.value--; diff --git a/lib/pages/common/multi_select/multi_select_controller.dart b/lib/pages/common/multi_select/multi_select_controller.dart index 8b69a815c..30cd7f193 100644 --- a/lib/pages/common/multi_select/multi_select_controller.dart +++ b/lib/pages/common/multi_select/multi_select_controller.dart @@ -1,6 +1,8 @@ import 'package:PiliPlus/pages/common/common_list_controller.dart'; import 'package:PiliPlus/pages/common/multi_select/base.dart'; -abstract class MultiSelectController - extends CommonListController - with CommonMultiSelectMixin, DeleteItemMixin {} +abstract class MultiSelectController< + R, + T extends MultiSelectData +> = CommonListController + with CommonMultiSelectMixin, DeleteItemMixin; diff --git a/lib/pages/common/publish/common_publish_page.dart b/lib/pages/common/publish/common_publish_page.dart index 1b8659c4d..ab1b16980 100644 --- a/lib/pages/common/publish/common_publish_page.dart +++ b/lib/pages/common/publish/common_publish_page.dart @@ -200,6 +200,12 @@ abstract class CommonPublishPageState ); } + void onSubmitted(String value) { + if (enablePublish.value) { + onPublish(); + } + } + Future onPublish(); Future onCustomPublish({List? pictures}); diff --git a/lib/pages/common/publish/common_rich_text_pub_page.dart b/lib/pages/common/publish/common_rich_text_pub_page.dart index ff6d4e0a7..ebdf479d3 100644 --- a/lib/pages/common/publish/common_rich_text_pub_page.dart +++ b/lib/pages/common/publish/common_rich_text_pub_page.dart @@ -280,7 +280,7 @@ abstract class CommonRichTextPubPageState _onInsertUser(res, fromClick); } else if (res is Set) { for (var e in res) { - e.checked = null; + e.checked = false; _onInsertUser(e, fromClick); } res.clear(); diff --git a/lib/pages/common/search/common_search_controller.dart b/lib/pages/common/search/common_search_controller.dart index e835142ec..ef336f35a 100644 --- a/lib/pages/common/search/common_search_controller.dart +++ b/lib/pages/common/search/common_search_controller.dart @@ -17,7 +17,7 @@ abstract class CommonSearchController extends CommonListController { @override Future onRefresh() { if (editController.value.text.isEmpty) { - return Future.value(); + return Future.syncValue(null); } return super.onRefresh(); } diff --git a/lib/pages/download/controller.dart b/lib/pages/download/controller.dart index a05c0d84e..b1a20e169 100644 --- a/lib/pages/download/controller.dart +++ b/lib/pages/download/controller.dart @@ -78,7 +78,6 @@ class DownloadPageController extends GetxController title: '确定删除选中视频?', onConfirm: () async { SmartDialog.showLoading(); - final allChecked = this.allChecked.toSet(); final watchProgress = GStorage.watchProgress; for (var page in allChecked) { await watchProgress.deleteAll( diff --git a/lib/pages/download/detail/view.dart b/lib/pages/download/detail/view.dart index d0e1cec29..2086220db 100644 --- a/lib/pages/download/detail/view.dart +++ b/lib/pages/download/detail/view.dart @@ -27,7 +27,7 @@ class DownloadDetailPage extends StatefulWidget { final String pageId; final String title; - final ValueNotifier progress; + final ChangeNotifier progress; @override State createState() => _DownloadDetailPageState(); @@ -81,6 +81,7 @@ class _DownloadDetailPageState extends State @override Widget build(BuildContext context) { + final colorScheme = ColorScheme.of(context); return Obx(() { final enableMultiSelect = this.enableMultiSelect.value; return PopScope( @@ -118,7 +119,7 @@ class _DownloadDetailPageState extends State }, child: Text( '更新', - style: TextStyle(color: Get.theme.colorScheme.onSurface), + style: TextStyle(color: colorScheme.onSurface), ), ), ], diff --git a/lib/pages/download/detail/widgets/item.dart b/lib/pages/download/detail/widgets/item.dart index ef9c42e1f..2bf3c0ba2 100644 --- a/lib/pages/download/detail/widgets/item.dart +++ b/lib/pages/download/detail/widgets/item.dart @@ -41,7 +41,7 @@ class DetailItem extends StatelessWidget { }); final BiliDownloadEntryInfo entry; - final ValueNotifier? progress; + final ChangeNotifier? progress; final DownloadService downloadService; final VoidCallback? onDelete; final bool showTitle; @@ -210,9 +210,9 @@ class DetailItem extends StatelessWidget { type: PBadgeType.gray, ), if (progress != null) - ValueListenableBuilder( - valueListenable: progress!, - builder: (_, _, _) { + ListenableBuilder( + listenable: progress!, + builder: (_, _) { final progress = GStorage.watchProgress.get( cid.toString(), ); @@ -264,7 +264,7 @@ class DetailItem extends StatelessWidget { type: PBadgeType.gray, ), Positioned.fill( - child: selectMask(theme, checked ?? entry.checked ?? false), + child: selectMask(theme, checked ?? entry.checked), ), ], ), diff --git a/lib/pages/download/search/view.dart b/lib/pages/download/search/view.dart index 6513727ae..3272d0112 100644 --- a/lib/pages/download/search/view.dart +++ b/lib/pages/download/search/view.dart @@ -17,7 +17,7 @@ class DownloadSearchPage extends StatefulWidget { required this.progress, }); - final ValueNotifier progress; + final ChangeNotifier progress; @override State createState() => _DownloadSearchPageState(); diff --git a/lib/pages/download/view.dart b/lib/pages/download/view.dart index 8fe9e74c6..2921573ea 100644 --- a/lib/pages/download/view.dart +++ b/lib/pages/download/view.dart @@ -33,7 +33,7 @@ class DownloadPage extends StatefulWidget { class _DownloadPageState extends State { final _downloadService = Get.find(); final _controller = Get.put(DownloadPageController()); - final _progress = ValueNotifier(null); + final _progress = ChangeNotifier(); @override void dispose() { @@ -197,7 +197,7 @@ class _DownloadPageState extends State { entry.cid.toString(), ); }, - checked: item.checked ?? false, + checked: item.checked, onSelect: (_) => _controller.onSelect(item), controller: _controller, ); @@ -354,7 +354,7 @@ class _DownloadPageState extends State { top: 6.0, ), Positioned.fill( - child: selectMask(theme, pageInfo.checked ?? false), + child: selectMask(theme, pageInfo.checked), ), ], ), diff --git a/lib/pages/dynamics/widgets/author_panel.dart b/lib/pages/dynamics/widgets/author_panel.dart index 7790d11f0..cb7572169 100644 --- a/lib/pages/dynamics/widgets/author_panel.dart +++ b/lib/pages/dynamics/widgets/author_panel.dart @@ -46,14 +46,15 @@ class AuthorPanel extends StatelessWidget { }); Widget _buildAvatar(ModuleAuthorModel moduleAuthor) { - String? pendant = moduleAuthor.pendant?.image; + final pendant = moduleAuthor.pendant?.image; + final hasPendant = pendant != null && pendant.isNotEmpty; Widget avatar = PendantAvatar( avatar: moduleAuthor.face, - size: pendant.isNullOrEmpty ? 40 : 34, + size: hasPendant ? 34 : 40, officialType: null, // 已被注释 garbPendantImage: pendant, ); - if (!pendant.isNullOrEmpty) { + if (hasPendant) { avatar = Padding(padding: const EdgeInsets.all(3), child: avatar); } return avatar; @@ -477,7 +478,7 @@ class AuthorPanel extends StatelessWidget { ); } return UserHttp.dynamicReport( - mid: moduleAuthor.mid, + mid: moduleAuthor.mid!, dynId: item.idStr, reasonType: reasonType, reasonDesc: reasonType == 0 ? reasonDesc : null, diff --git a/lib/pages/dynamics_create/view.dart b/lib/pages/dynamics_create/view.dart index 3b8788fcd..0f86058da 100644 --- a/lib/pages/dynamics_create/view.dart +++ b/lib/pages/dynamics_create/view.dart @@ -665,34 +665,32 @@ class _CreateDynPanelState extends CommonRichTextPubPageState { selected: false, ); - Widget _buildEditWidget(ThemeData theme) => Form( - autovalidateMode: AutovalidateMode.onUserInteraction, - child: Listener( - onPointerUp: (event) { - if (readOnly.value) { - updatePanelType(PanelType.keyboard); - } - }, - child: Obx( - () => RichTextField( - key: key, - controller: editController, - minLines: 4, - maxLines: null, - focusNode: focusNode, - readOnly: readOnly.value, - onChanged: onChanged, - decoration: InputDecoration( - hintText: '说点什么吧', - hintStyle: TextStyle(color: theme.colorScheme.outline), - border: const OutlineInputBorder( - borderSide: BorderSide.none, - gapPadding: 0, - ), - contentPadding: EdgeInsets.zero, + Widget _buildEditWidget(ThemeData theme) => Listener( + onPointerUp: (event) { + if (readOnly.value) { + updatePanelType(PanelType.keyboard); + } + }, + child: Obx( + () => RichTextField( + key: key, + controller: editController, + minLines: 4, + maxLines: null, + focusNode: focusNode, + readOnly: readOnly.value, + onChanged: onChanged, + onSubmitted: onSubmitted, + decoration: InputDecoration( + hintText: '说点什么吧', + hintStyle: TextStyle(color: theme.colorScheme.outline), + border: const OutlineInputBorder( + borderSide: BorderSide.none, + gapPadding: 0, ), - // inputFormatters: [LengthLimitingTextInputFormatter(1000)], + contentPadding: EdgeInsets.zero, ), + // inputFormatters: [LengthLimitingTextInputFormatter(1000)], ), ), ); diff --git a/lib/pages/dynamics_mention/widgets/item.dart b/lib/pages/dynamics_mention/widgets/item.dart index 80ec92093..e82f7ab58 100644 --- a/lib/pages/dynamics_mention/widgets/item.dart +++ b/lib/pages/dynamics_mention/widgets/item.dart @@ -40,9 +40,9 @@ class DynMentionItem extends StatelessWidget { ), trailing: Checkbox( tristate: false, - value: item.checked ?? false, + value: item.checked, onChanged: (value) { - item.checked = value; + item.checked = value!; (context as Element).markNeedsBuild(); onCheck(value); }, diff --git a/lib/pages/dynamics_repost/view.dart b/lib/pages/dynamics_repost/view.dart index f7faa3d20..d4d678d21 100644 --- a/lib/pages/dynamics_repost/view.dart +++ b/lib/pages/dynamics_repost/view.dart @@ -226,33 +226,31 @@ class _RepostPanelState extends CommonRichTextPubPageState { ), ); - Widget _buildEditWidget(ThemeData theme) => Form( - autovalidateMode: AutovalidateMode.onUserInteraction, - child: Listener( - onPointerUp: (event) { - if (readOnly.value) { - updatePanelType(PanelType.keyboard); - } - }, - child: Obx( - () => RichTextField( - key: key, - controller: editController, - minLines: 4, - maxLines: null, - focusNode: focusNode, - readOnly: readOnly.value, - decoration: InputDecoration( - hintText: '说点什么吧', - hintStyle: TextStyle(color: theme.colorScheme.outline), - border: const OutlineInputBorder( - borderSide: BorderSide.none, - gapPadding: 0, - ), - contentPadding: EdgeInsets.zero, + Widget _buildEditWidget(ThemeData theme) => Listener( + onPointerUp: (event) { + if (readOnly.value) { + updatePanelType(PanelType.keyboard); + } + }, + child: Obx( + () => RichTextField( + key: key, + controller: editController, + minLines: 4, + maxLines: null, + focusNode: focusNode, + onSubmitted: onSubmitted, + readOnly: readOnly.value, + decoration: InputDecoration( + hintText: '说点什么吧', + hintStyle: TextStyle(color: theme.colorScheme.outline), + border: const OutlineInputBorder( + borderSide: BorderSide.none, + gapPadding: 0, ), - // inputFormatters: [LengthLimitingTextInputFormatter(1000)], + contentPadding: EdgeInsets.zero, ), + // inputFormatters: [LengthLimitingTextInputFormatter(1000)], ), ), ); diff --git a/lib/pages/fav/note/widget/item.dart b/lib/pages/fav/note/widget/item.dart index f05e263a5..b600dfb3b 100644 --- a/lib/pages/fav/note/widget/item.dart +++ b/lib/pages/fav/note/widget/item.dart @@ -71,7 +71,7 @@ class FavNoteItem extends StatelessWidget { Positioned.fill( child: selectMask( theme, - item.checked == true, + item.checked, ), ), ], diff --git a/lib/pages/fav/pgc/controller.dart b/lib/pages/fav/pgc/controller.dart index 10c3a5da9..9cc75ece6 100644 --- a/lib/pages/fav/pgc/controller.dart +++ b/lib/pages/fav/pgc/controller.dart @@ -81,7 +81,7 @@ class FavPgcController ctr.loadingState ..value.data!.insertAll( 0, - removeList.map((item) => item..checked = null), + removeList.map((item) => item..checked = false), ) ..refresh(); ctr.allSelected.value = false; diff --git a/lib/pages/fav/pgc/widget/item.dart b/lib/pages/fav/pgc/widget/item.dart index 30bc35f60..791acde1d 100644 --- a/lib/pages/fav/pgc/widget/item.dart +++ b/lib/pages/fav/pgc/widget/item.dart @@ -88,7 +88,7 @@ class FavPgcItem extends StatelessWidget { Positioned.fill( child: selectMask( theme, - item.checked == true, + item.checked, borderRadius: const BorderRadius.all( Radius.circular(4), ), diff --git a/lib/pages/fav/video/controller.dart b/lib/pages/fav/video/controller.dart index 544276188..716e5c719 100644 --- a/lib/pages/fav/video/controller.dart +++ b/lib/pages/fav/video/controller.dart @@ -18,7 +18,7 @@ class FavController extends CommonListController { Future queryData([bool isRefresh = true]) { if (!account.isLogin) { loadingState.value = const Error('账号未登录'); - return Future.value(); + return Future.syncValue(null); } return super.queryData(isRefresh); } diff --git a/lib/pages/fav_detail/controller.dart b/lib/pages/fav_detail/controller.dart index 54fb41410..d73db9f60 100644 --- a/lib/pages/fav_detail/controller.dart +++ b/lib/pages/fav_detail/controller.dart @@ -11,7 +11,6 @@ import 'package:PiliPlus/pages/common/multi_select/base.dart'; import 'package:PiliPlus/pages/common/multi_select/multi_select_controller.dart'; import 'package:PiliPlus/pages/fav_sort/view.dart'; import 'package:PiliPlus/utils/accounts.dart'; -import 'package:PiliPlus/utils/extension/iterable_ext.dart'; import 'package:PiliPlus/utils/extension/scroll_controller_ext.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; @@ -152,11 +151,10 @@ class FavDetailController ); void toViewPlayAll() { - if (loadingState.value.isSuccess) { - List? list = loadingState.value.data; - if (list.isNullOrEmpty) return; + if (loadingState.value case Success(:final response)) { + if (response == null || response.isEmpty) return; - for (FavDetailItemModel element in list!) { + for (FavDetailItemModel element in response) { if (element.ugc?.firstCid == null) { continue; } else { diff --git a/lib/pages/fav_detail/widget/fav_video_card.dart b/lib/pages/fav_detail/widget/fav_video_card.dart index d8db42fb4..881fdb162 100644 --- a/lib/pages/fav_detail/widget/fav_video_card.dart +++ b/lib/pages/fav_detail/widget/fav_video_card.dart @@ -135,7 +135,7 @@ class FavVideoCardH extends StatelessWidget { Positioned.fill( child: selectMask( theme, - item.checked == true, + item.checked, ), ), ], diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart index 2e214056b..95953247d 100644 --- a/lib/pages/history/widgets/item.dart +++ b/lib/pages/history/widgets/item.dart @@ -164,7 +164,7 @@ class HistoryItem extends StatelessWidget { ), ), Positioned.fill( - child: selectMask(theme, item.checked == true), + child: selectMask(theme, item.checked), ), ], ); diff --git a/lib/pages/later/base_controller.dart b/lib/pages/later/base_controller.dart index e934cf047..d25285e2a 100644 --- a/lib/pages/later/base_controller.dart +++ b/lib/pages/later/base_controller.dart @@ -8,9 +8,7 @@ class LaterBaseController extends GetxController { RxBool enableMultiSelect = false.obs; RxInt checkedCount = 0.obs; - RxMap counts = { - for (final item in LaterViewType.values) item: -1, - }.obs; + RxList counts = List.filled(LaterViewType.values.length, -1).obs; late double dx = 0; late final RxBool isPlayAll = Pref.enablePlayAll.obs; diff --git a/lib/pages/later/child_view.dart b/lib/pages/later/child_view.dart index 462edf8cf..ecffc852d 100644 --- a/lib/pages/later/child_view.dart +++ b/lib/pages/later/child_view.dart @@ -84,7 +84,7 @@ class _LaterViewChildPageState extends State 'sourceType': SourceType.watchLater, 'count': _laterController .baseCtr - .counts[LaterViewType.all], + .counts[LaterViewType.all.index], 'favTitle': '稍后再看', 'mediaId': _laterController.mid, 'desc': _laterController.asc.value, diff --git a/lib/pages/later/controller.dart b/lib/pages/later/controller.dart index 6154d7c61..74a0327e5 100644 --- a/lib/pages/later/controller.dart +++ b/lib/pages/later/controller.dart @@ -11,7 +11,6 @@ import 'package:PiliPlus/pages/common/multi_select/base.dart'; import 'package:PiliPlus/pages/common/multi_select/multi_select_controller.dart'; import 'package:PiliPlus/pages/later/base_controller.dart'; import 'package:PiliPlus/utils/accounts.dart'; -import 'package:PiliPlus/utils/extension/iterable_ext.dart'; import 'package:PiliPlus/utils/extension/scroll_controller_ext.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:flutter/material.dart'; @@ -120,13 +119,13 @@ class LaterController extends MultiSelectController @override List? getDataList(response) { - baseCtr.counts[laterViewType] = response.count ?? 0; + baseCtr.counts[laterViewType.index] = response.count ?? 0; return response.list; } @override void checkIsEnd(int length) { - if (length >= baseCtr.counts[laterViewType]!) { + if (length >= baseCtr.counts[laterViewType.index]) { isEnd = true; } } @@ -144,7 +143,7 @@ class LaterController extends MultiSelectController content: content, onConfirm: () async { var res = await UserHttp.toViewClear(cleanType); - if (res['status']) { + if (res.isSuccess) { onReload(); final restTypes = List.from(LaterViewType.values) ..remove(laterViewType); @@ -153,19 +152,20 @@ class LaterController extends MultiSelectController Get.find(tag: item.type.toString()).onReload(); } catch (_) {} } + SmartDialog.showToast('操作成功'); + } else { + res.toast(); } - SmartDialog.showToast(res['msg']); }, ); } // 稍后再看播放全部 void toViewPlayAll() { - if (loadingState.value.isSuccess) { - List? list = loadingState.value.data; - if (list.isNullOrEmpty) return; + if (loadingState.value case Success(:final response)) { + if (response == null || response.isEmpty) return; - for (LaterItemModel item in list!) { + for (LaterItemModel item in response) { if (item.cid == null || item.pgcLabel?.isNotEmpty == true) { continue; } else { @@ -176,7 +176,7 @@ class LaterController extends MultiSelectController title: item.title, extraArguments: { 'sourceType': SourceType.watchLater, - 'count': baseCtr.counts[LaterViewType.all], + 'count': baseCtr.counts[LaterViewType.all.index], 'favTitle': '稍后再看', 'mediaId': mid, 'desc': asc.value, @@ -190,8 +190,7 @@ class LaterController extends MultiSelectController @override ValueChanged? get updateCount => - (count) => baseCtr.counts[laterViewType] = - baseCtr.counts[laterViewType]! - count; + (count) => baseCtr.counts[laterViewType.index] -= count; @override Future onReload() { diff --git a/lib/pages/later/view.dart b/lib/pages/later/view.dart index fabcb2bb3..84b384d80 100644 --- a/lib/pages/later/view.dart +++ b/lib/pages/later/view.dart @@ -116,7 +116,7 @@ class _LaterPageState extends State // tabAlignment: TabAlignment.start, controller: _tabController, tabs: LaterViewType.values.map((item) { - final count = _baseCtr.counts[item]; + final count = _baseCtr.counts[item.index]; return Tab( text: '${item.title}${count != -1 ? '($count)' : ''}', ); @@ -216,7 +216,7 @@ class _LaterPageState extends State 'mediaId': mid, 'mid': mid, 'title': '稍后再看', - 'count': _baseCtr.counts[LaterViewType.all], + 'count': _baseCtr.counts[LaterViewType.all.index], }, ); }, diff --git a/lib/pages/later/widgets/video_card_h_later.dart b/lib/pages/later/widgets/video_card_h_later.dart index 97642a367..79eed1ff2 100644 --- a/lib/pages/later/widgets/video_card_h_later.dart +++ b/lib/pages/later/widgets/video_card_h_later.dart @@ -162,7 +162,7 @@ class VideoCardHLater extends StatelessWidget { Positioned.fill( child: selectMask( theme, - videoItem.checked == true, + videoItem.checked, ), ), ], diff --git a/lib/pages/live/view.dart b/lib/pages/live/view.dart index 855d0eae3..cad26f796 100644 --- a/lib/pages/live/view.dart +++ b/lib/pages/live/view.dart @@ -6,7 +6,6 @@ import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/common/widgets/pair.dart'; -import 'package:PiliPlus/common/widgets/self_sized_horizontal_list.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/common/image_type.dart'; import 'package:PiliPlus/models_new/live/live_feed_index/card_data_list_item.dart'; @@ -40,6 +39,14 @@ class _LivePageState extends CommonPageState @override bool get wantKeepAlive => true; + late TextScaler textScaler; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + textScaler = MediaQuery.textScalerOf(context); + } + @override Widget build(BuildContext context) { super.build(context); @@ -77,85 +84,88 @@ class _LivePageState extends CommonPageState Widget _buildTop(ThemeData theme, Pair data) { return SliverMainAxisGroup( slivers: [ - if (data.first != null) - SliverToBoxAdapter(child: _buildFollowList(theme, data.first!)), - if (data.second?.cardData?.areaEntranceV3?.list?.isNotEmpty == true) - SliverToBoxAdapter( - child: Row( - children: [ - Expanded( - child: SelfSizedHorizontalList( - gapSize: 12, - padding: const EdgeInsets.only( - top: 10, - bottom: 10, - right: 12, - ), - childBuilder: (index) { - late final item = data - .second! - .cardData! - .areaEntranceV3! - .list![index - 1]; - return Obx( - () { - final isCurr = index == controller.areaIndex.value; - return SearchText( - fontSize: 14, - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 3, - ), - text: index == 0 ? '推荐' : '${item.title}', - bgColor: isCurr - ? theme.colorScheme.secondaryContainer - : Colors.transparent, - textColor: isCurr - ? theme.colorScheme.onSecondaryContainer - : null, - onTap: (value) { - controller.onSelectArea( - index, - index == 0 ? null : item, + if (data.first != null) ..._buildFollowList(theme, data.first!), + if (data.second?.cardData?.areaEntranceV3?.list case final list?) + if (list.isNotEmpty) + SliverToBoxAdapter( + child: Row( + children: [ + Expanded( + child: SizedBox( + // 20+10+14*textScaler + height: 30.0 + textScaler.scale(14), + child: ListView.separated( + scrollDirection: Axis.horizontal, + separatorBuilder: (context, index) => + const SizedBox(width: 12), + padding: const EdgeInsets.only( + top: 10, + bottom: 10, + right: 12, + ), + itemBuilder: (context, index) { + late final item = list[index - 1]; + return Obx( + () { + final isCurr = + index == controller.areaIndex.value; + return SearchText( + fontSize: 14, + height: 1, + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 5, + ), + text: index == 0 ? '推荐' : '${item.title}', + bgColor: isCurr + ? theme.colorScheme.secondaryContainer + : Colors.transparent, + textColor: isCurr + ? theme.colorScheme.onSecondaryContainer + : null, + onTap: (value) { + controller.onSelectArea( + index, + index == 0 ? null : item, + ); + }, ); }, ); }, - ); - }, - itemCount: - data.second!.cardData!.areaEntranceV3!.list!.length + 1, + itemCount: list.length + 1, + ), + ), ), - ), - iconButton( - size: 26, - iconSize: 16, - context: context, - tooltip: '游戏赛事', - icon: const Icon(Icons.gamepad), - onPressed: () => Get.toNamed( - '/webview', - parameters: { - 'uaType': 'mob', - 'url': - 'https://www.bilibili.com/h5/match/data/home?navhide=1&${Utils.themeUrl(theme.brightness.isDark)}', - }, + iconButton( + size: 26, + iconSize: 16, + context: context, + tooltip: '游戏赛事', + icon: const Icon(Icons.gamepad), + onPressed: () => Get.toNamed( + '/webview', + parameters: { + 'uaType': 'mob', + 'url': + 'https://www.bilibili.com/h5/match/data/home?navhide=1&${Utils.themeUrl(theme.brightness.isDark)}', + }, + ), ), - ), - const SizedBox(width: 8), - iconButton( - size: 26, - iconSize: 16, - context: context, - tooltip: '全部标签', - icon: const Icon(Icons.widgets), - onPressed: () => Get.to(const LiveAreaPage()), - ), - ], - ), - ) - else - const SliverToBoxAdapter(child: SizedBox(height: 10)), + const SizedBox(width: 8), + iconButton( + size: 26, + iconSize: 16, + context: context, + tooltip: '全部标签', + icon: const Icon(Icons.widgets), + onPressed: () => Get.to(const LiveAreaPage()), + ), + ], + ), + ) + else + const SliverToBoxAdapter(child: SizedBox(height: 10)), ], ); } @@ -165,7 +175,7 @@ class _LivePageState extends CommonPageState crossAxisSpacing: StyleString.cardSpace, maxCrossAxisExtent: Grid.smallCardWidth, childAspectRatio: StyleString.aspectRatio, - mainAxisExtent: MediaQuery.textScalerOf(context).scale(90), + mainAxisExtent: textScaler.scale(90), ); Widget _buildBody(ThemeData theme, LoadingState loadingState) { @@ -180,38 +190,44 @@ class _LivePageState extends CommonPageState if (controller.newTags case final newTags?) if (newTags.isNotEmpty) SliverToBoxAdapter( - child: SelfSizedHorizontalList( - gapSize: 12, - padding: const EdgeInsets.only(bottom: 8), - childBuilder: (index) { - late final item = newTags[index]; - return Obx( - () { - final isCurr = index == controller.tagIndex.value; - return SearchText( - fontSize: 13, - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 3, - ), - text: '${item.name}', - bgColor: isCurr - ? theme.colorScheme.secondaryContainer - : Colors.transparent, - textColor: isCurr - ? theme.colorScheme.onSecondaryContainer - : null, - onTap: (value) { - controller.onSelectTag( - index, - item.sortType, - ); - }, - ); - }, - ); - }, - itemCount: newTags.length, + child: SizedBox( + // 8+10+13*textScaler + height: 18.0 + textScaler.scale(13), + child: ListView.separated( + scrollDirection: Axis.horizontal, + separatorBuilder: (_, _) => const SizedBox(width: 12), + padding: const EdgeInsets.only(bottom: 8), + itemBuilder: (context, index) { + late final item = newTags[index]; + return Obx( + () { + final isCurr = index == controller.tagIndex.value; + return SearchText( + height: 1, + fontSize: 13, + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 5, + ), + text: '${item.name}', + bgColor: isCurr + ? theme.colorScheme.secondaryContainer + : Colors.transparent, + textColor: isCurr + ? theme.colorScheme.onSecondaryContainer + : null, + onTap: (value) { + controller.onSelectTag( + index, + item.sortType, + ); + }, + ); + }, + ); + }, + itemCount: newTags.length, + ), ), ), response != null && response.isNotEmpty @@ -241,12 +257,10 @@ class _LivePageState extends CommonPageState }; } - Widget _buildFollowList(ThemeData theme, LiveCardList item) { - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( + List _buildFollowList(ThemeData theme, LiveCardList item) { + return [ + SliverToBoxAdapter( + child: Row( children: [ Text.rich( TextSpan( @@ -277,66 +291,76 @@ class _LivePageState extends CommonPageState ), ], ), - if (item.cardData?.myIdolV1?.list?.isNotEmpty == true) - _buildFollowBody(theme, item.cardData!.myIdolV1!.list!), - ], - ); + ), + if (item.cardData?.myIdolV1?.list case final list?) + if (list.isNotEmpty) _buildFollowBody(theme, list), + ]; } Widget _buildFollowBody(ThemeData theme, List followList) { - return SelfSizedHorizontalList( - gapSize: 5, - padding: EdgeInsets.zero, - childBuilder: (index) { - final item = followList[index]; - return SizedBox( - width: 65, - child: GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () => PageUtils.toLiveRoom(item.roomid), - onLongPress: () { - Feedback.forLongPress(context); - Get.toNamed('/member?mid=${item.uid}'); - }, - onSecondaryTap: PlatformUtils.isMobile - ? null - : () => Get.toNamed('/member?mid=${item.uid}'), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const SizedBox(height: 8), - Container( - margin: const EdgeInsets.all(2), - padding: const EdgeInsets.all(2), - decoration: BoxDecoration( - border: Border.all( - width: 1.5, - color: theme.colorScheme.primary, - strokeAlign: BorderSide.strokeAlignOutside, + return SliverToBoxAdapter( + child: SizedBox( + // 8+4+4+45+4+12*textScaler + height: 65.0 + textScaler.scale(12), + child: CustomScrollView( + scrollDirection: Axis.horizontal, + slivers: [ + SliverFixedExtentList.builder( + itemExtent: 70, + itemCount: followList.length, + itemBuilder: (context, index) { + final item = followList[index]; + return SizedBox( + width: 65, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => PageUtils.toLiveRoom(item.roomid), + onLongPress: () { + Feedback.forLongPress(context); + Get.toNamed('/member?mid=${item.uid}'); + }, + onSecondaryTap: PlatformUtils.isMobile + ? null + : () => Get.toNamed('/member?mid=${item.uid}'), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 8), + Container( + margin: const EdgeInsets.all(2), + padding: const EdgeInsets.all(2), + decoration: BoxDecoration( + border: Border.all( + width: 1.5, + color: theme.colorScheme.primary, + strokeAlign: BorderSide.strokeAlignOutside, + ), + shape: BoxShape.circle, + ), + child: NetworkImgLayer( + type: ImageType.avatar, + width: 45, + height: 45, + src: item.face, + ), + ), + const SizedBox(height: 4), + Text( + item.uname!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle(fontSize: 12, height: 1), + textAlign: TextAlign.center, + ), + ], ), - shape: BoxShape.circle, ), - child: NetworkImgLayer( - type: ImageType.avatar, - width: 45, - height: 45, - src: item.face, - ), - ), - const SizedBox(height: 4), - Text( - item.uname!, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: const TextStyle(fontSize: 12), - textAlign: TextAlign.center, - ), - ], + ); + }, ), - ), - ); - }, - itemCount: followList.length, + ], + ), + ), ); } } diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index c31fe9b32..a6bc7295c 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -107,6 +107,7 @@ class LiveRoomController extends GetxController { late final bool isLogin; late final int mid; + late final int mainMid = Accounts.main.mid; String? videoUrl; bool? isPlaying; @@ -432,7 +433,8 @@ class LiveRoomController extends GetxController { ? Colors.white : DmUtils.decimalToColor(extra['color']), type: DmUtils.getPosition(extra['mode']), - selfSend: extra['send_from_me'] ?? false, + // extra['send_from_me'] is invalid + selfSend: uid == mainMid, extra: LiveDanmaku( id: extra['id_str'], mid: uid, diff --git a/lib/pages/live_room/send_danmaku/view.dart b/lib/pages/live_room/send_danmaku/view.dart index 3c373fd60..3b8126416 100644 --- a/lib/pages/live_room/send_danmaku/view.dart +++ b/lib/pages/live_room/send_danmaku/view.dart @@ -94,32 +94,30 @@ class _ReplyPageState extends CommonRichTextPubPageState { left: 15, bottom: 10, ), - child: Form( - autovalidateMode: AutovalidateMode.onUserInteraction, - child: Listener( - onPointerUp: (event) { - if (readOnly.value) { - updatePanelType(PanelType.keyboard); - } - }, - child: Obx( - () => RichTextField( - key: key, - controller: editController, - minLines: 1, - maxLines: 2, - autofocus: false, - readOnly: readOnly.value, - onChanged: onChanged, - focusNode: focusNode, - decoration: const InputDecoration( - hintText: "输入弹幕内容", - border: InputBorder.none, - hintStyle: TextStyle(fontSize: 14), - ), - style: theme.textTheme.bodyLarge, - // inputFormatters: [LengthLimitingTextInputFormatter(20)], + child: Listener( + onPointerUp: (event) { + if (readOnly.value) { + updatePanelType(PanelType.keyboard); + } + }, + child: Obx( + () => RichTextField( + key: key, + controller: editController, + minLines: 1, + maxLines: 2, + autofocus: false, + readOnly: readOnly.value, + onChanged: onChanged, + onSubmitted: onSubmitted, + focusNode: focusNode, + decoration: const InputDecoration( + hintText: "输入弹幕内容", + border: InputBorder.none, + hintStyle: TextStyle(fontSize: 14), ), + style: theme.textTheme.bodyLarge, + // inputFormatters: [LengthLimitingTextInputFormatter(20)], ), ), ), @@ -182,6 +180,6 @@ class _ReplyPageState extends CommonRichTextPubPageState { @override Future onMention([bool fromClick = false]) { - return Future.value(); + return Future.syncValue(null); } } diff --git a/lib/pages/login/controller.dart b/lib/pages/login/controller.dart index 134c1fd10..3e52fcf89 100644 --- a/lib/pages/login/controller.dart +++ b/lib/pages/login/controller.dart @@ -738,13 +738,9 @@ class LoginPageController extends GetxController context: context, builder: (context) => AlertDialog( title: const Text('选择账号mid, 为0时使用匿名'), - titlePadding: const EdgeInsets.only(left: 22, top: 16, right: 22), - contentPadding: const EdgeInsets.symmetric(vertical: 5), - actionsPadding: const EdgeInsets.only( - left: 16, - right: 16, - bottom: 10, - ), + titlePadding: const .only(left: 22, top: 16, right: 22), + contentPadding: const .symmetric(vertical: 5), + actionsPadding: const .only(left: 16, right: 16, bottom: 10), content: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/pages/member_dynamics/controller.dart b/lib/pages/member_dynamics/controller.dart index 21bef6320..f5fae269a 100644 --- a/lib/pages/member_dynamics/controller.dart +++ b/lib/pages/member_dynamics/controller.dart @@ -27,7 +27,7 @@ class MemberDynamicsController @override Future queryData([bool isRefresh = true]) { if (!isRefresh && (isEnd || offset == '-1')) { - return Future.value(); + return Future.syncValue(null); } return super.queryData(isRefresh); } diff --git a/lib/pages/member_favorite/widget/item.dart b/lib/pages/member_favorite/widget/item.dart index 44761b263..dbd3af711 100644 --- a/lib/pages/member_favorite/widget/item.dart +++ b/lib/pages/member_favorite/widget/item.dart @@ -34,7 +34,7 @@ class MemberFavItem extends StatelessWidget { } if (item.type == 0 || item.type == 11) { - final bool? isDeleted = await Get.toNamed( + final isDeleted = await Get.toNamed( '/favDetail', parameters: { 'mediaId': item.id.toString(), diff --git a/lib/pages/member_profile/view.dart b/lib/pages/member_profile/view.dart index 4f67a88b0..e35a55bf3 100644 --- a/lib/pages/member_profile/view.dart +++ b/lib/pages/member_profile/view.dart @@ -15,7 +15,6 @@ import 'package:PiliPlus/utils/app_sign.dart'; import 'package:PiliPlus/utils/date_utils.dart'; import 'package:PiliPlus/utils/extension/file_ext.dart'; import 'package:PiliPlus/utils/extension/iterable_ext.dart'; -import 'package:PiliPlus/utils/extension/string_ext.dart'; import 'package:PiliPlus/utils/extension/theme_ext.dart'; import 'package:PiliPlus/utils/image_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart'; @@ -351,12 +350,12 @@ class _EditProfilePageState extends State { dynamic datum, }) async { final accessKey = Accounts.main.accessKey; - if (accessKey.isNullOrEmpty) { + if (accessKey == null || accessKey.isEmpty) { SmartDialog.showToast('请退出账号后重新登录'); return; } - Map data = { - 'access_key': accessKey!, + final data = { + 'access_key': accessKey, 'build': '2001100', 'c_locale': 'zh_CN', 'channel': 'master', diff --git a/lib/pages/member_shop/view.dart b/lib/pages/member_shop/view.dart index ed853c96d..defc224bd 100644 --- a/lib/pages/member_shop/view.dart +++ b/lib/pages/member_shop/view.dart @@ -6,7 +6,6 @@ import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models_new/space/space_shop/item.dart'; import 'package:PiliPlus/pages/member_shop/controller.dart'; import 'package:PiliPlus/pages/member_shop/widgets/item.dart'; -import 'package:PiliPlus/utils/extension/iterable_ext.dart'; import 'package:PiliPlus/utils/grid.dart'; import 'package:PiliPlus/utils/waterfall.dart'; import 'package:flutter/material.dart'; @@ -80,7 +79,7 @@ class _MemberShopState extends State ), ); case Success(:var response): - if (response.isNullOrEmpty) { + if (response == null || response.isEmpty) { return HttpError(onReload: _controller.onReload); } Widget sliver = SliverWaterfallFlow( @@ -92,7 +91,7 @@ class _MemberShopState extends State maxWidth: _maxWidth, ); }, - childCount: response!.length, + childCount: response.length, ), ); if (_controller.showMoreTab == true) { diff --git a/lib/pages/member_video/controller.dart b/lib/pages/member_video/controller.dart index 32e6e17d9..7682fe99b 100644 --- a/lib/pages/member_video/controller.dart +++ b/lib/pages/member_video/controller.dart @@ -178,12 +178,10 @@ class MemberVideoCtr return; } - if (loadingState.value.isSuccess) { - List? list = loadingState.value.data; + if (loadingState.value case Success(:final response)) { + if (response == null || response.isEmpty) return; - if (list.isNullOrEmpty) return; - - for (SpaceArchiveItem element in list!) { + for (SpaceArchiveItem element in response) { if (element.cid == null) { continue; } else { diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart index 9c388c0a4..69eb93d05 100644 --- a/lib/pages/mine/controller.dart +++ b/lib/pages/mine/controller.dart @@ -287,7 +287,7 @@ class MineController extends CommonDataController @override Future onRefresh() { if (!accountService.isLogin.value) { - return Future.value(); + return Future.syncValue(null); } queryUserInfo(); return super.onRefresh(); diff --git a/lib/pages/mine/view.dart b/lib/pages/mine/view.dart index 15772e358..62eb376b2 100644 --- a/lib/pages/mine/view.dart +++ b/lib/pages/mine/view.dart @@ -15,6 +15,7 @@ import 'package:PiliPlus/pages/login/controller.dart'; import 'package:PiliPlus/pages/main/controller.dart'; import 'package:PiliPlus/pages/mine/controller.dart'; import 'package:PiliPlus/pages/mine/widgets/item.dart'; +import 'package:PiliPlus/utils/extension/get_ext.dart'; import 'package:PiliPlus/utils/extension/theme_ext.dart'; import 'package:PiliPlus/utils/platform_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; @@ -34,7 +35,7 @@ class MinePage extends StatefulWidget { class _MediaPageState extends CommonPageState with AutomaticKeepAliveClientMixin { @override - MineController controller = Get.put(MineController()); + MineController controller = Get.putOrFind(MineController.new); late final MainController _mainController = Get.find(); @override diff --git a/lib/pages/msg_feed_top/like_me/controller.dart b/lib/pages/msg_feed_top/like_me/controller.dart index 666e778bd..3edf0db9c 100644 --- a/lib/pages/msg_feed_top/like_me/controller.dart +++ b/lib/pages/msg_feed_top/like_me/controller.dart @@ -27,7 +27,7 @@ class LikeMeController @override Future queryData([bool isRefresh = true]) { if (!isRefresh && isEnd) { - return Future.value(); + return Future.syncValue(null); } return super.queryData(isRefresh); } diff --git a/lib/pages/pgc/controller.dart b/lib/pages/pgc/controller.dart index abda72362..831e166c9 100644 --- a/lib/pages/pgc/controller.dart +++ b/lib/pages/pgc/controller.dart @@ -8,7 +8,6 @@ import 'package:PiliPlus/models_new/pgc/pgc_index_result/list.dart'; import 'package:PiliPlus/models_new/pgc/pgc_timeline/result.dart'; import 'package:PiliPlus/pages/common/common_list_controller.dart'; import 'package:PiliPlus/services/account_service.dart'; -import 'package:PiliPlus/utils/extension/iterable_ext.dart'; import 'package:PiliPlus/utils/extension/scroll_controller_ext.dart'; import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:flutter/material.dart'; @@ -108,7 +107,7 @@ class PgcController List? list = data.list; followCount.value = data.total ?? -1; - if (list.isNullOrEmpty) { + if (list == null || list.isEmpty) { followEnd = true; if (isRefresh) { followState.value = Success(list); @@ -118,13 +117,13 @@ class PgcController } if (isRefresh) { - if (list!.length >= followCount.value) { + if (list.length >= followCount.value) { followEnd = true; } followState.value = Success(list); followController?.animToTop(); } else if (followState.value.isSuccess) { - final currentList = followState.value.data!..addAll(list!); + final currentList = followState.value.data!..addAll(list); if (currentList.length >= followCount.value) { followEnd = true; } diff --git a/lib/pages/search/controller.dart b/lib/pages/search/controller.dart index f4edb0f15..9e6bf4b6d 100644 --- a/lib/pages/search/controller.dart +++ b/lib/pages/search/controller.dart @@ -18,20 +18,21 @@ import 'package:get/get.dart'; import 'package:stream_transform/stream_transform.dart'; mixin DebounceStreamMixin { - Duration duration = const Duration(milliseconds: 200); + final Duration duration = const Duration(milliseconds: 200); StreamController? ctr; - StreamSubscription? sub; + StreamSubscription? _sub; void onValueChanged(T value); void subInit() { - ctr = StreamController(); - sub = ctr!.stream.debounce(duration, trailing: true).listen(onValueChanged); + _sub = (ctr = StreamController()).stream + .debounce(duration, trailing: true) + .listen(onValueChanged); } void subDispose() { - sub?.cancel(); + _sub?.cancel(); ctr?.close(); - sub = null; + _sub = null; ctr = null; } } diff --git a/lib/pages/search/widgets/search_text.dart b/lib/pages/search/widgets/search_text.dart index 4883c8470..83a5f2e38 100644 --- a/lib/pages/search/widgets/search_text.dart +++ b/lib/pages/search/widgets/search_text.dart @@ -10,6 +10,7 @@ class SearchText extends StatelessWidget { final Color? textColor; final TextAlign? textAlign; final EdgeInsetsGeometry? padding; + final double? height; const SearchText({ super.key, @@ -21,6 +22,7 @@ class SearchText extends StatelessWidget { this.textColor, this.textAlign, this.padding, + this.height, }); @override @@ -46,6 +48,7 @@ class SearchText extends StatelessWidget { textAlign: textAlign, style: TextStyle( fontSize: fontSize, + height: height, color: textColor ?? colorScheme.onSurfaceVariant, ), ), diff --git a/lib/pages/setting/models/style_settings.dart b/lib/pages/setting/models/style_settings.dart index 55d5f83c7..0c11bfb11 100644 --- a/lib/pages/setting/models/style_settings.dart +++ b/lib/pages/setting/models/style_settings.dart @@ -669,7 +669,7 @@ List get styleSettings => [ ), NormalModel( onTap: (context, setState) async { - final double? result = await Get.toNamed('/fontSizeSetting'); + final result = await Get.toNamed('/fontSizeSetting'); if (result != null) { Get.put(ColorSelectController()).currentTextScale.value = result; } diff --git a/lib/pages/setting/pages/color_select.dart b/lib/pages/setting/pages/color_select.dart index 7cbc3b1f3..a8f8bdd91 100644 --- a/lib/pages/setting/pages/color_select.dart +++ b/lib/pages/setting/pages/color_select.dart @@ -1,4 +1,7 @@ +import 'dart:io' show Platform; + import 'package:PiliPlus/common/widgets/color_palette.dart'; +import 'package:PiliPlus/main.dart' show MyApp; import 'package:PiliPlus/models/common/nav_bar_config.dart'; import 'package:PiliPlus/models/common/theme/theme_color_type.dart'; import 'package:PiliPlus/models/common/theme/theme_type.dart'; @@ -163,19 +166,23 @@ class _ColorSelectPageState extends State { ), ), ), - Obx( - () => CheckboxListTile( - title: const Text('动态取色'), - controlAffinity: ListTileControlAffinity.leading, - value: ctr.dynamicColor.value, - onChanged: (val) { - ctr - ..dynamicColor.value = val! - ..setting.put(SettingBoxKey.dynamicColor, val); - Get.forceAppUpdate(); - }, + if (!Platform.isIOS) + Obx( + () => CheckboxListTile( + title: const Text('动态取色'), + controlAffinity: ListTileControlAffinity.leading, + value: ctr.dynamicColor.value, + onChanged: (val) async { + ctr + ..dynamicColor.value = val! + ..setting.put(SettingBoxKey.dynamicColor, val); + if (val) { + await MyApp.initPlatformState(); + } + Get.forceAppUpdate(); + }, + ), ), - ), Padding( padding: padding, child: AnimatedSize( diff --git a/lib/pages/setting/view.dart b/lib/pages/setting/view.dart index a39ad2b8d..8ae174198 100644 --- a/lib/pages/setting/view.dart +++ b/lib/pages/setting/view.dart @@ -14,7 +14,6 @@ import 'package:PiliPlus/pages/setting/widgets/multi_select_dialog.dart'; import 'package:PiliPlus/pages/webdav/view.dart'; import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/accounts/account.dart'; -import 'package:PiliPlus/utils/extension/iterable_ext.dart'; import 'package:PiliPlus/utils/extension/size_ext.dart'; import 'package:flutter/material.dart' hide ListTile; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -223,9 +222,9 @@ class _SettingPageState extends State { ); }, ); - if (!context.mounted || result.isNullOrEmpty) return; + if (!context.mounted || result == null || result.isEmpty) return; Future logout() { - _noAccount.value = result!.length == Accounts.account.length; + _noAccount.value = result.length == Accounts.account.length; return Accounts.deleteAll(result); } @@ -236,7 +235,7 @@ class _SettingPageState extends State { return AlertDialog( title: const Text('提示'), content: Text( - "确认要退出以下账号登录吗\n\n${result!.map((i) => i.mid.toString()).join('\n')}", + "确认要退出以下账号登录吗\n\n${result.map((i) => i.mid.toString()).join('\n')}", ), actions: [ TextButton( diff --git a/lib/pages/subscription/controller.dart b/lib/pages/subscription/controller.dart index 1305ec51c..aef855ba5 100644 --- a/lib/pages/subscription/controller.dart +++ b/lib/pages/subscription/controller.dart @@ -22,7 +22,7 @@ class SubController extends CommonListController { Future queryData([bool isRefresh = true]) { if (!account.isLogin) { loadingState.value = const Error('账号未登录'); - return Future.value(); + return Future.syncValue(null); } return super.queryData(isRefresh); } diff --git a/lib/pages/video/controller.dart b/lib/pages/video/controller.dart index b771a618e..49bca99a1 100644 --- a/lib/pages/video/controller.dart +++ b/lib/pages/video/controller.dart @@ -1027,7 +1027,7 @@ class VideoDetailController extends GetxController if (isPlaying) { await plPlayerController.pause(); } - await Navigator.of(Get.context!).push( + await Get.key.currentState!.push( GetDialogRoute( pageBuilder: (buildContext, animation, secondaryAnimation) { return SendDanmakuPanel( diff --git a/lib/pages/video/introduction/ugc/view.dart b/lib/pages/video/introduction/ugc/view.dart index 42d0dc31c..adc48bf9c 100644 --- a/lib/pages/video/introduction/ugc/view.dart +++ b/lib/pages/video/introduction/ugc/view.dart @@ -346,10 +346,10 @@ class _UgcIntroPanelState extends State { ], Obx(() { final videoTags = introController.videoTags.value; - if (videoTags.isNullOrEmpty) { + if (videoTags == null || videoTags.isEmpty) { return const SizedBox.shrink(); } - return _buildTags(videoTags!); + return _buildTags(videoTags); }), ]; diff --git a/lib/pages/video/member/controller.dart b/lib/pages/video/member/controller.dart index 0cfb92041..098275237 100644 --- a/lib/pages/video/member/controller.dart +++ b/lib/pages/video/member/controller.dart @@ -109,7 +109,7 @@ class HorizontalMemberPageController @override Future onRefresh() { if (!hasPrev) { - return Future.value(); + return Future.syncValue(null); } isLoadPrevious = true; return queryData(); diff --git a/lib/pages/video/pay_coins/view.dart b/lib/pages/video/pay_coins/view.dart index 1371db3c1..9157d879e 100644 --- a/lib/pages/video/pay_coins/view.dart +++ b/lib/pages/video/pay_coins/view.dart @@ -32,7 +32,7 @@ class PayCoinsPage extends StatefulWidget { int copyright = 1, bool hasCoin = false, }) { - Navigator.of(Get.context!).push( + Get.key.currentState!.push( GetDialogRoute( pageBuilder: (buildContext, animation, secondaryAnimation) { return PayCoinsPage( diff --git a/lib/pages/video/post_panel/view.dart b/lib/pages/video/post_panel/view.dart index fa3a41206..cfe7a90de 100644 --- a/lib/pages/video/post_panel/view.dart +++ b/lib/pages/video/post_panel/view.dart @@ -13,7 +13,6 @@ import 'package:PiliPlus/pages/video/controller.dart'; import 'package:PiliPlus/pages/video/post_panel/popup_menu_text.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/utils/duration_utils.dart'; -import 'package:PiliPlus/utils/extension/iterable_ext.dart'; import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart'; @@ -251,7 +250,7 @@ class _PostPanelState extends State @override Widget buildList(ThemeData theme) { - if (list.isNullOrEmpty) { + if (list.isEmpty) { return errorWidget(); } final bottom = MediaQuery.viewPaddingOf(context).bottom; diff --git a/lib/pages/video/reply/widgets/reply_item_grpc.dart b/lib/pages/video/reply/widgets/reply_item_grpc.dart index 9df563cb2..c49d414d2 100644 --- a/lib/pages/video/reply/widgets/reply_item_grpc.dart +++ b/lib/pages/video/reply/widgets/reply_item_grpc.dart @@ -9,7 +9,7 @@ import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/pendant_avatar.dart'; import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart' show ReplyInfo, ReplyControl, Content, Url; -import 'package:PiliPlus/http/init.dart'; +import 'package:PiliPlus/http/reply.dart'; import 'package:PiliPlus/http/video.dart'; import 'package:PiliPlus/models/common/badge_type.dart'; import 'package:PiliPlus/models/common/image_type.dart'; @@ -31,7 +31,6 @@ import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:PiliPlus/utils/url_utils.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:cached_network_image/cached_network_image.dart'; -import 'package:dio/dio.dart'; import 'package:fixnum/fixnum.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/gestures.dart'; @@ -938,28 +937,17 @@ class ReplyItemGrpc extends StatelessWidget { context, ReportOptions.commentReport, (reasonType, reasonDesc, banUid) async { - final res = await Request().post( - '/x/v2/reply/report', - data: { - 'add_blacklist': banUid, - 'csrf': Accounts.main.csrf, - 'gaia_source': 'main_h5', - 'oid': item.oid, - 'platform': 'android', - 'reason': reasonType, - 'rpid': item.id, - 'scene': 'main', - 'type': 1, - if (reasonType == 0) 'content': reasonDesc!, - }, - options: Options( - contentType: Headers.formUrlEncodedContentType, - ), + final res = await ReplyHttp.report( + rpid: item.id, + oid: item.oid, + reasonType: reasonType, + reasonDesc: reasonDesc, + banUid: banUid, ); - if (res.data['code'] == 0) { + if (res.isSuccess) { onDelete(); } - return res.data as Map; + return res; }, ); }, diff --git a/lib/pages/video/reply_new/view.dart b/lib/pages/video/reply_new/view.dart index 125deedb5..b83885604 100644 --- a/lib/pages/video/reply_new/view.dart +++ b/lib/pages/video/reply_new/view.dart @@ -142,31 +142,29 @@ class _ReplyPageState extends CommonRichTextPubPageState { left: 15, bottom: 10, ), - child: Form( - autovalidateMode: AutovalidateMode.onUserInteraction, - child: Listener( - onPointerUp: (event) { - if (readOnly.value) { - updatePanelType(PanelType.keyboard); - } - }, - child: Obx( - () => RichTextField( - key: key, - controller: editController, - minLines: 4, - maxLines: 8, - autofocus: false, - readOnly: readOnly.value, - onChanged: onChanged, - focusNode: focusNode, - decoration: InputDecoration( - hintText: widget.hint ?? "输入回复内容", - border: InputBorder.none, - hintStyle: const TextStyle(fontSize: 14), - ), - style: themeData.textTheme.bodyLarge, + child: Listener( + onPointerUp: (event) { + if (readOnly.value) { + updatePanelType(PanelType.keyboard); + } + }, + child: Obx( + () => RichTextField( + key: key, + controller: editController, + minLines: 4, + maxLines: 8, + autofocus: false, + readOnly: readOnly.value, + onChanged: onChanged, + onSubmitted: onSubmitted, + focusNode: focusNode, + decoration: InputDecoration( + hintText: widget.hint ?? "输入回复内容", + border: InputBorder.none, + hintStyle: const TextStyle(fontSize: 14), ), + style: themeData.textTheme.bodyLarge, ), ), ), diff --git a/lib/pages/video/send_danmaku/view.dart b/lib/pages/video/send_danmaku/view.dart index 59bd5d922..4bc9090f9 100644 --- a/lib/pages/video/send_danmaku/view.dart +++ b/lib/pages/video/send_danmaku/view.dart @@ -369,40 +369,33 @@ class _SendDanmakuPanelState extends CommonTextPubPageState { ), const SizedBox(width: 12), Expanded( - child: Form( - autovalidateMode: AutovalidateMode.onUserInteraction, - child: Listener( - onPointerUp: (event) { - if (readOnly.value) { - updatePanelType(PanelType.keyboard); - } - }, - child: Obx( - () => TextField( - controller: editController, - autofocus: false, - readOnly: readOnly.value, - inputFormatters: [ - LengthLimitingTextInputFormatter(100), - ], - onChanged: onChanged, - textInputAction: TextInputAction.send, - onSubmitted: (value) { - if (value.trim().isNotEmpty) { - onPublish(); - } - }, - focusNode: focusNode, - decoration: InputDecoration( - hintText: "输入弹幕内容", - border: InputBorder.none, - hintStyle: TextStyle( - fontSize: 15, - color: themeData.colorScheme.outline, - ), + child: Listener( + onPointerUp: (event) { + if (readOnly.value) { + updatePanelType(PanelType.keyboard); + } + }, + child: Obx( + () => TextField( + controller: editController, + autofocus: false, + readOnly: readOnly.value, + inputFormatters: [ + LengthLimitingTextInputFormatter(100), + ], + onChanged: onChanged, + textInputAction: TextInputAction.send, + onSubmitted: onSubmitted, + focusNode: focusNode, + decoration: InputDecoration( + hintText: "输入弹幕内容", + border: InputBorder.none, + hintStyle: TextStyle( + fontSize: 15, + color: themeData.colorScheme.outline, ), - style: themeData.textTheme.bodyLarge, ), + style: themeData.textTheme.bodyLarge, ), ), ), diff --git a/lib/pages/video/widgets/header_control.dart b/lib/pages/video/widgets/header_control.dart index 1214124c1..240a7ab41 100644 --- a/lib/pages/video/widgets/header_control.dart +++ b/lib/pages/video/widgets/header_control.dart @@ -1436,6 +1436,7 @@ class HeaderControlState extends State context: context, builder: (context) { final state = player.state; + final colorScheme = ColorScheme.of(context); return AlertDialog( title: const Text('播放信息'), contentPadding: const EdgeInsets.only(top: 16), @@ -1562,7 +1563,7 @@ class HeaderControlState extends State onPressed: Get.back, child: Text( '确定', - style: TextStyle(color: Get.theme.colorScheme.outline), + style: TextStyle(color: colorScheme.outline), ), ), ], diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index c6fa670a7..b613d2cf9 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -274,6 +274,7 @@ class _WhisperDetailPageState minLines: 1, maxLines: 4, onChanged: onChanged, + onSubmitted: onSubmitted, textInputAction: TextInputAction.newline, decoration: InputDecoration( filled: true, @@ -375,7 +376,7 @@ class _WhisperDetailPageState @override Future onMention([bool fromClick = false]) { - return Future.value(); + return Future.syncValue(null); } @override diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 5fcde3f31..0c6921acf 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -1770,9 +1770,6 @@ class _PLVideoPlayerState extends State .inSeconds; final int buffer = plPlayerController.bufferedSeconds.value; - if (value > max || max <= 0) { - return const SizedBox.shrink(); - } return ProgressBar( progress: Duration(seconds: value), buffered: Duration(seconds: buffer), diff --git a/lib/plugin/pl_player/widgets/bottom_control.dart b/lib/plugin/pl_player/widgets/bottom_control.dart index ad88157c4..f5b0fbc5a 100644 --- a/lib/plugin/pl_player/widgets/bottom_control.dart +++ b/lib/plugin/pl_player/widgets/bottom_control.dart @@ -59,9 +59,6 @@ class BottomControl extends StatelessWidget { final child = Obx(() { final int value = controller.sliderPositionSeconds.value; final int max = controller.durationSeconds.value.inSeconds; - if (value > max || max <= 0) { - return const SizedBox.shrink(); - } return ProgressBar( progress: Duration(seconds: value), buffered: Duration(seconds: controller.bufferedSeconds.value), diff --git a/lib/services/download/download_service.dart b/lib/services/download/download_service.dart index 60a8a5cf4..831c0a013 100644 --- a/lib/services/download/download_service.dart +++ b/lib/services/download/download_service.dart @@ -34,7 +34,7 @@ class DownloadService extends GetxService { final _lock = Lock(); - final flagNotifier = {}; + final flagNotifier = SetNotifier(); final waitDownloadQueue = RxList(); final downloadList = []; @@ -592,7 +592,9 @@ class DownloadService extends GetxService { } } -extension SetExt on Set { +typedef SetNotifier = Set; + +extension SetNotifierExt on SetNotifier { void refresh() { for (var i in this) { i(); diff --git a/lib/services/logger.dart b/lib/services/logger.dart index e08794864..039b1afca 100644 --- a/lib/services/logger.dart +++ b/lib/services/logger.dart @@ -1,6 +1,5 @@ import 'dart:io'; -import 'package:PiliPlus/utils/extension/file_ext.dart'; import 'package:PiliPlus/utils/json_file_handler.dart'; import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:catcher_2/catcher_2.dart'; @@ -39,9 +38,6 @@ abstract final class LoggerUtils { final File file = File(filename); if (!file.existsSync()) { await file.create(recursive: true); - // TODO: remove after next two versions - final oldFile = File(p.join(dir, '.pili_logs')); - if (oldFile.existsSync()) oldFile.tryDel(); } return _logFile = file; } diff --git a/lib/tcp/live.dart b/lib/tcp/live.dart index f63090c1a..769499663 100644 --- a/lib/tcp/live.dart +++ b/lib/tcp/live.dart @@ -211,11 +211,12 @@ class LiveMessageStream { } @pragma('vm:notify-debugger-on-exception') - void _processingData(List data) { + void _processingData(List value) { try { - final subHeader = PackageHeaderRes.fromBytesData( - Uint8List.fromList(data), - ); + final Uint8List data = value is Uint8List + ? value + : Uint8List.fromList(value); + final subHeader = PackageHeaderRes.fromBytesData(data); if (subHeader != null) { final msgBody = utf8.decode( data.sublist(subHeader.headerSize, subHeader.totalSize), @@ -270,7 +271,7 @@ class LiveMessageStream { void onData(dynamic data) { final header = PackageHeaderRes.fromBytesData(data as Uint8List); if (header != null) { - List decompressedData = []; + List decompressedData = const []; //心跳包回复不用处理 if (header.operationCode == 3) return; if (header.operationCode == 8) { @@ -283,11 +284,13 @@ class LiveMessageStream { _processingData(data); return; case 2: - decompressedData = ZLibDecoder().convert(data.sublist(0x10)); + decompressedData = ZLibDecoder().convert( + Uint8List.sublistView(data, 0x10), + ); break; case 3: decompressedData = const BrotliDecoder().convert( - data.sublist(0x10), + Uint8List.sublistView(data, 0x10), ); //debugPrint('Body: ${utf8.decode()}'); } diff --git a/lib/utils/accounts/account.dart b/lib/utils/accounts/account.dart index 336a8954a..b2922c30f 100644 --- a/lib/utils/accounts/account.dart +++ b/lib/utils/accounts/account.dart @@ -8,7 +8,7 @@ import 'package:hive/hive.dart'; sealed class Account { Map? toJson() => null; - Future onChange() => Future.value(); + Future onChange() => Future.syncValue(null); Set get type => const {}; @@ -74,7 +74,7 @@ class LoginAccount extends Account { @override Future delete() { assert(_hasDelete = true); - return _box.delete(_midStr); + return Future.wait([cookieJar.deleteAll(), _box.delete(_midStr)]); } @override @@ -144,10 +144,8 @@ class AnonymousAccount extends Account { bool activated = false; @override - Future delete() async { - await cookieJar.deleteAll(); - cookieJar.setBuvid3(); - } + Future delete() => + cookieJar.deleteAll().whenComplete(cookieJar.setBuvid3); static final _instance = AnonymousAccount._(); @@ -166,16 +164,15 @@ class AnonymousAccount extends Account { extension BiliCookie on Cookie { void setBiliDomain([String domain = '.bilibili.com']) { - this - ..domain = domain - ..httpOnly = false - ..path = '/'; + this.domain = domain; + httpOnly = false; + path = '/'; } } extension BiliCookieJar on DefaultCookieJar { Map toJson() { - final cookies = domainCookies['bilibili.com']?['/'] ?? {}; + final cookies = domainCookies['bilibili.com']?['/'] ?? const {}; return {for (var i in cookies.values) i.cookie.name: i.cookie.value}; } diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index 39a16af40..251cb00e2 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -87,8 +87,7 @@ abstract final class PiliScheme { case 'bilibili': switch (host) { case 'root': - Navigator.popUntil( - Get.context!, + Get.key.currentState!.popUntil( (Route route) => route.isFirst, ); return true; diff --git a/lib/utils/extension/file_ext.dart b/lib/utils/extension/file_ext.dart index fdae77b8c..f74fbad48 100644 --- a/lib/utils/extension/file_ext.dart +++ b/lib/utils/extension/file_ext.dart @@ -1,6 +1,6 @@ import 'dart:io'; -extension FileExt on File { +extension FileSystemEntityExt on FileSystemEntity { Future tryDel({bool recursive = false}) async { try { await delete(recursive: recursive); @@ -9,12 +9,6 @@ extension FileExt on File { } extension DirectoryExt on Directory { - Future tryDel({bool recursive = false}) async { - try { - await delete(recursive: recursive); - } catch (_) {} - } - Future lengthGte(int length) async { int count = 0; await for (var _ in list()) { diff --git a/lib/utils/json_file_handler.dart b/lib/utils/json_file_handler.dart index 5330eeb90..d6853fa35 100644 --- a/lib/utils/json_file_handler.dart +++ b/lib/utils/json_file_handler.dart @@ -15,7 +15,10 @@ class JsonFileHandler extends ReportHandler { final bool printLogs; final bool handleWhenRejected; - static late Future _future; + static Future _future = LoggerUtils.getLogsPath() + .then((file) => file.open(mode: FileMode.writeOnlyAppend)) + .then((raf) => raf.writeFrom(const [])) + .then(_flush); JsonFileHandler._({ this.enableDeviceParameters = true, @@ -35,12 +38,7 @@ class JsonFileHandler extends ReportHandler { bool handleWhenRejected = false, }) async { try { - final raf = await (await LoggerUtils.getLogsPath()).open( - mode: FileMode.writeOnlyAppend, - ); - await raf.writeFrom(const []); - await raf.flush(); - _future = Future.syncValue(raf); + await _future; return JsonFileHandler._( enableDeviceParameters: enableDeviceParameters, enableApplicationParameters: enableApplicationParameters, @@ -55,10 +53,12 @@ class JsonFileHandler extends ReportHandler { } } + static Future _flush(RandomAccessFile raf) => raf.flush(); + static Future add( Future Function(RandomAccessFile) onValue, ) { - return _future = _future.then(onValue); + return _future = _future.then(onValue).then(_flush); } @override diff --git a/lib/utils/login_utils.dart b/lib/utils/login_utils.dart index f4104bebe..34f6c9b71 100644 --- a/lib/utils/login_utils.dart +++ b/lib/utils/login_utils.dart @@ -47,11 +47,11 @@ abstract final class LoginUtils { static Future onLoginMain() async { final account = Accounts.main; - GrpcReq.updateHeaders(account.accessKey); - setWebCookie(account); - RequestUtils.syncHistoryStatus(); final result = await UserHttp.userInfo(); if (result.isSuccess) { + GrpcReq.updateHeaders(account.accessKey); + setWebCookie(account); + RequestUtils.syncHistoryStatus(); final UserInfoData data = result.data; if (data.isLogin == true) { final accountService = Get.find() diff --git a/lib/utils/page_utils.dart b/lib/utils/page_utils.dart index f98ce5de3..20cd4ec0a 100644 --- a/lib/utils/page_utils.dart +++ b/lib/utils/page_utils.dart @@ -653,8 +653,8 @@ abstract class PageUtils { Get.generalDialog( barrierLabel: '', barrierDismissible: true, - pageBuilder: (buildContext, animation, secondaryAnimation) { - if (Get.context!.isPortrait) { + pageBuilder: (context, animation, secondaryAnimation) { + if (context.isPortrait) { return SafeArea( child: FractionallySizedBox( heightFactor: 0.7, @@ -680,7 +680,7 @@ abstract class PageUtils { }, transitionDuration: const Duration(milliseconds: 350), transitionBuilder: (context, animation, secondaryAnimation, child) { - Offset begin = Get.context!.isPortrait + Offset begin = context.isPortrait ? const Offset(0.0, 1.0) : const Offset(1.0, 0.0); var tween = Tween( diff --git a/lib/utils/storage_pref.dart b/lib/utils/storage_pref.dart index 771bc03b0..56a4a0bc9 100644 --- a/lib/utils/storage_pref.dart +++ b/lib/utils/storage_pref.dart @@ -656,6 +656,7 @@ abstract class Pref { _setting.get(SettingBoxKey.customColor, defaultValue: 0); static bool get dynamicColor => + !Platform.isIOS && _setting.get(SettingBoxKey.dynamicColor, defaultValue: !Platform.isIOS); static bool get autoClearCache => diff --git a/lib/utils/update.dart b/lib/utils/update.dart index 257e8ef51..308b9e96a 100644 --- a/lib/utils/update.dart +++ b/lib/utils/update.dart @@ -6,7 +6,6 @@ import 'package:PiliPlus/http/api.dart'; import 'package:PiliPlus/http/init.dart'; import 'package:PiliPlus/http/ua_type.dart'; import 'package:PiliPlus/utils/accounts/account.dart'; -import 'package:PiliPlus/utils/extension/string_ext.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/storage_key.dart'; @@ -131,7 +130,7 @@ abstract class Update { for (Map i in data['assets']) { final String name = i['name']; if (name.contains(plat) && - (ext.isNullOrEmpty ? true : name.endsWith(ext!))) { + (ext == null || ext.isEmpty ? true : name.endsWith(ext))) { PageUtils.launchURL(i['browser_download_url']); return; }